def _build_cr_schedule( self, backend: Backend, flat_top_width: float, sigma: float, ) -> pulse.ScheduleBlock: """GaussianSquared cross resonance pulse. Args: backend: The target backend. flat_top_width: Total length of flat top part of the pulse in units of dt. sigma: Sigma of Gaussian edges in units of dt. Returns: A schedule definition for the cross resonance pulse to measure. """ opt = self.experiment_options # Compute valid integer duration cr_duration = round_pulse_duration(backend=backend, duration=flat_top_width + 2 * sigma * opt.risefall) with pulse.build(backend, default_alignment="left", name="cr") as cross_resonance: # add cross resonance tone pulse.play( pulse.GaussianSquare( duration=cr_duration, amp=opt.amp, sigma=sigma, width=flat_top_width, ), pulse.control_channels(*self.physical_qubits)[0], ) # add cancellation tone if not np.isclose(opt.amp_t, 0.0): pulse.play( pulse.GaussianSquare( duration=cr_duration, amp=opt.amp_t, sigma=sigma, width=flat_top_width, ), pulse.drive_channel(self.physical_qubits[1]), ) else: pulse.delay(cr_duration, pulse.drive_channel(self.physical_qubits[1])) # place holder for empty drive channels. this is necessary due to known pulse gate bug. pulse.delay(cr_duration, pulse.drive_channel(self.physical_qubits[0])) return cross_resonance
def _schedule(self) -> Tuple[pulse.ScheduleBlock, Parameter]: """Create the spectroscopy schedule.""" dt, granularity = self._dt, self._granularity duration = int(granularity * (self.experiment_options.duration / dt // granularity)) sigma = granularity * (self.experiment_options.sigma / dt // granularity) width = granularity * (self.experiment_options.width / dt // granularity) qubit = self.physical_qubits[0] freq_param = Parameter("frequency") with pulse.build(backend=self.backend, name="spectroscopy") as schedule: pulse.shift_frequency(freq_param, pulse.MeasureChannel(qubit)) pulse.play( pulse.GaussianSquare( duration=duration, amp=self.experiment_options.amp, sigma=sigma, width=width, ), pulse.MeasureChannel(qubit), ) pulse.acquire(duration, qubit, pulse.MemorySlot(0)) return schedule, freq_param
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.Constant(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_instruction_schedule_map_export(self): """Test that exporting the inst map works as planned.""" backend = FakeBelem() cals = BackendCalibrations( backend, library=FixedFrequencyTransmon(basis_gates=["sx"]), ) u_chan = pulse.ControlChannel(Parameter("ch0.1")) with pulse.build(name="cr") as cr: pulse.play(pulse.GaussianSquare(640, 0.5, 64, 384), u_chan) cals.add_schedule(cr, num_qubits=2) cals.update_inst_map({"cr"}) for qubit in range(backend.configuration().num_qubits): self.assertTrue(cals.default_inst_map.has("sx", (qubit,))) # based on coupling map of Belem to keep the test robust. expected_pairs = [(0, 1), (1, 0), (1, 2), (2, 1), (1, 3), (3, 1), (3, 4), (4, 3)] coupling_map = set(tuple(pair) for pair in backend.configuration().coupling_map) for pair in expected_pairs: self.assertTrue(pair in coupling_map) self.assertTrue(cals.default_inst_map.has("cr", pair), pair)
def _build_cr_schedule(self, flat_top_width: float) -> pulse.ScheduleBlock: """GaussianSquared cross resonance pulse. Args: flat_top_width: Total length of flat top part of the pulse in units of dt. Returns: A schedule definition for the cross resonance pulse to measure. """ opt = self.experiment_options # Compute valid integer duration duration = flat_top_width + 2 * opt.sigma * opt.risefall valid_duration = int(self._granularity * np.floor(duration / self._granularity)) with pulse.build(default_alignment="left", name="cr") as cross_resonance: # add cross resonance tone pulse.play( pulse.GaussianSquare( duration=valid_duration, amp=opt.amp, sigma=opt.sigma, width=flat_top_width, ), pulse.ControlChannel(self._cr_channel), ) # add cancellation tone if not np.isclose(opt.amp_t, 0.0): pulse.play( pulse.GaussianSquare( duration=valid_duration, amp=opt.amp_t, sigma=opt.sigma, width=flat_top_width, ), pulse.DriveChannel(self.physical_qubits[1]), ) else: pulse.delay(valid_duration, pulse.DriveChannel(self.physical_qubits[1])) # place holder for empty drive channels. this is necessary due to known pulse gate bug. pulse.delay(valid_duration, pulse.DriveChannel(self.physical_qubits[0])) return cross_resonance
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 test_cx_cz_case(self): """Test the case where the coupling map has CX and CZ on different qubits. We use FakeBelem which has a linear coupling map and will restrict ourselves to qubits 0, 1, and 2. The Cals will define a template schedule for CX and CZ. We will mock this with GaussianSquare and Gaussian pulses since the nature of the schedules is irrelevant here. The parameters for CX will only have values for qubis 0 and 1 while the parameters for CZ will only have values for qubis 1 and 2. We therefore will have a CX on qubits 0, 1 in the inst. map and a CZ on qubits 1, 2. """ cals = BackendCalibrations(FakeBelem()) sig = Parameter("σ") dur = Parameter("duration") width = Parameter("width") amp_cx = Parameter("amp") amp_cz = Parameter("amp") uchan = Parameter("ch1.0") with pulse.build(name="cx") as cx: pulse.play( pulse.GaussianSquare(duration=dur, amp=amp_cx, sigma=sig, width=width), pulse.ControlChannel(uchan), ) with pulse.build(name="cz") as cz: pulse.play( pulse.Gaussian(duration=dur, amp=amp_cz, sigma=sig), pulse.ControlChannel(uchan) ) cals.add_schedule(cx, num_qubits=2) cals.add_schedule(cz, num_qubits=2) cals.add_parameter_value(640, "duration", schedule="cx") cals.add_parameter_value(64, "σ", schedule="cx") cals.add_parameter_value(320, "width", qubits=(0, 1), schedule="cx") cals.add_parameter_value(320, "width", qubits=(1, 0), schedule="cx") cals.add_parameter_value(0.1, "amp", qubits=(0, 1), schedule="cx") cals.add_parameter_value(0.8, "amp", qubits=(1, 0), schedule="cx") cals.add_parameter_value(0.1, "amp", qubits=(2, 1), schedule="cz") cals.add_parameter_value(0.8, "amp", qubits=(1, 2), schedule="cz") # CX only defined for qubits (0, 1) and (1,0)? self.assertTrue(cals.default_inst_map.has("cx", (0, 1))) self.assertTrue(cals.default_inst_map.has("cx", (1, 0))) self.assertFalse(cals.default_inst_map.has("cx", (2, 1))) self.assertFalse(cals.default_inst_map.has("cx", (1, 2))) # CZ only defined for qubits (2, 1) and (1,2)? self.assertTrue(cals.default_inst_map.has("cz", (2, 1))) self.assertTrue(cals.default_inst_map.has("cz", (1, 2))) self.assertFalse(cals.default_inst_map.has("cz", (0, 1))) self.assertFalse(cals.default_inst_map.has("cz", (1, 0)))
def _spec_gate_schedule( self, backend: Optional[Backend] = None ) -> Tuple[pulse.ScheduleBlock, Parameter]: """Create the spectroscopy schedule.""" freq_param = Parameter("frequency") with pulse.build(backend=backend, name="spectroscopy") as schedule: pulse.shift_frequency(freq_param, pulse.DriveChannel(self.physical_qubits[0])) pulse.play( pulse.GaussianSquare( duration=self.experiment_options.duration, amp=self.experiment_options.amp, sigma=self.experiment_options.sigma, width=self.experiment_options.width, ), pulse.DriveChannel(self.physical_qubits[0]), ) pulse.shift_frequency(-freq_param, pulse.DriveChannel(self.physical_qubits[0])) return schedule, freq_param
def test_circuit_generation_from_sec(self): """Test generated circuits when time unit is sec.""" backend = CrossResonanceHamiltonianBackend() expr = cr_hamiltonian.CrossResonanceHamiltonian( qubits=(0, 1), flat_top_widths=[500], unit="ns", amp=0.1, sigma=20, risefall=2, ) nearlest_16 = 576 with pulse.build(default_alignment="left", name="cr") as ref_cr_sched: pulse.play( pulse.GaussianSquare( nearlest_16, amp=0.1, sigma=20, width=500, ), pulse.ControlChannel(0), ) pulse.delay(nearlest_16, pulse.DriveChannel(0)) pulse.delay(nearlest_16, pulse.DriveChannel(1)) cr_gate = circuit.Gate("cr_gate", num_qubits=2, params=[500]) expr_circs = expr.circuits(backend) x0_circ = QuantumCircuit(2, 1) x0_circ.append(cr_gate, [0, 1]) x0_circ.h(1) x0_circ.measure(1, 0) x1_circ = QuantumCircuit(2, 1) x1_circ.x(0) x1_circ.append(cr_gate, [0, 1]) x1_circ.h(1) x1_circ.measure(1, 0) y0_circ = QuantumCircuit(2, 1) y0_circ.append(cr_gate, [0, 1]) y0_circ.sdg(1) y0_circ.h(1) y0_circ.measure(1, 0) y1_circ = QuantumCircuit(2, 1) y1_circ.x(0) y1_circ.append(cr_gate, [0, 1]) y1_circ.sdg(1) y1_circ.h(1) y1_circ.measure(1, 0) z0_circ = QuantumCircuit(2, 1) z0_circ.append(cr_gate, [0, 1]) z0_circ.measure(1, 0) z1_circ = QuantumCircuit(2, 1) z1_circ.x(0) z1_circ.append(cr_gate, [0, 1]) z1_circ.measure(1, 0) ref_circs = [x0_circ, y0_circ, z0_circ, x1_circ, y1_circ, z1_circ] for c in ref_circs: c.add_calibration(cr_gate, (0, 1), ref_cr_sched) self.assertListEqual(expr_circs, ref_circs)
def test_circuit_generation(self): """Test generated circuits.""" backend = FakeBogota() # Add granularity to check duration optimization logic setattr( backend.configuration(), "timing_constraints", {"granularity": 16}, ) expr = cr_hamiltonian.CrossResonanceHamiltonian( qubits=(0, 1), flat_top_widths=[1000], amp=0.1, sigma=64, risefall=2, ) expr.backend = backend nearlest_16 = 1248 with pulse.build(default_alignment="left", name="cr") as ref_cr_sched: pulse.play( pulse.GaussianSquare( nearlest_16, amp=0.1, sigma=64, width=1000, ), pulse.ControlChannel(0), ) pulse.delay(nearlest_16, pulse.DriveChannel(0)) pulse.delay(nearlest_16, pulse.DriveChannel(1)) cr_gate = cr_hamiltonian.CrossResonanceHamiltonian.CRPulseGate( width=1000) expr_circs = expr.circuits() x0_circ = QuantumCircuit(2, 1) x0_circ.append(cr_gate, [0, 1]) x0_circ.h(1) x0_circ.measure(1, 0) x1_circ = QuantumCircuit(2, 1) x1_circ.x(0) x1_circ.append(cr_gate, [0, 1]) x1_circ.h(1) x1_circ.measure(1, 0) y0_circ = QuantumCircuit(2, 1) y0_circ.append(cr_gate, [0, 1]) y0_circ.sdg(1) y0_circ.h(1) y0_circ.measure(1, 0) y1_circ = QuantumCircuit(2, 1) y1_circ.x(0) y1_circ.append(cr_gate, [0, 1]) y1_circ.sdg(1) y1_circ.h(1) y1_circ.measure(1, 0) z0_circ = QuantumCircuit(2, 1) z0_circ.append(cr_gate, [0, 1]) z0_circ.measure(1, 0) z1_circ = QuantumCircuit(2, 1) z1_circ.x(0) z1_circ.append(cr_gate, [0, 1]) z1_circ.measure(1, 0) ref_circs = [x0_circ, y0_circ, z0_circ, x1_circ, y1_circ, z1_circ] for c in ref_circs: c.add_calibration(cr_gate, (0, 1), ref_cr_sched) self.assertListEqual(expr_circs, ref_circs)