diff --git a/pygsti/tools/edesigntools.py b/pygsti/tools/edesigntools.py index 696f0946e..2df212f3c 100644 --- a/pygsti/tools/edesigntools.py +++ b/pygsti/tools/edesigntools.py @@ -12,7 +12,6 @@ import numpy as _np - def calculate_edesign_estimated_runtime(edesign, gate_time_dict=None, gate_time_1Q=None, gate_time_2Q=None, measure_reset_time=0.0, interbatch_latency=0.0, total_shots_per_circuit=1000, @@ -331,3 +330,35 @@ def calculate_fisher_information_matrices_by_L(model, circuits, num_shots=1, ter prev_L = L return fisher_information_by_L + +def pad_edesign_with_idle_lines(edesign, line_labels): + """Utility to explicitly pad out ExperimentDesigns with idle lines. + + Parameters + ---------- + edesign: ExperimentDesign + The edesign to be padded. + + line_labels: tuple of int or str + Full line labels for the padded edesign. + + Returns + ------- + ExperimentDesign + An edesign where all circuits have been padded out with missing idle lines + """ + from pygsti.protocols import CombinedExperimentDesign as _CombinedDesign + from pygsti.protocols import SimultaneousExperimentDesign as _SimulDesign + + if set(edesign.qubit_labels) == set(line_labels): + return edesign + + if isinstance(edesign, _CombinedDesign): + new_designs = {} + for subkey, subdesign in edesign.items(): + new_designs[subkey] = pad_edesign_with_idle_lines(subdesign, line_labels) + + return _CombinedDesign(new_designs, qubit_labels=line_labels) + + # SimultaneousDesign with single design + full qubit labels tensors out the circuits with idle lines + return _SimulDesign([edesign], qubit_labels=line_labels) diff --git a/test/unit/tools/test_edesigntools.py b/test/unit/tools/test_edesigntools.py index d63122060..913ddf4ec 100644 --- a/test/unit/tools/test_edesigntools.py +++ b/test/unit/tools/test_edesigntools.py @@ -3,6 +3,8 @@ from pygsti.baseobjs import Label from pygsti.modelpacks import smq2Q_XYICNOT, smq1Q_XYI from pygsti.tools import edesigntools as et +from pygsti.protocols import CircuitListsDesign, SimultaneousExperimentDesign, CombinedExperimentDesign +from pygsti.circuits import Circuit as C from ..util import BaseCase @@ -144,4 +146,98 @@ def test_fisher_information(self): self.assertArraysAlmostEqual(v, fim_by_L[k]) self.assertLess(10*fim_by_L2_time, fim1_time) # Cached version should be very fast compared to uncached - + + def test_generic_design_padding(self): + # Create a series of designs with some overlap when they will be padded out + design_124 = CircuitListsDesign([[ + C.cast('Gx:Q1Gy:Q1@(Q1,Q2,Q4)'), + C.cast('Gx:Q2Gy:Q2@(Q1,Q2,Q4)'), # Will be repeat with design_2 + C.cast('Gx:Q4Gy:Q4@(Q1,Q2,Q4)'), # Will be repeat with design_14 (but only on Q4) + C.cast('Gx:Q1Gy:Q4@(Q1,Q2,Q4)'), # Will be repeat with design_14 (on both Q1 and Q4) + C.cast('[Gx:Q1Gy:Q2][Gy:Q1Gx:Q2]@(Q1,Q2,Q4)') # Will be repeat with sim_design_12 + ]], qubit_labels=('Q1', 'Q2', 'Q4')) + + design_2 = CircuitListsDesign([[ + C.cast('Gx:Q2Gy:Q2@(Q2)'), # Repeat from design_124 after padding + C.cast('Gy:Q2@(Q2)') + ]], qubit_labels=('Q2',)) + + design_14 = CircuitListsDesign([[ + C.cast('Gx:Q4Gy:Q4@(Q1,Q4)'), # Repeat from design_124 after padding + C.cast('Gx:Q1Gy:Q4@(Q1,Q4)'), # Repeat from design_124 after padding + C.cast('Gx:Q1@(Q1,Q4)') + ]], qubit_labels=('Q1', 'Q4')) + + sim_design_1 = CircuitListsDesign([[ + C.cast('Gx:Q1Gy:Q1@(Q1)'), # Q1 part of repeat from design_124 after padding + C.cast('Gx:Q1Gx:Q1') + ]], qubit_labels=('Q1',)) + + sim_design_2 = CircuitListsDesign([[ + C.cast('Gy:Q2Gx:Q2@(Q2)'), # Q2 part of repeat from design_124 after padding + C.cast('Gx:Q2Gx:Q2') + ]], qubit_labels=('Q2',)) + + sim_design_12 = SimultaneousExperimentDesign([sim_design_1, sim_design_2], qubit_labels=('Q1', 'Q2')) + + # The expected deduplicated experiment + expected_design_012345 = CircuitListsDesign([[ + C.cast('Gx:Q1Gy:Q1@(Q0,Q1,Q2,Q3,Q4,Q5)'), # design_124 + C.cast('Gx:Q2Gy:Q2@(Q0,Q1,Q2,Q3,Q4,Q5)'), # design_124 and design_2 + C.cast('Gx:Q4Gy:Q4@(Q0,Q1,Q2,Q3,Q4,Q5)'), # design_124 and design_14 + C.cast('Gx:Q1Gy:Q4@(Q0,Q1,Q2,Q3,Q4,Q5)'), # design_124 and design_14 + C.cast('Gy:Q2@(Q0,Q1,Q2,Q3,Q4,Q5)'), # design_2 + C.cast('Gx:Q1@(Q0,Q1,Q2,Q3,Q4,Q5)'), # design_14 + C.cast('[Gx:Q1Gy:Q2][Gy:Q1Gx:Q2]@(Q0,Q1,Q2,Q3,Q4,Q5)'), # design_124 and sim_design_12 + C.cast('[Gx:Q1Gx:Q2][Gx:Q1Gx:Q2]@(Q0,Q1,Q2,Q3,Q4,Q5)') # sim_design_12 + ]], qubit_labels=('Q0', 'Q1', 'Q2', 'Q3', 'Q4', 'Q5')) + + + # Create nested combined designs and test padding + nested_design = CombinedExperimentDesign({ + '2': design_2, + '14': design_14 + }) + + full_design = CombinedExperimentDesign({ + '124': design_124, + '2+14': nested_design, + 'sim_12': sim_design_12 + }) + + # Padding should dedup "repeats" and add qubits before/during/after the current lines + padded_design = et.pad_edesign_with_idle_lines(full_design, ('Q0', 'Q1', 'Q2', 'Q3', 'Q4', 'Q5')) + + self.assertTrue(set(padded_design.all_circuits_needing_data) == set(expected_design_012345.all_circuits_needing_data), + "Padded experiment circuits did not match expected experiment circuits") + + def test_gst_design_padding(self): + # Get GST designs + gst_1 = smq1Q_XYI.create_gst_experiment_design(8, ('Q1',)) + gst_2 = smq1Q_XYI.create_gst_experiment_design(8, ('Q2',)) + gst_12 = smq2Q_XYICNOT.create_gst_experiment_design(8, ('Q1', 'Q2')) + + # Get nested combined design + nested_12 = CombinedExperimentDesign({ + '1': gst_1, + '2': gst_2, + }) + + full_gst = CombinedExperimentDesign({ + '1+2': nested_12, + '12': gst_12 + }) + + # Pad and test + padded_gst_design = et.pad_edesign_with_idle_lines(full_gst, ('Q1', 'Q2')) + + padded_circs_1 = [circ.insert_idling_lines(None, ('Q2',)) for circ in gst_1.all_circuits_needing_data] + self.assertTrue(set(padded_gst_design.all_circuits_needing_data).issuperset(set(padded_circs_1)), + "GST on qubit 1 was not a subset of padded experiment design") + + padded_circs_2 = [circ.insert_idling_lines('Q2', ('Q1',)) for circ in gst_2.all_circuits_needing_data] + self.assertTrue(set(padded_gst_design.all_circuits_needing_data).issuperset(set(padded_circs_2)), + "GST on qubit 2 was not a subset of the padded experiment design") + + self.assertTrue(set(padded_gst_design.all_circuits_needing_data).issuperset(set(gst_12.all_circuits_needing_data)), + "2Q GST was not a subset of the padded experiment design") \ No newline at end of file