This notebook shows a simple example of how to use pyGSTi with Cirq. It has three sections:
import cirq
import pygsti
from pygsti.modelpacks import smq1Q_XYI
from pygsti.circuits import Circuit
import numpy as np
import tqdm
target_model = smq1Q_XYI.target_model()
preps = smq1Q_XYI.prep_fiducials()
effects = smq1Q_XYI.meas_fiducials()
germs = smq1Q_XYI.germs()
max_lengths = list(np.logspace(0, 10, 11, base=2, dtype=int))
print(max_lengths)
pygsti_circuits = pygsti.circuits.gstcircuits.create_lsgst_circuits(target_model, preps, effects, germs, max_lengths)
len(pygsti_circuits)
cirq.Circuit
's¶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.
q0 = cirq.GridQubit(8, 3)
qubit_label_dict = {0: q0}
Do an example conversion.
pygsti_circuit = pygsti_circuits[111]
print('pyGSTi:')
print(pygsti_circuit)
print('Cirq:')
print(pygsti_circuit.convert_to_cirq(qubit_label_dict))
Do another example conversion.
pygsti_circuit = pygsti_circuits[90]
print('pyGSTi:')
print(pygsti_circuit)
print('Cirq:')
print(pygsti_circuit.convert_to_cirq(qubit_label_dict))
Now, lets try the same thing but specifing a wait duration for the idle operation.
wait_duration = cirq.Duration(nanos=100)
pygsti_circuit = pygsti_circuits[111]
print('pyGSTi:')
print(pygsti_circuit)
print('Cirq:')
print(pygsti_circuit.convert_to_cirq(qubit_label_dict, wait_duration))
pygsti_circuit = pygsti_circuits[90]
print('pyGSTi:')
print(pygsti_circuit)
print('Cirq:')
print(pygsti_circuit.convert_to_cirq(qubit_label_dict, wait_duration))
Now, convert all the circuits.
cirq_circuits = [c.convert_to_cirq(qubit_label_dict, wait_duration) for c in tqdm.tqdm(pygsti_circuits)]
cirq_circuits
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.
We also have support for converting a cirq circuit to a pyGSTi circuit, which is demonstrated below. Begin by constructing a cirq circuit directly.
#create to cirq qubit objects
qubit_00 = cirq.GridQubit(0,0)
qubit_01 = cirq.GridQubit(0,1)
#define a series of Moment objects, which fill the same role as circuit layers in pyGSTi.
moment1 = cirq.Moment([cirq.XPowGate(exponent=.5).on(qubit_00), cirq.I(qubit_01)])
moment2 = cirq.Moment([cirq.I(qubit_00), cirq.I(qubit_01)])
#This weird looking gate is the so-called N gate.
moment3 = cirq.Moment([cirq.PhasedXZGate(axis_phase_exponent=0.14758361765043326,
x_exponent=0.4195693767448338,
z_exponent=-0.2951672353008665).on(qubit_00),
cirq.I(qubit_01)])
moment4 = cirq.Moment([cirq.H(qubit_00), (cirq.T**-1).on(qubit_01)])
moment5 = cirq.Moment([cirq.CNOT.on(qubit_00, qubit_01)])
cirq_circuit_example = cirq.Circuit([moment1, moment2, moment3, moment4, moment5])
print(cirq_circuit_example)
To convert this into a pyGSTi circuit we can use the from_cirq
class method of the Circuit class.
converted_cirq_circuit_default = Circuit.from_cirq(cirq_circuit_example)
print(converted_cirq_circuit_default)
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.
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.converted_cirq_circuit_custom_qubit_map = Circuit.from_cirq(cirq_circuit_example, qubit_conversion={qubit_00: 'Qalice', qubit_01: 'Qbob'})
print(converted_cirq_circuit_custom_qubit_map)
remove_implied_idles
to False
.converted_cirq_circuit_implied_idles = Circuit.from_cirq(cirq_circuit_example, remove_implied_idles=True)
print(converted_cirq_circuit_implied_idles)
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.#auto is the default value, explicitly including here for comparison to alternative options.
converted_cirq_circuit_global_idle = Circuit.from_cirq(cirq_circuit_example, global_idle_replacement_label='auto')
print(converted_cirq_circuit_global_idle)
converted_cirq_circuit_global_idle_1 = Circuit.from_cirq(cirq_circuit_example, global_idle_replacement_label='Gbanana')
print(converted_cirq_circuit_global_idle_1)
from pygsti.baseobjs import Label
converted_cirq_circuit_global_idle_2 = Circuit.from_cirq(cirq_circuit_example, global_idle_replacement_label=Label('Gbanana', ('Q0_0','Q0_1')))
print(converted_cirq_circuit_global_idle_2)
converted_cirq_circuit_global_idle_3 = Circuit.from_cirq(cirq_circuit_example, global_idle_replacement_label= None)
print(converted_cirq_circuit_global_idle_3)
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.custom_gate_map = pygsti.tools.internalgates.cirq_gatenames_standard_conversions()
custom_gate_map[cirq.H] = 'Gdefinitelynoth'
converted_cirq_circuit_custom_gate_map = Circuit.from_cirq(cirq_circuit_example, cirq_gate_conversion=custom_gate_map)
print(converted_cirq_circuit_custom_gate_map)
Add measurements to the circuits.
for circuit in cirq_circuits:
circuit.append(cirq.measure(q0, key='result'))
Simulate the circuits (or run them on a real quantum computer!)
simulator = cirq.Simulator()
results = [simulator.run(circuit, repetitions=1000) for circuit in tqdm.tqdm(cirq_circuits)]
Load everything the results into a pyGSTi dataset.
dataset = pygsti.data.dataset.DataSet()
for pygsti_circuit, trial_result in zip(pygsti_circuits, results):
dataset.add_cirq_trial_result(pygsti_circuit, trial_result, key='result')
Perform GST.
gst_results = pygsti.run_stdpractice_gst(dataset, target_model, preps, effects, germs, max_lengths, modes=["full TP","Target"], verbosity=1)
See what if finds.
mdl_estimate = gst_results.estimates['full TP'].models['stdgaugeopt']
print("2DeltaLogL(estimate, data): ", pygsti.tools.two_delta_logl(mdl_estimate, dataset))
print("2DeltaLogL(ideal, data): ", pygsti.tools.two_delta_logl(target_model, dataset))