diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 14bf9456..fe8caaf1 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -158,4 +158,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v1 + uses: actions/deploy-pages@v2 diff --git a/_metadata.py b/_metadata.py index e8b82cd4..adeb78a8 100644 --- a/_metadata.py +++ b/_metadata.py @@ -1,2 +1,2 @@ -__extension_version__ = "0.15.0" +__extension_version__ = "0.16.0" __extension_name__ = "pytket-quantinuum" diff --git a/docs/changelog.rst b/docs/changelog.rst index c0814954..d7221df1 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,13 @@ Changelog ~~~~~~~~~ +0.16.0 (April 2023) +------------------- + +* cost function now takes the same kwargs as process_circuits +* add check for the number of classical registers to the backend +* Updated pytket version requirement to 1.14. + 0.15.0 (April 2023) ------------------- diff --git a/examples/python/wasm_in_pytket.py b/examples/python/wasm_in_pytket.py new file mode 100644 index 00000000..1c11b70b --- /dev/null +++ b/examples/python/wasm_in_pytket.py @@ -0,0 +1,136 @@ +# pip install pytket~=1.13 + +# # WASM function calls with pytket +# The WASM module in python allows you to add external classical functions from a compiled web assembly (WASM) to the circuit. + +# In the first step you need to read in the wasm file. You can do this when creating the wasm file handler by giving the path to the file. The wasmfilehandler now knows all available functions and the corresponding signatures. If you are not sure about the signatures of the functions of your file you can get a list of them from the wasmfilehandler like shown below. The parameters and result types of the supported functions must be i32. All functions that contain other types will be listed when printing the wasmfilehandler as well, but you can't add them to a circuit. + +from pytket import wasm, Circuit, Bit + +wfh = wasm.WasmFileHandler("testfile.wasm") +print("wasm file uid:") +print(wfh) + +print("\n\nwasm file repr:") +print(repr(wfh)) + +# In the next step we want to add some of the classical function calls to a circuit. We will start with adding the function "add_one" to read in for the first parameter from Bit(0) and write the result to Bit(1). The length of the two list giving the number of bits needs to be the number of parameters and the number of results. + +c = Circuit(0, 8) + +c.add_wasm( + "add_one", # name of the function + wfh, # wasm file handler + [1], # number of bits in each of the parameter i32 + [1], # number of bits in each of the result i32 + [Bit(0), Bit(1)], +) # list of bits where the wasm op will be added to + +# If you want to have more than one bit per parameter, you can add that in the following way. This will add the function "add_one" to read in for the first parameter from Bit(0) and Bit(1) and write the result to Bit(2), Bit(3) and Bit(4). + +c.add_wasm( + "add_one", # name of the function + wfh, # wasm file handler + [2], # number of bits in each of the parameter i32 + [3], # number of bits in each of the result i32 + [Bit(0), Bit(1), Bit(2), Bit(3), Bit(4)], +) # list of bits where the wasm op will be added to + +# When adding functions with multiple parameters this can be done in the same way: + +c.add_wasm( + "multi", # name of the function + wfh, # wasm file handler + [2, 1], # number of bits in each of the parameter i32 + [3], # number of bits in each of the result i32 + [Bit(0), Bit(1), Bit(5), Bit(2), Bit(3), Bit(4)], +) # list of bits where the wasm op will be added to + +# If you want to add two parameters with the same bits, that is fine, too. + +c.add_wasm( + "multi", # name of the function + wfh, # wasm file handler + [2, 2], # number of bits in each of the parameter i32 + [3], # number of bits in each of the result i32 + [Bit(0), Bit(1), Bit(0), Bit(1), Bit(2), Bit(3), Bit(4)], +) # list of bits where the wasm op will be added to + +# If you are working with registers in your circuit to organise the classical bits you can add wasm to your circuit using given registers for each parameter and the return value. + +# add registers to circuit + +c0 = c.add_c_register("c0", 3) +c1 = c.add_c_register("c1", 4) +c2 = c.add_c_register("c2", 5) + +c.add_wasm_to_reg( + "multi", # function name + wfh, # wasm file handler + [c0, c1], # register for each input parameter + [c2], +) # register for the result +c.add_wasm_to_reg( + "add_one", # function name + wfh, # wasm file handler + [c2], # register for each input parameter + [c2], +) # register for the result + +# The WASM might have some global data stored. To make sure this data is not messed up by function calls in the wrong order pytket will make sure that the order of the wasm calls within a circuit is not restructured. For this purpose pytket will add all wasm operation to a wasm_wire by default. If you are not worried about a possible restructure of the wasm calls in your circuit you have the option to not add the wasm_wire to your wasm operations. If you only want to stop some special reordering for some of your wasm operations you can add some the wasm operations to multiple wasm_wire to allow the restructuring in the intended way. Even if there are not wasm_wire given, pytket will only restructure the the wasm operations if there are no dependencies to in parameters or the results. + +# Here you can see that all operations we have created above are conected to the default wasm_wire: + +for g in c: + print(g) + +# We will now create a new circuit and add four operations. The two add_one operations should be allowed to commute, but we want to make sure that "multi" is executed after the two other functions. The last add_two operation can commute with all others. + +c = Circuit(0, 5) + +c.add_wasm("add_one", wfh, [1], [1], [Bit(0), Bit(0)], [0]) +c.add_wasm("add_one", wfh, [1], [1], [Bit(1), Bit(1)], [1]) +c.add_wasm("multi", wfh, [1, 1], [1], [Bit(2), Bit(3), Bit(2)], [0, 1]) +c.add_wasm("add_two", wfh, [1], [1], [Bit(4), Bit(4)], []) + +for g in c: + print(g) + +# One helpful feature might be to plot the DAG of the circuit to get an overview of the different components of the circuit + +from pytket.utils import Graph + +g = Graph(c) +g.view_DAG() + +# ## Send WASM to the Backend +# In the last step we want to send the circuit with the wasm to a backend. First we create the backend. For this step you will need Quantinuum credentials. + +from pytket.extensions.quantinuum import QuantinuumBackend + +machine = "H1-1E" +b = QuantinuumBackend(device_name=machine) +b.login() + +# When processing the circuit you need to add the wasmfilehandler you created as parameter to the `process_circuits` in the shown way + +from pytket.extensions.quantinuum import QuantinuumBackend + +c = Circuit(1) +c.name = "test_wasm" +a = c.add_c_register("a", 8) +c.add_wasm_to_reg("add_one", wfh, [a], [a]) +c = b.get_compiled_circuit(c) +h = b.process_circuits([c], n_shots=10, wasm_file_handler=wfh)[0] + +status = b.circuit_status(h) +print(status) + +status = b.circuit_status(h) +print(status) + +result = b.get_result(h) +print(result) + +for shot in result.get_shots(): + print(shot) diff --git a/examples/wasm_in_pytket.ipynb b/examples/wasm_in_pytket.ipynb new file mode 100644 index 00000000..7cf5b0b0 --- /dev/null +++ b/examples/wasm_in_pytket.ipynb @@ -0,0 +1 @@ +{"cells": [{"cell_type": "markdown", "metadata": {}, "source": ["![Quantinuum_logo](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIIAAABLCAMAAACLOq%2BzAAACx1BMVEUAAAAAAP%2BAgIBVqqqAgIBmmZmAgKptkpKAgJ9xjo6AgJl0i6JqgJV2iZ1tgJJ3iJlwgJ94h5ZxgJx5hpRzgJl5hp50gJdvhZt1gJVwhZl2gJ12gJtyhJV3gJlzhJx4gJd0g5txgJZ1g5lxgJx1g5hygJp2g5ZzgJl2g5tzgJh3gpp0gJdxgpl0gJtygph1gJpygpd1gJlzgpt2gJhzgpp2gJd0gplygJt0gphygJp1gpdzgJl1gptzgJh1gpp2gZl0gJtygZh0gJpzgZh1gJlzgZp1gJhzgZp1gJh0gZl0gZhygJp0gZhzgJl0gZpzgJh1gZpzgJh1gZl0gJp1gZh0gJp0gJlzgZp0gJhzgZp1gJhzgZl1gJp0gZh1gJp0gZhzgJl0gZpzgJlzgJh1gZlzgJp1gZl1gZh0gJl0gJlzgZl0gJhzgZl1gJp0gZl1gJl0gZh1gJl0gZpzgJl0gZlzgJh0gZlzgJp0gZl0gJl1gJh1gJpzgJl0gJhzgJl0gJpzgJl0gJl1gJl0gJp1gJl0gJl1gJhzgJp0gJlzgJl0gJh0gJl1gJp0gJl1gJl0gJh1gJl0gJpzgJl0gJlzgJh0gJl0gJp0gJl0gJl1gJh0gJl1gJp0gJlzgJl0gJh0gJl0gJp0gJl0gJl0gJh1gJl0gJpzgJh0gJl0gJl0gJl1gJp0gJl1gJl0gJhzgJl0gJl0gJl0gJh0gJl0gJp0gJl0gJl1gJh0gJlzgJp0gJl0gJl0gJl0gJl0gJl0gJl0gJl0gJl1gJl0gJlzgJl0gJl0gJl0gJl0gJl0gJl0gJl0gJl1gJl0gJlzgJl0gJl0gJl0gJl0gJl0gJl0gJl0gJl0gJl1gJl0gJl0gJl0gJl0gJl0gJl0gJl0gJl0gJl0gJl0gJl0gJl0gJl0gJl0gJl0gJl0gJl0gJl0gJl0gJl0gJl0gJn%2F%2F%2F%2Bc0JCdAAAA63RSTlMAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRocHR4fICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj9BQkNERUZHSElKS01OT1BRUlNUVVZXWFpbXF1eX2BhYmNkZWZoaWprbW5wcXJzdHV2d3h5ent8fX5%2FgIGDhYaHiImKjI2Oj5CSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq%2Bws7a3ubq7vL2%2BwMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbY2drb3N3e3%2BDh4uPk5ebn6Orr7O3u7%2FHy8%2FT19vf4%2Bfr7%2FP3%2B7Jw5zgAAAAFiS0dE7CG5sxsAAAVfSURBVBgZ7cGNV9X1AcfxNxcwnhRRaVCsKGszI%2BbDcs3KlDKldAmhGywz0ZyJOqLUHrBraVoKJD5E%2BBDaarXS2tZ0jmkqKjlLKR9SA6EFCNzPP7Hv7%2F7QmMm9nM754jkdXi9%2BsNCxL7x9uLb266qKZx%2BI5Ar4yYsn9J2GNyd46F6e%2BfW6RFWmh2404ANdxq6f022uOajL%2BvYPIXSPyN3qTFkY1vUBytW5tyOwLHE%2BjFMg73iwKqpyCCEHFFAhVpXsgfEKzJeBRWO1CDYpiK%2FisCbsU91N%2BDkFU4Q1WVIkqWrX%2BvGTv04Ih96Dp246o47aUrBlpxogR351z8XzHc%2F4j9TBGiz5lVQD82W0Lo7lErfv0EWN8dixTDoICyTV3Mn3heY36YK52LFf%2BgLypH%2FHcVlDT6vddqxI8EnfQLb29qMTg0%2FJ1RSNDQ%2FJiGJEVTydGlQv1%2F3YMEdGGp4BBJAt1xPYsFTGYoLYIL8l2LBZRhVBJDXJ8TI2%2FEmOewhilRwrsGGDHO8QxI1yvIgNa%2BQ3hiCqZCzChmXyq76KwBbLmIsN0%2BQqIbAHZEzGhmFql0dAKTKSsCGiRa622QQSI6kGO7bpgqWhBNAgrcSOabroX7fRuQ8%2Fqx6JHX2bdNH55ddxJZSrg5aNE6Lpdre06v80bnvVW5DfbnZuR6OxZKW6qhxL%2Btaoi97Altub1TWvY81jPnXJeuzJV5esw6LHW9UFa7Ep%2FayCK8Wqaz9QUKux7OFjCqIE28If2aeAiukGtxX%2B47w6VUT3iBg8Mdc1K%2F8S99GjR48ePXr06PHjkzRjycIxHhyR0%2BNwxOT0wxH64G%2F74xrzaDR%2B9%2F4SeDDzghDwZCZh3PdoJH7jhgLXZobhiH84Aph4E36TBgGpmem0S52eDuFLz8vYPwRjiMbj%2BJlycGRLr%2BKq1N964zi0HqjXBTEQockYB7UtGsexImCSYnHMVCJQl4cjwrcAWCaNxy%2F2tE4SskXb74i%2B5omGhhRgpDJwpGgGRtjhszuarsNvd5t2xmJ8XgYMHZ2WdqQuLS1tWBhEaQrGoTb9tTfGVyXAJF2NI0%2BJQF0Bjr5aACz31X%2FaC8cSfXOWDK0LwfjFt7tC4E49gmOEpmFMVW5KSyl%2Be%2F68QJX9gFPluHafwBWj32FUbynUjljg3GrgIf0UR74SgbrncAzQ08CKs7M0DyO56d2t9Ww%2FFYPfUxoOqZqN435lAeGf7fVQ0jIQx97NzNG%2BBKjbiKvyGK4%2BysE4vJYC7Y6H5lJgggbheF6JwJmXcFyvfOCVk6H7ziUAFa23ljdzeg2uFM2CG7QQxxSNA6ZrEiS3lOPYvwHm6EgyjZtx7TqCq69%2Bj3FkNTyp6iSP1gLpugvHMiUCp9bjSFEesPILRqkM7tFrlLbS8Bqu6%2FVHCP36n6EYmxv7QK9jenxKVu6J1sEYB8qAuTpxq68C18fVuOI0FePzYqBANalaDyRrHUbvmuO9gA%2FPxGE8rVFA0VHY4rsr7EBDAkUtHNiJK10ZwHy9lUD0cnmBmWq3FeNQGUaBarUV1%2FYqXP01DeNoMcYi1aoMY5MKI0neoRyMe31%2Fv5FeeS0fhQAlR2Fg8755WgirmnlWo%2FCLHRgKeFZIdY16Ixyu%2BrJy%2BDBHqW8IUF2G4xnpLVzvfYIrXtMxaopxPC%2BVY8T8ReePy1eI38xmX81%2FtedqjNVHgZfV9mUUrGok%2FvjpsXQ0svj90jSMG7zD8Yt%2BZiwwLwu%2FXG82rqzHcMV4R2AU%2FAa%2FGd7JOEImvv7e8mG0u3nJuxuzw3Fk5AP9vd7RwMQCGPQfHayoKClZ6R3FlRJV8MnJ2trG2trZXBH%2FA2RBOeTINIkQAAAAAElFTkSuQmCC)
\n", "pip install pytket~=1.13"]}, {"cell_type": "markdown", "metadata": {}, "source": ["# WASM function calls with pytket
\n", "The WASM module in python allows you to add external classical functions from a compiled web assembly (WASM) to the circuit."]}, {"cell_type": "markdown", "metadata": {}, "source": ["In the first step you need to read in the wasm file. You can do this when creating the wasm file handler by giving the path to the file. The wasmfilehandler now knows all available functions and the corresponding signatures. If you are not sure about the signatures of the functions of your file you can get a list of them from the wasmfilehandler like shown below. The parameters and result types of the supported functions must be i32. All functions that contain other types will be listed when printing the wasmfilehandler as well, but you can't add them to a circuit."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket import wasm, Circuit, Bit"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["wfh = wasm.WasmFileHandler(\"testfile.wasm\")\n", "print(\"wasm file uid:\")\n", "print(wfh)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["print(\"\\n\\nwasm file repr:\")\n", "print(repr(wfh))"]}, {"cell_type": "markdown", "metadata": {}, "source": ["In the next step we want to add some of the classical function calls to a circuit. We will start with adding the function \"add_one\" to read in for the first parameter from Bit(0) and write the result to Bit(1). The length of the two list giving the number of bits needs to be the number of parameters and the number of results."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c = Circuit(0, 8)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c.add_wasm(\n", " \"add_one\", # name of the function\n", " wfh, # wasm file handler\n", " [1], # number of bits in each of the parameter i32\n", " [1], # number of bits in each of the result i32\n", " [Bit(0), Bit(1)],\n", ") # list of bits where the wasm op will be added to"]}, {"cell_type": "markdown", "metadata": {}, "source": ["If you want to have more than one bit per parameter, you can add that in the following way. This will add the function \"add_one\" to read in for the first parameter from Bit(0) and Bit(1) and write the result to Bit(2), Bit(3) and Bit(4)."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c.add_wasm(\n", " \"add_one\", # name of the function\n", " wfh, # wasm file handler\n", " [2], # number of bits in each of the parameter i32\n", " [3], # number of bits in each of the result i32\n", " [Bit(0), Bit(1), Bit(2), Bit(3), Bit(4)],\n", ") # list of bits where the wasm op will be added to"]}, {"cell_type": "markdown", "metadata": {}, "source": ["When adding functions with multiple parameters this can be done in the same way:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c.add_wasm(\n", " \"multi\", # name of the function\n", " wfh, # wasm file handler\n", " [2, 1], # number of bits in each of the parameter i32\n", " [3], # number of bits in each of the result i32\n", " [Bit(0), Bit(1), Bit(5), Bit(2), Bit(3), Bit(4)],\n", ") # list of bits where the wasm op will be added to"]}, {"cell_type": "markdown", "metadata": {}, "source": ["If you want to add two parameters with the same bits, that is fine, too."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c.add_wasm(\n", " \"multi\", # name of the function\n", " wfh, # wasm file handler\n", " [2, 2], # number of bits in each of the parameter i32\n", " [3], # number of bits in each of the result i32\n", " [Bit(0), Bit(1), Bit(0), Bit(1), Bit(2), Bit(3), Bit(4)],\n", ") # list of bits where the wasm op will be added to"]}, {"cell_type": "markdown", "metadata": {}, "source": ["If you are working with registers in your circuit to organise the classical bits you can add wasm to your circuit using given registers for each parameter and the return value."]}, {"cell_type": "markdown", "metadata": {}, "source": ["add registers to circuit"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c0 = c.add_c_register(\"c0\", 3)\n", "c1 = c.add_c_register(\"c1\", 4)\n", "c2 = c.add_c_register(\"c2\", 5)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c.add_wasm_to_reg(\n", " \"multi\", # function name\n", " wfh, # wasm file handler\n", " [c0, c1], # register for each input parameter\n", " [c2],\n", ") # register for the result\n", "c.add_wasm_to_reg(\n", " \"add_one\", # function name\n", " wfh, # wasm file handler\n", " [c2], # register for each input parameter\n", " [c2],\n", ") # register for the result"]}, {"cell_type": "markdown", "metadata": {}, "source": ["The WASM might have some global data stored. To make sure this data is not messed up by function calls in the wrong order pytket will make sure that the order of the wasm calls within a circuit is not restructured. For this purpose pytket will add all wasm operation to a wasm_wire by default. If you are not worried about a possible restructure of the wasm calls in your circuit you have the option to not add the wasm_wire to your wasm operations. If you only want to stop some special reordering for some of your wasm operations you can add some the wasm operations to multiple wasm_wire to allow the restructuring in the intended way. Even if there are not wasm_wire given, pytket will only restructure the the wasm operations if there are no dependencies to in parameters or the results."]}, {"cell_type": "markdown", "metadata": {}, "source": ["Here you can see that all operations we have created above are conected to the default wasm_wire:"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["for g in c:\n", " print(g)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["We will now create a new circuit and add four operations. The two add_one operations should be allowed to commute, but we want to make sure that \"multi\" is executed after the two other functions. The last add_two operation can commute with all others."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c = Circuit(0, 5)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c.add_wasm(\"add_one\", wfh, [1], [1], [Bit(0), Bit(0)], [0])\n", "c.add_wasm(\"add_one\", wfh, [1], [1], [Bit(1), Bit(1)], [1])\n", "c.add_wasm(\"multi\", wfh, [1, 1], [1], [Bit(2), Bit(3), Bit(2)], [0, 1])\n", "c.add_wasm(\"add_two\", wfh, [1], [1], [Bit(4), Bit(4)], [])"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["for g in c:\n", " print(g)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["One helpful feature might be to plot the DAG of the circuit to get an overview of the different components of the circuit"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.utils import Graph"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["g = Graph(c)\n", "g.view_DAG()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Send WASM to the Backend
\n", "In the last step we want to send the circuit with the wasm to a backend. First we create the backend. For this step you will need Quantinuum credentials."]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.extensions.quantinuum import QuantinuumBackend"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["machine = \"H1-1E\"\n", "b = QuantinuumBackend(device_name=machine)\n", "b.login()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["When processing the circuit you need to add the wasmfilehandler you created as parameter to the `process_circuits` in the shown way"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["from pytket.extensions.quantinuum import QuantinuumBackend"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["c = Circuit(1)\n", "c.name = \"test_wasm\"\n", "a = c.add_c_register(\"a\", 8)\n", "c.add_wasm_to_reg(\"add_one\", wfh, [a], [a])\n", "c = b.get_compiled_circuit(c)\n", "h = b.process_circuits([c], n_shots=10, wasm_file_handler=wfh)[0]"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["status = b.circuit_status(h)\n", "print(status)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["status = b.circuit_status(h)\n", "print(status)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["result = b.get_result(h)\n", "print(result)"]}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": ["for shot in result.get_shots():\n", " print(shot)"]}], "metadata": {"kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.4"}}, "nbformat": 4, "nbformat_minor": 2} \ No newline at end of file diff --git a/pytket/extensions/quantinuum/backends/api_wrappers.py b/pytket/extensions/quantinuum/backends/api_wrappers.py index 2deac6a6..de0c1e93 100644 --- a/pytket/extensions/quantinuum/backends/api_wrappers.py +++ b/pytket/extensions/quantinuum/backends/api_wrappers.py @@ -526,7 +526,7 @@ def __init__(self, machine_list: Optional[list] = None): "name": "H1-1", "n_qubits": 20, "gateset": ["RZZ", "Riswap", "Rxxyyzz"], - "n_classical_registers": 50, + "n_classical_registers": 120, "n_shots": 10000, "system_family": "H1", "system_type": "hardware", @@ -539,7 +539,7 @@ def __init__(self, machine_list: Optional[list] = None): "name": "H1-2", "n_qubits": 12, "gateset": ["RZZ", "Riswap", "Rxxyyzz"], - "n_classical_registers": 50, + "n_classical_registers": 120, "n_shots": 10000, "system_family": "H1", "system_type": "hardware", @@ -552,7 +552,7 @@ def __init__(self, machine_list: Optional[list] = None): "name": "H2-1", "n_qubits": 32, "gateset": ["RZZ", "Riswap", "Rxxyyzz"], - "n_classical_registers": 50, + "n_classical_registers": 120, "n_shots": 10000, "system_family": "H2", "system_type": "hardware", diff --git a/pytket/extensions/quantinuum/backends/quantinuum.py b/pytket/extensions/quantinuum/backends/quantinuum.py index 409fb7f3..feed566b 100644 --- a/pytket/extensions/quantinuum/backends/quantinuum.py +++ b/pytket/extensions/quantinuum/backends/quantinuum.py @@ -53,6 +53,7 @@ from pytket.predicates import ( # type: ignore GateSetPredicate, MaxNQubitsPredicate, + MaxNClRegPredicate, Predicate, NoSymbolsPredicate, ) @@ -261,6 +262,9 @@ def _available_devices( def _dict_to_backendinfo(cls, dct: Dict[str, Any]) -> BackendInfo: name: str = dct.pop("name") n_qubits: int = dct.pop("n_qubits") + n_cl_reg: Optional[int] = None + if "n_classical_registers" in dct: + n_cl_reg = dct.pop("n_classical_registers") gate_set: List[str] = dct.pop("gateset", []) return BackendInfo( name=cls.__name__, @@ -268,6 +272,7 @@ def _dict_to_backendinfo(cls, dct: Dict[str, Any]) -> BackendInfo: version=__extension_version__, architecture=FullyConnected(n_qubits, "node"), gate_set=_get_gateset(gate_set), + n_cl_reg=n_cl_reg, supports_fast_feedforward=True, supports_midcircuit_measurement=True, supports_reset=True, @@ -351,6 +356,7 @@ def required_predicates(self) -> List[Predicate]: if not self._MACHINE_DEBUG: assert self.backend_info is not None preds.append(MaxNQubitsPredicate(self.backend_info.n_nodes)) + preds.append(MaxNClRegPredicate(self.backend_info.n_cl_reg)) return preds @@ -828,6 +834,7 @@ def cost( n_shots: int, syntax_checker: Optional[str] = None, use_websocket: Optional[bool] = None, + **kwargs: QuumKwargTypes, ) -> Optional[float]: """ Return the cost in HQC to complete this `circuit` with `n_shots` @@ -839,6 +846,9 @@ def cost( for example for device families. In which case you may need to set the ``syntax_checker`` kwarg to the appropriate syntax checker name. + See :py:meth:`QuantinuumBackend.process_circuits` for the + supported kwargs. + :param circuit: Circuit to calculate runtime estimate for. Must be valid for backend. :type circuit: Circuit @@ -875,7 +885,7 @@ def cost( cast(str, syntax_checker), api_handler=self.api_handler ) try: - handle = backend.process_circuit(circuit, n_shots) + handle = backend.process_circuit(circuit, n_shots, kwargs=kwargs) # type: ignore except DeviceNotAvailable as e: raise ValueError( f"Cannot find syntax checker for device {self._device_name}. " diff --git a/setup.py b/setup.py index 04add954..f48fb216 100644 --- a/setup.py +++ b/setup.py @@ -43,7 +43,7 @@ packages=find_namespace_packages(include=["pytket.*"]), include_package_data=True, install_requires=[ - "pytket >= 1.13.2, <2.0", + "pytket ~= 1.14", "requests >= 2.2", "types-requests", "websockets >= 7.0", diff --git a/tests/api1_test.py b/tests/api1_test.py index df460264..df9f442a 100644 --- a/tests/api1_test.py +++ b/tests/api1_test.py @@ -453,8 +453,8 @@ def test_available_devices( assert backinfo.supports_fast_feedforward == True assert backinfo.supports_midcircuit_measurement == True assert backinfo.supports_reset == True + assert backinfo.n_cl_reg == 120 assert backinfo.misc == { - "n_classical_registers": 50, "n_shots": 10000, "system_family": "mock_family", "system_type": "hardware", diff --git a/tests/backend_test.py b/tests/backend_test.py index 0bb3efcf..fae6cfc4 100644 --- a/tests/backend_test.py +++ b/tests/backend_test.py @@ -23,6 +23,7 @@ import hypothesis.strategies as st from hypothesis.strategies._internal import SearchStrategy from hypothesis import HealthCheck +from pytket.backends import CircuitNotValidError from pytket.passes import ( # type: ignore SequencePass, RemoveRedundancies, @@ -140,6 +141,57 @@ def test_quantinuum_offline() -> None: # assert result[0]["options"] == expected_result["options"] +@pytest.mark.skipif(skip_remote_tests, reason=REASON) +@pytest.mark.parametrize( + "authenticated_quum_backend", [{"device_name": "H1-1SC"}], indirect=True +) +def test_max_classical_register( + authenticated_quum_backend: QuantinuumBackend, +) -> None: + backend = authenticated_quum_backend + + c = Circuit(4, 4, "test 1") + c.H(0) + c.CX(0, 1) + c.measure_all() + c = backend.get_compiled_circuit(c) + assert backend._check_all_circuits([c]) + for i in range(0, 20): + c.add_c_register(f"creg-{i}", 32) + + assert backend._check_all_circuits([c]) + + for i in range(20, 200): + c.add_c_register(f"creg-{i}", 32) + + with pytest.raises(CircuitNotValidError): + backend._check_all_circuits([c]) + + +def test_max_classical_register_ii() -> None: + qapioffline = QuantinuumAPIOffline() + backend = QuantinuumBackend( + device_name="H1-1", machine_debug=False, api_handler=qapioffline # type: ignore + ) + + c = Circuit(4, 4, "test 1") + c.H(0) + c.CX(0, 1) + c.measure_all() + c = backend.get_compiled_circuit(c) + assert backend._check_all_circuits([c]) + for i in range(0, 20): + c.add_c_register(f"creg-{i}", 32) + + assert backend._check_all_circuits([c]) + + for i in range(20, 200): + c.add_c_register(f"creg-{i}", 32) + + with pytest.raises(CircuitNotValidError): + backend._check_all_circuits([c]) + + def test_tket_pass_submission() -> None: backend = QuantinuumBackend(device_name="H1-1SC", machine_debug=True) @@ -327,9 +379,11 @@ def test_cost_estimate( with pytest.raises(ValueError) as e: _ = b.cost(c, n_shots) assert "Cannot find syntax checker" in str(e.value) - estimate = b.cost(c, n_shots, syntax_checker=f"{b._device_name}-1SC") + estimate = b.cost( + c, n_shots, syntax_checker=f"{b._device_name}-1SC", no_opt=False + ) else: - estimate = b.cost(c, n_shots) + estimate = b.cost(c, n_shots, no_opt=False) if estimate is None: pytest.skip("API is flaky, sometimes returns None unexpectedly.") assert isinstance(estimate, float) @@ -676,6 +730,29 @@ def test_wasm( assert b.get_result(h) +@pytest.mark.skipif(skip_remote_tests, reason=REASON) +@pytest.mark.parametrize( + "authenticated_quum_backend", [{"device_name": "H1-1SC"}], indirect=True +) +def test_wasm_costs( + authenticated_quum_backend: QuantinuumBackend, +) -> None: + wasfile = WasmFileHandler(str(Path(__file__).parent / "testfile.wasm")) + c = Circuit(1) + c.name = "test_wasm" + a = c.add_c_register("a", 8) + c.add_wasm_to_reg("add_one", wasfile, [a], [a]) + + b = authenticated_quum_backend + + c = b.get_compiled_circuit(c) + costs = b.cost(c, n_shots=10, syntax_checker="H1-1SC", wasm_file_handler=wasfile) + if costs is None: + pytest.skip("API is flaky, sometimes returns None unexpectedly.") + assert isinstance(costs, float) + assert costs > 0.0 + + @pytest.mark.skipif(skip_remote_tests, reason=REASON) @pytest.mark.parametrize( "authenticated_quum_backend", [{"device_name": "H1-1SC"}], indirect=True diff --git a/tests/conftest.py b/tests/conftest.py index 4303aca6..67d97a27 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -60,7 +60,7 @@ def mock_machine_info() -> Dict[str, Any]: "name": "H9-27", "n_qubits": 12, "gateset": [], - "n_classical_registers": 50, + "n_classical_registers": 120, "n_shots": 10000, "system_family": "mock_family", "system_type": "hardware", @@ -78,7 +78,7 @@ def sample_machine_infos() -> List[Dict[str, Any]]: "name": "H1-2SC", "n_qubits": 12, "gateset": ["RZZ", "Riswap", "Rxxyyzz"], - "n_classical_registers": 50, + "n_classical_registers": 120, "n_shots": 10000, "system_type": "syntax checker", "wasm": True, @@ -87,7 +87,7 @@ def sample_machine_infos() -> List[Dict[str, Any]]: "name": "H1-1SC", "n_qubits": 12, "gateset": ["RZZ", "Riswap", "Rxxyyzz"], - "n_classical_registers": 50, + "n_classical_registers": 120, "n_shots": 10000, "system_type": "syntax checker", "wasm": True, @@ -96,7 +96,7 @@ def sample_machine_infos() -> List[Dict[str, Any]]: "name": "H1-1E", "n_qubits": 20, "gateset": ["RZZ", "Riswap", "Rxxyyzz"], - "n_classical_registers": 50, + "n_classical_registers": 120, "n_shots": 10000, "system_type": "emulator", "batching": True, @@ -106,7 +106,7 @@ def sample_machine_infos() -> List[Dict[str, Any]]: "name": "H1-1", "n_qubits": 20, "gateset": ["RZZ", "Riswap", "Rxxyyzz"], - "n_classical_registers": 50, + "n_classical_registers": 120, "n_shots": 10000, "system_family": "H1", "system_type": "hardware", @@ -119,7 +119,7 @@ def sample_machine_infos() -> List[Dict[str, Any]]: "name": "H1-2", "n_qubits": 12, "gateset": ["RZZ", "Riswap", "Rxxyyzz"], - "n_classical_registers": 50, + "n_classical_registers": 120, "n_shots": 10000, "system_family": "H1", "system_type": "hardware", @@ -132,7 +132,7 @@ def sample_machine_infos() -> List[Dict[str, Any]]: "name": "H1-2E", "n_qubits": 12, "gateset": ["RZZ", "Riswap", "Rxxyyzz"], - "n_classical_registers": 50, + "n_classical_registers": 120, "n_shots": 10000, "system_type": "emulator", "batching": True,