def _build_schedules( self, basis_gates: Set[str]) -> Dict[str, pulse.ScheduleBlock]: """Dummy schedule building.""" with pulse.build(name="x") as schedule: pulse.play(pulse.Drag(160, 0.1, 40, 0), pulse.DriveChannel(0)) schedules = dict() if "x" in basis_gates: schedules["x"] = schedule return schedules
def setUp(self): """Setup the tests.""" super().setUp() self.qubit = 1 with pulse.build(name="x") as sched: pulse.play(pulse.Drag(160, Parameter("amp"), 40, 0.4), pulse.DriveChannel(self.qubit)) self.sched = sched
def test_run_x_cal(self): """Test that we can transpile in the calibrations before and after update. If this test passes then we were successful in running a calibration experiment, updating a pulse parameter, having this parameter propagated to the schedules for use the next time the experiment is run. """ # Initial pulse amplitude init_amp = 0.5 amp_cal = FineXAmplitudeCal(0, self.cals, "x") circs = transpile(amp_cal.circuits(), self.backend, inst_map=amp_cal.transpile_options.inst_map) with pulse.build(name="x") as expected_x: pulse.play(pulse.Drag(160, 0.5, 40, 0), pulse.DriveChannel(0)) with pulse.build(name="sx") as expected_sx: pulse.play(pulse.Drag(160, 0.25, 40, 0), pulse.DriveChannel(0)) self.assertEqual(circs[5].calibrations["x"][((0, ), ())], expected_x) self.assertEqual(circs[5].calibrations["sx"][((0, ), ())], expected_sx) # run the calibration experiment. This should update the amp parameter of x which we test. exp_data = amp_cal.run(self.backend) d_theta = exp_data.analysis_results(1).value.value new_amp = init_amp * np.pi / (np.pi + d_theta) circs = transpile(amp_cal.circuits(), self.backend, inst_map=amp_cal.transpile_options.inst_map) x_cal = circs[5].calibrations["x"][((0, ), ())] # Requires allclose due to numerical precision. self.assertTrue(np.allclose(x_cal.blocks[0].pulse.amp, new_amp)) self.assertFalse(np.allclose(x_cal.blocks[0].pulse.amp, init_amp)) self.assertEqual(circs[5].calibrations["sx"][((0, ), ())], expected_sx)
def test_inst_map_updates(self): """Test that updating a parameter will force an inst map update.""" cals = BackendCalibrations( FakeBelem(), library=FixedFrequencyTransmon(basis_gates=["sx", "x"]), ) # Test the schedules before the update. for qubit in range(5): for gate, amp in [("x", 0.5), ("sx", 0.25)]: with pulse.build() as expected: pulse.play(pulse.Drag(160, amp, 40, 0), pulse.DriveChannel(qubit)) self.assertEqual(cals.default_inst_map.get(gate, qubit), expected) # Update the duration, this should impact all gates. cals.add_parameter_value(200, "duration", schedule="sx") # Test that all schedules now have an updated duration in the inst_map for qubit in range(5): for gate, amp in [("x", 0.5), ("sx", 0.25)]: with pulse.build() as expected: pulse.play(pulse.Drag(200, amp, 40, 0), pulse.DriveChannel(qubit)) self.assertEqual(cals.default_inst_map.get(gate, qubit), expected) # Update the amp on a single qubit, this should only update one gate in the inst_map cals.add_parameter_value(0.8, "amp", qubits=(4,), schedule="sx") # Test that all schedules now have an updated duration in the inst_map for qubit in range(5): for gate, amp in [("x", 0.5), ("sx", 0.25)]: if gate == "sx" and qubit == 4: amp = 0.8 with pulse.build() as expected: pulse.play(pulse.Drag(200, amp, 40, 0), pulse.DriveChannel(qubit)) self.assertEqual(cals.default_inst_map.get(gate, qubit), expected)
def test_setup_withLibrary(self): """Test that we can setup with a library.""" cals = BackendCalibrations( FakeArmonk(), library=FixedFrequencyTransmon( basis_gates=["x", "sx"], default_values={"duration": 320} ), ) # Check the x gate with pulse.build(name="x") as expected: pulse.play(pulse.Drag(duration=320, amp=0.5, sigma=80, beta=0), pulse.DriveChannel(0)) self.assertEqual(cals.get_schedule("x", (0,)), expected) # Check the sx gate with pulse.build(name="sx") as expected: pulse.play(pulse.Drag(duration=320, amp=0.25, sigma=80, beta=0), pulse.DriveChannel(0)) self.assertEqual(cals.get_schedule("sx", (0,)), expected)
def test_update_cals(self): """Test that the calibrations are updated.""" init_beta = 0.0 drag_cal = FineDragCal(0, self.cals, "x", self.backend) transpile_opts = copy.copy(drag_cal.transpile_options.__dict__) transpile_opts["initial_layout"] = list(drag_cal.physical_qubits) circs = transpile(drag_cal.circuits(), **transpile_opts) with pulse.build(name="x") as expected_x: pulse.play(pulse.Drag(160, 0.5, 40, 0), pulse.DriveChannel(0)) with pulse.build(name="sx") as expected_sx: pulse.play(pulse.Drag(160, 0.25, 40, 0), pulse.DriveChannel(0)) self.assertEqual(circs[5].calibrations["x"][((0, ), ())], expected_x) self.assertEqual(circs[5].calibrations["sx"][((0, ), ())], expected_sx) # run the calibration experiment. This should update the beta parameter of x which we test. exp_data = drag_cal.run(self.backend) self.assertExperimentDone(exp_data) d_theta = exp_data.analysis_results(1).value.n sigma = 40 target_angle = np.pi new_beta = -np.sqrt(np.pi) * d_theta * sigma / target_angle**2 transpile_opts = copy.copy(drag_cal.transpile_options.__dict__) transpile_opts["initial_layout"] = list(drag_cal.physical_qubits) circs = transpile(drag_cal.circuits(), **transpile_opts) x_cal = circs[5].calibrations["x"][((0, ), ())] # Requires allclose due to numerical precision. self.assertTrue(np.allclose(x_cal.blocks[0].pulse.beta, new_beta)) self.assertFalse(np.allclose(x_cal.blocks[0].pulse.beta, init_beta)) self.assertEqual(circs[5].calibrations["sx"][((0, ), ())], expected_sx)
def setUp(self): """Setup the tests""" super().setUp() library = FixedFrequencyTransmon() self.backend = FakeArmonk() self.cals = Calibrations.from_backend(self.backend, library) # Add some pulses on the 1-2 transition. d0 = pulse.DriveChannel(0) with pulse.build(name="x12") as x12: with pulse.frequency_offset(-300e6, d0): pulse.play(pulse.Drag(160, Parameter("amp"), 40, 0.0), d0) with pulse.build(name="sx12") as sx12: with pulse.frequency_offset(-300e6, d0): pulse.play(pulse.Drag(160, Parameter("amp"), 40, 0.0), d0) self.cals.add_schedule(x12, 0) self.cals.add_schedule(sx12, 0) self.cals.add_parameter_value(0.4, "amp", 0, "x12") self.cals.add_parameter_value(0.2, "amp", 0, "sx12")
def test_assemble_parametric(self): """Test that parametric pulses can be assembled properly into a PulseQobj.""" sched = pulse.Schedule(name='test_parametric') sched += Play(pulse.Gaussian(duration=25, sigma=4, amp=0.5j), DriveChannel(0)) sched += Play( pulse.Drag(duration=25, amp=0.2 + 0.3j, sigma=7.8, beta=4), DriveChannel(1)) sched += Play(pulse.ConstantPulse(duration=25, amp=1), DriveChannel(2)) sched += Play( pulse.GaussianSquare(duration=150, amp=0.2, sigma=8, width=140), MeasureChannel(0)) << sched.duration backend = FakeOpenPulse3Q() backend.configuration().parametric_pulses = [ 'gaussian', 'drag', 'gaussian_square', 'constant' ] qobj = assemble(sched, backend) self.assertEqual(qobj.config.pulse_library, []) qobj_insts = qobj.experiments[0].instructions self.assertTrue( all(inst.name == 'parametric_pulse' for inst in qobj_insts)) self.assertEqual(qobj_insts[0].pulse_shape, 'gaussian') self.assertEqual(qobj_insts[1].pulse_shape, 'drag') self.assertEqual(qobj_insts[2].pulse_shape, 'constant') self.assertEqual(qobj_insts[3].pulse_shape, 'gaussian_square') self.assertDictEqual(qobj_insts[0].parameters, { 'duration': 25, 'sigma': 4, 'amp': 0.5j }) self.assertDictEqual(qobj_insts[1].parameters, { 'duration': 25, 'sigma': 7.8, 'amp': 0.2 + 0.3j, 'beta': 4 }) self.assertDictEqual(qobj_insts[2].parameters, { 'duration': 25, 'amp': 1 }) self.assertDictEqual(qobj_insts[3].parameters, { 'duration': 150, 'sigma': 8, 'amp': 0.2, 'width': 140 }) self.assertEqual( qobj.to_dict()['experiments'][0]['instructions'][0]['parameters'] ['amp'], 0.5j)
def test_circuits(self): """Test the quantum circuits.""" test_amps = [-0.5, 0, 0.5] rabi = RoughXSXAmplitudeCal(0, self.cals, amplitudes=test_amps) circs = transpile(rabi.circuits(), self.backend, inst_map=rabi.transpile_options.inst_map) for circ, amp in zip(circs, test_amps): self.assertEqual(circ.count_ops()["Rabi"], 1) d0 = pulse.DriveChannel(0) with pulse.build(name="x") as expected_x: pulse.play(pulse.Drag(160, amp, 40, 0), d0) self.assertEqual(circ.calibrations["Rabi"][((0,), (amp,))], expected_x)
def test_assemble_parametric_unsupported(self): """Test that parametric pulses are translated to Waveform if they're not supported by the backend during assemble time. """ sched = pulse.Schedule(name='test_parametric_to_sample_pulse') sched += Play(pulse.Drag(duration=25, amp=0.2+0.3j, sigma=7.8, beta=4), DriveChannel(1)) sched += Play(pulse.Constant(duration=25, amp=1), DriveChannel(2)) backend = FakeOpenPulse3Q() backend.configuration().parametric_pulses = ['something_extra'] qobj = assemble(sched, backend) self.assertNotEqual(qobj.config.pulse_library, []) qobj_insts = qobj.experiments[0].instructions self.assertFalse(hasattr(qobj_insts[0], 'pulse_shape'))
def _single_qubit_schedule( name: str, dur: Parameter, amp: Parameter, sigma: Parameter, beta: Parameter, ) -> ScheduleBlock: """Build a single qubit pulse.""" chan = pulse.DriveChannel(Parameter("ch0")) with pulse.build(name=name) as sched: pulse.play( pulse.Drag(duration=dur, amp=amp, sigma=sigma, beta=beta), chan) return sched
def _single_qubit_schedule( name: str, dur: Parameter, amp: Parameter, sigma: Parameter, beta: Optional[Parameter] = None, ) -> ScheduleBlock: """Build a single qubit pulse.""" chan = pulse.DriveChannel(Parameter("ch0")) if beta is not None: with pulse.build(name=name) as sched: pulse.play(pulse.Drag(duration=dur, amp=amp, sigma=sigma, beta=beta), chan) else: with pulse.build(name=name) as sched: pulse.play(pulse.Gaussian(duration=dur, amp=amp, sigma=sigma), chan) return sched
def test_used_in_calls(self): """Test that we can identify schedules by name when calls are present.""" with pulse.build(name="xp") as xp: pulse.play(pulse.Gaussian(160, 0.5, 40), pulse.DriveChannel(1)) with pulse.build(name="xp2") as xp2: pulse.play(pulse.Gaussian(160, 0.5, 40), pulse.DriveChannel(1)) with pulse.build(name="call_xp") as xp_call: pulse.call(xp) with pulse.build(name="call_call_xp") as xp_call_call: pulse.play(pulse.Drag(160, 0.5, 40, 0.2), pulse.DriveChannel(1)) pulse.call(xp_call) self.assertSetEqual(used_in_calls("xp", [xp_call]), {"call_xp"}) self.assertSetEqual(used_in_calls("xp", [xp2]), set()) self.assertSetEqual(used_in_calls("xp", [xp_call, xp_call_call]), {"call_xp", "call_call_xp"}) with pulse.build(name="xp") as xp: pulse.play(pulse.Gaussian(160, 0.5, 40), pulse.DriveChannel(2)) cr_tone_p = pulse.GaussianSquare(640, 0.2, 64, 500) rotary_p = pulse.GaussianSquare(640, 0.1, 64, 500) cr_tone_m = pulse.GaussianSquare(640, -0.2, 64, 500) rotary_m = pulse.GaussianSquare(640, -0.1, 64, 500) with pulse.build(name="cr") as cr: with pulse.align_sequential(): with pulse.align_left(): pulse.play(rotary_p, pulse.DriveChannel(3)) # Rotary tone pulse.play(cr_tone_p, pulse.ControlChannel(2)) # CR tone. pulse.call(xp) with pulse.align_left(): pulse.play(rotary_m, pulse.DriveChannel(3)) pulse.play(cr_tone_m, pulse.ControlChannel(2)) pulse.call(xp) self.assertSetEqual(used_in_calls("xp", [cr]), {"cr"})
def circuits(self, backend: Optional[Backend] = None) -> List[QuantumCircuit]: """Create the circuits for the Drag calibration. Args: backend: A backend object. Returns: circuits: The circuits that will run the Drag calibration. Raises: CalibrationError: - If the beta parameters in the xp and xm pulses are not the same. - If either the xp or xm pulse do not have at least one Drag pulse. - If the number of different repetition series is not three. """ schedule = self.experiment_options.schedule if schedule is None: beta = Parameter("β") with pulse.build(backend=backend, name="drag") as schedule: pulse.play( pulse.Drag( duration=self.experiment_options.duration, amp=self.experiment_options.amp, sigma=self.experiment_options.sigma, beta=beta, ), pulse.DriveChannel(self._physical_qubits[0]), ) if len(schedule.parameters) != 1: raise CalibrationError( "The schedule for Drag calibration must have one free parameter." f"Found {len(schedule.parameters)}.") beta = next(iter(schedule.parameters)) drag_gate = Gate(name=schedule.name, num_qubits=1, params=[beta]) reps = self.experiment_options.reps if len(reps) != 3: raise CalibrationError( f"{self.__class__.__name__} must use exactly three repetition numbers. " f"Received {reps} with length {len(reps)} != 3.") circuits = [] for idx, rep in enumerate(reps): circuit = QuantumCircuit(1) for _ in range(rep): circuit.append(drag_gate, (0, )) circuit.rz(np.pi, 0) circuit.append(drag_gate, (0, )) circuit.rz(np.pi, 0) circuit.measure_active() circuit.add_calibration(schedule.name, self.physical_qubits, schedule, params=[beta]) for beta_val in self.experiment_options.betas: beta_val = np.round(beta_val, decimals=6) assigned_circuit = circuit.assign_parameters({beta: beta_val}, inplace=False) assigned_circuit.metadata = { "experiment_type": self._type, "qubits": self.physical_qubits, "xval": beta_val, "series": idx, } circuits.append(assigned_circuit) return circuits
def test_drag_roundtrip_serializable(self): """Test round trip JSON serialization""" with pulse.build(name="xp") as sched: pulse.play(pulse.Drag(160, 0.5, 40, Parameter("β")), pulse.DriveChannel(0)) exp = RoughDrag(0, backend=self.backend, schedule=sched) self.assertRoundTripSerializable(exp, self.json_equiv)
def test_fine_drag(self): """Test that we can update from a fine DRAG experiment.""" d_theta = 0.03 # rotation error per single gate. backend = FineDragTestBackend(error=d_theta) qubit = 0 test_tol = 0.005 beta = Parameter("β") chan = Parameter("ch0") with pulse.build(backend=backend, name="xp") as x_plus: pulse.play( pulse.Drag(duration=160, amp=0.208519, sigma=40, beta=beta), pulse.DriveChannel(chan), ) # Setup the calibrations cals = BackendCalibrations(backend) cals.add_schedule(x_plus, num_qubits=1) old_beta = 0.2 cals.add_parameter_value(old_beta, "β", qubit, x_plus) cals.inst_map_add("xp", (qubit, )) # Check that the inst_map has the default beta beta_val = cals.default_inst_map.get("xp", (qubit, )).blocks[0].pulse.beta self.assertEqual(beta_val, old_beta) # Run a Drag calibration experiment. drag = FineXDrag(qubit) drag.set_experiment_options(schedule=cals.get_schedule("xp", qubit)) drag.set_transpile_options(basis_gates=["rz", "xp", "ry"]) exp_data = drag.run(backend).block_for_results() result = exp_data.analysis_results(1) # Test the fit for good measure. self.assertTrue(abs(result.value.value - d_theta) < test_tol) self.assertEqual(result.quality, "good") # Check schedules pre-update expected = x_plus.assign_parameters({ beta: 0.2, chan: qubit }, inplace=False) self.assertEqual(cals.get_schedule("xp", qubit), expected) FineDragUpdater.update(cals, exp_data, parameter="β", schedule="xp") # Check schedules post-update. Here the FineDragTestBackend has a leakage # of 0.03 per gate so the DRAG update rule # -np.sqrt(np.pi) * d_theta * sigma / target_angle ** 2 should give a new beta of # 0.2 - np.sqrt(np.pi) * 0.03 * 40 / (np.pi ** 2) new_beta = old_beta - np.sqrt( np.pi) * result.value.value * 40 / np.pi**2 expected = x_plus.assign_parameters({ beta: new_beta, chan: qubit }, inplace=False) self.assertEqual(cals.get_schedule("xp", qubit), expected) # Check the inst map post update beta_val = cals.default_inst_map.get("xp", (qubit, )).blocks[0].pulse.beta self.assertTrue(np.allclose(beta_val, new_beta))
def test_drag(self): """Test calibrations update from drag.""" backend = DragBackend(gate_name="xp") beta = Parameter("β") qubit = 1 test_tol = 0.02 chan = Parameter("ch0") with pulse.build(backend=backend, name="xp") as x_plus: pulse.play( pulse.Drag(duration=160, amp=0.208519, sigma=40, beta=beta), pulse.DriveChannel(chan), ) # Setup the calibrations cals = BackendCalibrations(backend) cals.add_schedule(x_plus, num_qubits=1) cals.add_parameter_value(0.2, "β", qubit, x_plus) cals.inst_map_add("xp", (qubit, )) # Check that the inst_map has the default beta beta_val = cals.default_inst_map.get("xp", (qubit, )).blocks[0].pulse.beta self.assertEqual(beta_val, 0.2) # Run a Drag calibration experiment. drag = DragCal(qubit) drag.set_experiment_options(schedule=cals.get_schedule( "xp", qubit, assign_params={"β": beta}), ) exp_data = drag.run(backend) exp_data.block_for_results() result = exp_data.analysis_results(1) # Test the fit for good measure. self.assertTrue( abs(result.value.value - backend.ideal_beta) < test_tol) self.assertEqual(result.quality, "good") # Check schedules pre-update expected = x_plus.assign_parameters({ beta: 0.2, chan: 1 }, inplace=False) self.assertEqual(cals.get_schedule("xp", qubit), expected) Drag.update(cals, exp_data, parameter="β", schedule="xp") # Check schedules post-update expected = x_plus.assign_parameters({ beta: result.value.value, chan: 1 }, inplace=False) self.assertEqual(cals.get_schedule("xp", qubit), expected) # Check the inst map post update beta_val = cals.default_inst_map.get("xp", (qubit, )).blocks[0].pulse.beta self.assertTrue(np.allclose(beta_val, result.value.value))