{ "cells": [ { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "DmNaCESptyWj" }, "source": [ "# Cirq Integration" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This notebook shows a simple example of how to use pyGSTi with Cirq. It has three sections:\n", "\n", "1. Sets up pyGSTi.\n", "2. Shows how pyGSTi circuits can be converted to Cirq circuits.\n", "3. Shows how Cirq circuits can be converted into pyGSTi circuits.\n", "4. Shows how the Cirq circuits can be run and the results loaded back into pyGSTi for analysis." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": {}, "colab_type": "code", "id": "gw9bJiKmkST9" }, "outputs": [], "source": [ "import cirq\n", "import pygsti\n", "from pygsti.modelpacks import smq1Q_XYI\n", "from pygsti.circuits import Circuit\n", "import numpy as np\n", "import tqdm" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "uugvjGQ3vR0z" }, "source": [ "## 1. Generate the GST circuits" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "cWpHwZVtvejH" }, "source": [ "### Make target gate set $\\{R_{X}(\\pi/2), R_{Y}(\\pi/2),I\\}$" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "target_model = smq1Q_XYI.target_model()" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "JVfiXBu4vqJV" }, "source": [ "### Preparation and measurement fiducials, germs" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": {}, "colab_type": "code", "id": "EPNxq24fvo6t" }, "outputs": [], "source": [ "preps = smq1Q_XYI.prep_fiducials()\n", "effects = smq1Q_XYI.meas_fiducials()\n", "germs = smq1Q_XYI.germs()" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "u9fHRr8Hv933" }, "source": [ "### Construct pyGSTi circuits" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": {}, "colab_type": "code", "id": "Jptyo9F0vx5N" }, "outputs": [], "source": [ "max_lengths = list(np.logspace(0, 10, 11, base=2, dtype=int))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 34 }, "colab_type": "code", "id": "SuvgxDpKwCul", "outputId": "6654eeeb-3870-4b61-af43-0c66cb09169e" }, "outputs": [], "source": [ "print(max_lengths)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": {}, "colab_type": "code", "id": "qk-yEEWTwFJM" }, "outputs": [], "source": [ "pygsti_circuits = pygsti.circuits.gstcircuits.create_lsgst_circuits(target_model, preps, effects, germs, max_lengths)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 34 }, "colab_type": "code", "id": "9vD8DXOPwHSV", "outputId": "06e10aec-f7ab-4b7b-d0c6-242ce225d5a2" }, "outputs": [], "source": [ "len(pygsti_circuits)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. Convert to runable `cirq.Circuit`'s" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Setup" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, we need to map the qubit names from pyGSTi (`0`, `1`, etc.) into cirq qubits. There's nothing special about `cirq.GridQubit(8, 3)`; it's just an example." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "q0 = cirq.GridQubit(8, 3)\n", "qubit_label_dict = {0: q0}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Testing examples" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Do an example conversion." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "pygsti_circuit = pygsti_circuits[111]\n", "print('pyGSTi:')\n", "print(pygsti_circuit)\n", "print('Cirq:')\n", "print(pygsti_circuit.convert_to_cirq(qubit_label_dict))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Do another example conversion." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pygsti_circuit = pygsti_circuits[90]\n", "print('pyGSTi:')\n", "print(pygsti_circuit)\n", "print('Cirq:')\n", "print(pygsti_circuit.convert_to_cirq(qubit_label_dict))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, lets try the same thing but specifing a wait duration for the idle operation." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "wait_duration = cirq.Duration(nanos=100)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pygsti_circuit = pygsti_circuits[111]\n", "print('pyGSTi:')\n", "print(pygsti_circuit)\n", "print('Cirq:')\n", "print(pygsti_circuit.convert_to_cirq(qubit_label_dict, wait_duration))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pygsti_circuit = pygsti_circuits[90]\n", "print('pyGSTi:')\n", "print(pygsti_circuit)\n", "print('Cirq:')\n", "print(pygsti_circuit.convert_to_cirq(qubit_label_dict, wait_duration))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### The real thing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, convert all the circuits." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "cirq_circuits = [c.convert_to_cirq(qubit_label_dict, wait_duration) for c in tqdm.tqdm(pygsti_circuits)]" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "cirq_circuits" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that we're missing the measurments and the first circuit is empty (it's should just be an idle). Otherwise, the results look good, and those things should be easy to fix." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3. Convert Cirq circuits to pyGSTi circuits\n", "We also have support for converting a cirq circuit to a pyGSTi circuit, which is demonstrated below.\n", "Begin by constructing a cirq circuit directly." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#create to cirq qubit objects\n", "qubit_00 = cirq.GridQubit(0,0)\n", "qubit_01 = cirq.GridQubit(0,1)\n", "#define a series of Moment objects, which fill the same role as circuit layers in pyGSTi.\n", "moment1 = cirq.Moment([cirq.XPowGate(exponent=.5).on(qubit_00), cirq.I(qubit_01)])\n", "moment2 = cirq.Moment([cirq.I(qubit_00), cirq.I(qubit_01)])\n", "#This weird looking gate is the so-called N gate.\n", "moment3 = cirq.Moment([cirq.PhasedXZGate(axis_phase_exponent=0.14758361765043326, \n", " x_exponent=0.4195693767448338, \n", " z_exponent=-0.2951672353008665).on(qubit_00),\n", " cirq.I(qubit_01)])\n", "moment4 = cirq.Moment([cirq.H(qubit_00), (cirq.T**-1).on(qubit_01)])\n", "moment5 = cirq.Moment([cirq.CNOT.on(qubit_00, qubit_01)])\n", "cirq_circuit_example = cirq.Circuit([moment1, moment2, moment3, moment4, moment5])\n", "print(cirq_circuit_example)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To convert this into a pyGSTi circuit we can use the `from_cirq` class method of the Circuit class." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "converted_cirq_circuit_default = Circuit.from_cirq(cirq_circuit_example)\n", "print(converted_cirq_circuit_default)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Above you can see the result of converting the circuit using the default conversion settings. The classmethod has multiple options for customizing the returned pyGSTi circuit.\n", "1. By default the method constructs a mapping between cirq qubit objects and pygsti qubit labels based on the type of cirq qubit provided. E.g. a GridQubit gets mapped to `Q{row}_{col}` where row and col are the corresponding attribute values for the GridQubit. Something similar is done for NamedQubit and LineQubit objects. This can be overridden by passing in a dictionary for the `qubit_conversion` kwarg." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "converted_cirq_circuit_custom_qubit_map = Circuit.from_cirq(cirq_circuit_example, qubit_conversion={qubit_00: 'Qalice', qubit_01: 'Qbob'})\n", "print(converted_cirq_circuit_custom_qubit_map)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "2. By default cirq included idle gates explicitly on all qubits in a layer without a specified operation applied. In pygsti we typically treat these as implied, and so the default behavior is to strip these extra idles. This can be turned off by setting `remove_implied_idles` to `False`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "converted_cirq_circuit_implied_idles = Circuit.from_cirq(cirq_circuit_example, remove_implied_idles=True)\n", "print(converted_cirq_circuit_implied_idles)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "3. Layers consisting entirely of idle gates are by default converted to the default pyGSTi global idle convention or Label(()), or to a user specified replacement. This is controlled by the `global_idle_replacement_label` kwarg. The default value is the string 'auto', which will utilize the aforementioned default convention. Users can instead pass in either a string, which is converted to a corresponding Label object, or a circuit Label object directly. Finally, by passing in `None` the global idle replacement is not performed, and the full verbatim translation of that cirq layer is produced." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#auto is the default value, explicitly including here for comparison to alternative options.\n", "converted_cirq_circuit_global_idle = Circuit.from_cirq(cirq_circuit_example, global_idle_replacement_label='auto')\n", "print(converted_cirq_circuit_global_idle)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "converted_cirq_circuit_global_idle_1 = Circuit.from_cirq(cirq_circuit_example, global_idle_replacement_label='Gbanana')\n", "print(converted_cirq_circuit_global_idle_1)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from pygsti.baseobjs import Label\n", "converted_cirq_circuit_global_idle_2 = Circuit.from_cirq(cirq_circuit_example, global_idle_replacement_label=Label('Gbanana', ('Q0_0','Q0_1')))\n", "print(converted_cirq_circuit_global_idle_2)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "converted_cirq_circuit_global_idle_3 = Circuit.from_cirq(cirq_circuit_example, global_idle_replacement_label= None)\n", "print(converted_cirq_circuit_global_idle_3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "4. There is built-in support for converting _most_ Cirq gates into their corresponding built-in pyGSTi gate names (see `cirq_gatenames_standard_conversions` in `pygsti.tools.internalgates` for more on this). There is also a fallback behavior where if not found in the default map, the converter will search among the built-in gate unitaries for one that matches (up to a global phase). If this doesn't work for a particular gate of user interest, of you simply want to override the default mapping as needed, this can be done by passing in a custom dictionary for the `cirq_gate_conversion` kwarg." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "custom_gate_map = pygsti.tools.internalgates.cirq_gatenames_standard_conversions()\n", "custom_gate_map[cirq.H] = 'Gdefinitelynoth'\n", "converted_cirq_circuit_custom_gate_map = Circuit.from_cirq(cirq_circuit_example, cirq_gate_conversion=custom_gate_map)\n", "print(converted_cirq_circuit_custom_gate_map)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 4. Run the circuits" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Add measurements to the circuits." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for circuit in cirq_circuits:\n", " circuit.append(cirq.measure(q0, key='result'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Simulate the circuits (or run them on a real quantum computer!)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "simulator = cirq.Simulator()\n", "results = [simulator.run(circuit, repetitions=1000) for circuit in tqdm.tqdm(cirq_circuits)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Load everything the results into a pyGSTi dataset." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "dataset = pygsti.data.dataset.DataSet()\n", "for pygsti_circuit, trial_result in zip(pygsti_circuits, results):\n", " dataset.add_cirq_trial_result(pygsti_circuit, trial_result, key='result')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Perform GST." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "gst_results = pygsti.run_stdpractice_gst(dataset, target_model, preps, effects, germs, max_lengths, modes=[\"full TP\",\"Target\"], verbosity=1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "See what if finds." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "mdl_estimate = gst_results.estimates['full TP'].models['stdgaugeopt']\n", "print(\"2DeltaLogL(estimate, data): \", pygsti.tools.two_delta_logl(mdl_estimate, dataset))\n", "print(\"2DeltaLogL(ideal, data): \", pygsti.tools.two_delta_logl(target_model, dataset))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "colab": { "collapsed_sections": [], "name": "XYI GST circuit generation with commentary 2019-10-17.ipynb", "provenance": [] }, "kernelspec": { "display_name": "api_updates", "language": "python", "name": "api_updates" }, "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.9.13" } }, "nbformat": 4, "nbformat_minor": 4 }