def test_schedule_with_multiple_parameters_under_same_name(self): """Test getting schedule with parameters that have the same name.""" param1 = Parameter('param') param2 = Parameter('param') param3 = Parameter('param') target_sched = Schedule() target_sched.insert(0, ShiftPhase(param1, DriveChannel(0)), inplace=True) target_sched.insert(10, ShiftPhase(param2, DriveChannel(0)), inplace=True) target_sched.insert(20, ShiftPhase(param3, DriveChannel(0)), inplace=True) inst_map = InstructionScheduleMap() inst_map.add('target_sched', (0, ), target_sched) ref_sched = Schedule() ref_sched.insert(0, ShiftPhase(1.23, DriveChannel(0)), inplace=True) ref_sched.insert(10, ShiftPhase(1.23, DriveChannel(0)), inplace=True) ref_sched.insert(20, ShiftPhase(1.23, DriveChannel(0)), inplace=True) test_sched = inst_map.get('target_sched', (0, ), param=1.23) for test_inst, ref_inst in zip(test_sched.instructions, ref_sched.instructions): self.assertEqual(test_inst[0], ref_inst[0]) self.assertAlmostEqual(test_inst[1], ref_inst[1])
def test_partially_bound_callable(self): """Test register partial function.""" import functools def callable_schedule(par_b, par_a): sched = Schedule() sched.insert(10, Play(Constant(10, par_b), DriveChannel(0)), inplace=True) sched.insert(20, Play(Constant(10, par_a), DriveChannel(0)), inplace=True) return sched ref_sched = Schedule() ref_sched.insert(10, Play(Constant(10, 0.1), DriveChannel(0)), inplace=True) ref_sched.insert(20, Play(Constant(10, 0.2), DriveChannel(0)), inplace=True) inst_map = InstructionScheduleMap() def test_callable_sched1(par_b): return callable_schedule(par_b, 0.2) inst_map.add("my_gate1", (0,), test_callable_sched1, ["par_b"]) ret_sched = inst_map.get("my_gate1", (0,), par_b=0.1) self.assertEqual(ret_sched, ref_sched) # bind partially test_callable_sched2 = functools.partial(callable_schedule, par_a=0.2) inst_map.add("my_gate2", (0,), test_callable_sched2, ["par_b"]) ret_sched = inst_map.get("my_gate2", (0,), par_b=0.1) self.assertEqual(ret_sched, ref_sched)
def test_pass_alive_with_dcx_ish(self): """Test if the pass is not terminated by error with direct CX input.""" cx_sched = Schedule() # Fake direct cr cx_sched.insert(0, Play(GaussianSquare(800, 0.2, 64, 544), ControlChannel(1)), inplace=True) # Fake direct compensation tone # Compensation tone doesn't have dedicated pulse class. # So it's reported as a waveform now. compensation_tone = Waveform(0.1 * np.ones(800, dtype=complex)) cx_sched.insert(0, Play(compensation_tone, DriveChannel(0)), inplace=True) inst_map = InstructionScheduleMap() inst_map.add("cx", (1, 0), schedule=cx_sched) theta = pi / 3 rzx_qc = circuit.QuantumCircuit(2) rzx_qc.rzx(theta, 1, 0) pass_ = RZXCalibrationBuilder(instruction_schedule_map=inst_map) with self.assertWarns(UserWarning): # User warning that says q0 q1 is invalid cal_qc = PassManager(pass_).run(rzx_qc) self.assertEqual(cal_qc, rzx_qc)
def test_schedule_with_non_alphanumeric_ordering(self): """Test adding and getting schedule with non obvious parameter ordering.""" theta = Parameter("theta") phi = Parameter("phi") lamb = Parameter("lambda") target_sched = Schedule() target_sched.insert(0, ShiftPhase(theta, DriveChannel(0)), inplace=True) target_sched.insert(10, ShiftPhase(phi, DriveChannel(0)), inplace=True) target_sched.insert(20, ShiftPhase(lamb, DriveChannel(0)), inplace=True) inst_map = InstructionScheduleMap() inst_map.add("target_sched", (0,), target_sched, arguments=["theta", "phi", "lambda"]) ref_sched = Schedule() ref_sched.insert(0, ShiftPhase(0, DriveChannel(0)), inplace=True) ref_sched.insert(10, ShiftPhase(1, DriveChannel(0)), inplace=True) ref_sched.insert(20, ShiftPhase(2, DriveChannel(0)), inplace=True) # if parameter is alphanumerical ordering this maps to # theta -> 2 # phi -> 1 # lamb -> 0 # however non alphanumerical ordering is specified in add method thus mapping should be # theta -> 0 # phi -> 1 # lamb -> 2 test_sched = inst_map.get("target_sched", (0,), 0, 1, 2) for test_inst, ref_inst in zip(test_sched.instructions, ref_sched.instructions): self.assertEqual(test_inst[0], ref_inst[0]) self.assertEqual(test_inst[1], ref_inst[1])
def callable_schedule(par_b, par_a): sched = Schedule() sched.insert(10, Play(Constant(10, par_b), DriveChannel(0)), inplace=True) sched.insert(20, Play(Constant(10, par_a), DriveChannel(0)), inplace=True) return sched
def test_binding_too_many_parameters(self): """Test getting schedule with too many parameter binding.""" param = Parameter('param') target_sched = Schedule() target_sched.insert(0, ShiftPhase(param, DriveChannel(0)), inplace=True) inst_map = InstructionScheduleMap() inst_map.add('target_sched', (0,), target_sched) with self.assertRaises(PulseError): inst_map.get('target_sched', (0,), 0, 1, 2, 3)
def test_binding_unassigned_parameters(self): """Test getting schedule with unassigned parameter binding.""" param = Parameter("param") target_sched = Schedule() target_sched.insert(0, ShiftPhase(param, DriveChannel(0)), inplace=True) inst_map = InstructionScheduleMap() inst_map.add("target_sched", (0,), target_sched) with self.assertRaises(PulseError): inst_map.get("target_sched", (0,), P0=0)
def test_get_schedule_with_unbound_parameter(self): """Test get schedule with partial binding.""" param1 = Parameter('param1') param2 = Parameter('param2') target_sched = Schedule() target_sched.insert(0, ShiftPhase(param1, DriveChannel(0)), inplace=True) target_sched.insert(10, ShiftPhase(param2, DriveChannel(0)), inplace=True) inst_map = InstructionScheduleMap() inst_map.add('target_sched', (0, ), target_sched) ref_sched = Schedule() ref_sched.insert(0, ShiftPhase(param1, DriveChannel(0)), inplace=True) ref_sched.insert(10, ShiftPhase(1.23, DriveChannel(0)), inplace=True) test_sched = inst_map.get('target_sched', (0, ), param2=1.23) for test_inst, ref_inst in zip(test_sched.instructions, ref_sched.instructions): self.assertEqual(test_inst[0], ref_inst[0]) self.assertAlmostEqual(test_inst[1], ref_inst[1])
def test_pulse_to_signals(self): """Generic test.""" sched = Schedule(name="Schedule") sched += Play(Drag(duration=20, amp=0.5, sigma=4, beta=0.5), DriveChannel(0)) sched += ShiftPhase(1.0, DriveChannel(0)) sched += Play(Drag(duration=20, amp=0.5, sigma=4, beta=0.5), DriveChannel(0)) sched += ShiftFrequency(0.5, DriveChannel(0)) sched += Play( GaussianSquare(duration=200, amp=0.3, sigma=4, width=150), DriveChannel(0)) test_gaussian = GaussianSquare(duration=200, amp=0.3, sigma=4, width=150) sched = sched.insert(0, Play(test_gaussian, DriveChannel(1))) converter = InstructionToSignals(dt=1, carriers=None) signals = converter.get_signals(sched) self.assertEqual(len(signals), 2) self.assertTrue(isinstance(signals[0], PiecewiseConstant)) self.assertTrue(isinstance(signals[0], PiecewiseConstant)) samples = test_gaussian.get_waveform().samples self.assertTrue( np.allclose(signals[1].samples[0:len(samples)], samples))
def test_assemble_meas_map(self): """Test assembling a single schedule, no lo config.""" schedule = Schedule(name='fake_experiment') schedule = schedule.insert( 5, Acquire(5, AcquireChannel(0), MemorySlot(0))) schedule = schedule.insert( 5, Acquire(5, AcquireChannel(1), MemorySlot(1))) qobj = assemble(schedule, qubit_lo_freq=self.default_qubit_lo_freq, meas_lo_freq=self.default_meas_lo_freq, meas_map=[[0], [1]]) validate_qobj_against_schema(qobj) with self.assertRaises(QiskitError): assemble(schedule, qubit_lo_freq=self.default_qubit_lo_freq, meas_lo_freq=self.default_meas_lo_freq, meas_map=[[0, 1, 2]])
def pad(schedule: Schedule, channels: Optional[Iterable[Channel]] = None, until: Optional[int] = None) -> Schedule: """Pad the input ``Schedule`` with ``Delay`` s on all unoccupied timeslots until ``until`` if it is provided, otherwise until ``schedule.duration``. Args: schedule: Schedule to pad. channels: Channels to pad. Defaults to all channels in ``schedule`` if not provided. If the supplied channel is not a member of ``schedule``, it will be added. until: Time to pad until. Defaults to ``schedule.duration`` if not provided. Returns: The padded schedule. """ until = until or schedule.duration channels = channels or schedule.channels for channel in channels: if channel not in schedule.channels: schedule |= Delay(until, channel) continue curr_time = 0 # TODO: Replace with method of getting instructions on a channel for interval in schedule.timeslots[channel]: if curr_time >= until: break if interval[0] != curr_time: end_time = min(interval[0], until) schedule = schedule.insert( curr_time, Delay(end_time - curr_time, channel)) curr_time = interval[1] if curr_time < until: schedule = schedule.insert(curr_time, Delay(until - curr_time, channel)) return schedule
def get_calibration(self, params: List, qubits: List) -> Schedule: """ Args: params: Parameters of the RZXGate(theta). I.e. params[0] is theta. qubits: List of qubits for which to get the schedules. The first qubit is the control and the second is the target. Returns: schedule: The calibration schedule for the RZXGate(theta). Raises: QiskitError: if the control and target qubits cannot be identified or the backend does not support cx between the qubits. """ theta = params[0] q1, q2 = qubits[0], qubits[1] if not self._inst_map.has('cx', qubits): raise QiskitError( 'This transpilation pass requires the backend to support cx ' 'between qubits %i and %i.' % (q1, q2)) cx_sched = self._inst_map.get('cx', qubits=(q1, q2)) rzx_theta = Schedule(name='rzx(%.3f)' % theta) if theta == 0.0: return rzx_theta crs, comp_tones, shift_phases = [], [], [] control, target = None, None for time, inst in cx_sched.instructions: if isinstance(inst, ShiftPhase) and time == 0: shift_phases.append(ShiftPhase(-theta, inst.channel)) # Identify the CR pulses. if isinstance(inst, Play) and not isinstance(inst, ShiftPhase): if isinstance(inst.channel, ControlChannel): crs.append((time, inst)) # Identify the compensation tones. if isinstance(inst.channel, DriveChannel) and not isinstance(inst, ShiftPhase): if isinstance(inst.pulse, GaussianSquare): comp_tones.append((time, inst)) target = inst.channel.index control = q1 if target == q2 else q2 if control is None: raise QiskitError('Control qubit is None.') if target is None: raise QiskitError('Target qubit is None.') echo_x = self._inst_map.get('x', qubits=control) # Build the schedule for inst in shift_phases: rzx_theta = rzx_theta.insert(0, inst) # Stretch/compress the CR gates and compensation tones cr1 = self.rescale_cr_inst(crs[0][1], theta) cr2 = self.rescale_cr_inst(crs[1][1], theta) if len(comp_tones) == 0: comp1, comp2 = None, None elif len(comp_tones) == 2: comp1 = self.rescale_cr_inst(comp_tones[0][1], theta) comp2 = self.rescale_cr_inst(comp_tones[1][1], theta) else: raise QiskitError( 'CX must have either 0 or 2 rotary tones between qubits %i and %i ' 'but %i were found.' % (control, target, len(comp_tones))) # Build the schedule for the RZXGate rzx_theta = rzx_theta.insert(0, cr1) if comp1 is not None: rzx_theta = rzx_theta.insert(0, comp1) rzx_theta = rzx_theta.insert(comp1.duration, echo_x) time = comp1.duration + echo_x.duration rzx_theta = rzx_theta.insert(time, cr2) if comp2 is not None: rzx_theta = rzx_theta.insert(time, comp2) time = 2 * comp1.duration + echo_x.duration rzx_theta = rzx_theta.insert(time, echo_x) # Reverse direction of the ZX with Hadamard gates if control == qubits[0]: return rzx_theta else: rzc = self._inst_map.get('rz', [control], np.pi / 2) sxc = self._inst_map.get('sx', [control]) rzt = self._inst_map.get('rz', [target], np.pi / 2) sxt = self._inst_map.get('sx', [target]) h_sched = Schedule(name='hadamards') h_sched = h_sched.insert(0, rzc) h_sched = h_sched.insert(0, sxc) h_sched = h_sched.insert(sxc.duration, rzc) h_sched = h_sched.insert(0, rzt) h_sched = h_sched.insert(0, sxt) h_sched = h_sched.insert(sxc.duration, rzt) rzx_theta = h_sched.append(rzx_theta) return rzx_theta.append(h_sched)
def get_calibration(self, params: List, qubits: List) -> Schedule: """ Builds the calibration schedule for the RZXGate(theta) without echos. Args: params: Parameters of the RZXGate(theta). I.e. params[0] is theta. qubits: List of qubits for which to get the schedules. The first qubit is the control and the second is the target. Returns: schedule: The calibration schedule for the RZXGate(theta). Raises: QiskitError: If the control and target qubits cannot be identified, or the backend does not support a cx gate between the qubits, or the backend does not natively support the specified direction of the cx. """ theta = params[0] q1, q2 = qubits[0], qubits[1] if not self._inst_map.has("cx", qubits): raise QiskitError( "This transpilation pass requires the backend to support cx " "between qubits %i and %i." % (q1, q2)) cx_sched = self._inst_map.get("cx", qubits=(q1, q2)) rzx_theta = Schedule(name="rzx(%.3f)" % theta) if theta == 0.0: return rzx_theta control, target = None, None for _, inst in cx_sched.instructions: # Identify the compensation tones. if isinstance(inst.channel, DriveChannel) and isinstance( inst, Play): if isinstance(inst.pulse, GaussianSquare): target = inst.channel.index control = q1 if target == q2 else q2 if control is None: raise QiskitError("Control qubit is None.") if target is None: raise QiskitError("Target qubit is None.") if control != qubits[0]: raise QiskitError( "RZXCalibrationBuilderNoEcho only supports hardware-native RZX gates." ) # Get the filtered Schedule instructions for the CR gates and compensation tones. crs = cx_sched.filter(*[self._filter_control]).instructions rotaries = cx_sched.filter(*[self._filter_drive]).instructions # Stretch/compress the CR gates and compensation tones. cr = self.rescale_cr_inst(crs[0][1], 2 * theta) rot = self.rescale_cr_inst(rotaries[0][1], 2 * theta) # Build the schedule for the RZXGate without the echos. rzx_theta = rzx_theta.insert(0, cr) rzx_theta = rzx_theta.insert(0, rot) rzx_theta = rzx_theta.insert(0, Delay(cr.duration, DriveChannel(control))) return rzx_theta
def get_calibration(self, node_op: CircuitInst, qubits: List) -> Union[Schedule, ScheduleBlock]: """Builds the calibration schedule for the RZXGate(theta) without echos. Args: node_op: Instruction of the RZXGate(theta). I.e. params[0] is theta. qubits: List of qubits for which to get the schedules. The first qubit is the control and the second is the target. Returns: schedule: The calibration schedule for the RZXGate(theta). Raises: QiskitError: If the control and target qubits cannot be identified, or the backend does not natively support the specified direction of the cx. CalibrationNotAvailable: RZX schedule cannot be built for input node. """ theta = node_op.params[0] rzx_theta = Schedule(name="rzx(%.3f)" % theta) rzx_theta.metadata["publisher"] = CalibrationPublisher.QISKIT if np.isclose(theta, 0.0): return rzx_theta cx_sched = self._inst_map.get("cx", qubits=qubits) cal_type, cr_tones, comp_tones = _check_calibration_type(cx_sched) if cal_type != CXCalType.ECR: if self._verbose: warnings.warn( f"CX instruction for qubits {qubits} is likely {cal_type.value} sequence. " "Pulse stretch for this calibration is not currently implemented. " "RZX schedule is not generated for this qubit pair.", UserWarning, ) raise CalibrationNotAvailable if len(comp_tones) == 0: raise QiskitError( f"{repr(cx_sched)} has no target compensation tones. " "Native CR direction cannot be determined.") # Determine native direction, assuming only single drive channel per qubit. # This guarantees channel and qubit index equality. is_native = comp_tones[0].channel.index == qubits[1] stretched_cr_tone = self.rescale_cr_inst(cr_tones[0], 2 * theta) stretched_comp_tone = self.rescale_cr_inst(comp_tones[0], 2 * theta) if is_native: # Placeholder to make pulse gate work delay = Delay(stretched_cr_tone.duration, DriveChannel(qubits[0])) # This doesn't remove unwanted instruction such as ZI # These terms are eliminated along with other gates around the pulse gate. rzx_theta = rzx_theta.insert(0, stretched_cr_tone, inplace=True) rzx_theta = rzx_theta.insert(0, stretched_comp_tone, inplace=True) rzx_theta = rzx_theta.insert(0, delay, inplace=True) return rzx_theta raise QiskitError( "RZXCalibrationBuilderNoEcho only supports hardware-native RZX gates." )
def get_calibration(self, node_op: CircuitInst, qubits: List) -> Union[Schedule, ScheduleBlock]: """Builds the calibration schedule for the RZXGate(theta) with echos. Args: node_op: Instruction of the RZXGate(theta). I.e. params[0] is theta. qubits: List of qubits for which to get the schedules. The first qubit is the control and the second is the target. Returns: schedule: The calibration schedule for the RZXGate(theta). Raises: QiskitError: If the control and target qubits cannot be identified. CalibrationNotAvailable: RZX schedule cannot be built for input node. """ theta = node_op.params[0] rzx_theta = Schedule(name="rzx(%.3f)" % theta) rzx_theta.metadata["publisher"] = CalibrationPublisher.QISKIT if np.isclose(theta, 0.0): return rzx_theta cx_sched = self._inst_map.get("cx", qubits=qubits) cal_type, cr_tones, comp_tones = _check_calibration_type(cx_sched) if cal_type != CXCalType.ECR: if self._verbose: warnings.warn( f"CX instruction for qubits {qubits} is likely {cal_type.value} sequence. " "Pulse stretch for this calibration is not currently implemented. " "RZX schedule is not generated for this qubit pair.", UserWarning, ) raise CalibrationNotAvailable if len(comp_tones) == 0: raise QiskitError( f"{repr(cx_sched)} has no target compensation tones. " "Native CR direction cannot be determined.") # Determine native direction, assuming only single drive channel per qubit. # This guarantees channel and qubit index equality. is_native = comp_tones[0].channel.index == qubits[1] stretched_cr_tones = list( map(lambda p: self.rescale_cr_inst(p, theta), cr_tones)) stretched_comp_tones = list( map(lambda p: self.rescale_cr_inst(p, theta), comp_tones)) if is_native: xgate = self._inst_map.get("x", qubits[0]) for cr, comp in zip(stretched_cr_tones, stretched_comp_tones): current_dur = rzx_theta.duration rzx_theta.insert(current_dur, cr, inplace=True) rzx_theta.insert(current_dur, comp, inplace=True) rzx_theta.append(xgate, inplace=True) else: # Add hadamard gate to flip xgate = self._inst_map.get("x", qubits[1]) szc = self._inst_map.get("rz", qubits[1], np.pi / 2) sxc = self._inst_map.get("sx", qubits[1]) szt = self._inst_map.get("rz", qubits[0], np.pi / 2) sxt = self._inst_map.get("sx", qubits[0]) # Hadamard to control rzx_theta.insert(0, szc, inplace=True) rzx_theta.insert(0, sxc, inplace=True) rzx_theta.insert(sxc.duration, szc, inplace=True) # Hadamard to target rzx_theta.insert(0, szt, inplace=True) rzx_theta.insert(0, sxt, inplace=True) rzx_theta.insert(sxt.duration, szt, inplace=True) for cr, comp in zip(stretched_cr_tones, stretched_comp_tones): current_dur = rzx_theta.duration rzx_theta.insert(current_dur, cr, inplace=True) rzx_theta.insert(current_dur, comp, inplace=True) rzx_theta.append(xgate, inplace=True) current_dur = rzx_theta.duration # Hadamard to control rzx_theta.insert(current_dur, szc, inplace=True) rzx_theta.insert(current_dur, sxc, inplace=True) rzx_theta.insert(current_dur + sxc.duration, szc, inplace=True) # Hadamard to target rzx_theta.insert(current_dur, szt, inplace=True) rzx_theta.insert(current_dur, sxt, inplace=True) rzx_theta.insert(current_dur + sxt.duration, szt, inplace=True) return rzx_theta
def get_calibration(self, node_op: CircuitInst, qubits: List) -> Union[Schedule, ScheduleBlock]: """Builds the calibration schedule for the RZXGate(theta) with echos. Args: node_op: Instruction of the RZXGate(theta). I.e. params[0] is theta. qubits: List of qubits for which to get the schedules. The first qubit is the control and the second is the target. Returns: schedule: The calibration schedule for the RZXGate(theta). Raises: QiskitError: if the control and target qubits cannot be identified or the backend does not support cx between the qubits. """ theta = node_op.params[0] q1, q2 = qubits[0], qubits[1] if not self._inst_map.has("cx", qubits): raise QiskitError( "This transpilation pass requires the backend to support cx " "between qubits %i and %i." % (q1, q2)) cx_sched = self._inst_map.get("cx", qubits=(q1, q2)) rzx_theta = Schedule(name="rzx(%.3f)" % theta) rzx_theta.metadata["publisher"] = CalibrationPublisher.QISKIT if theta == 0.0: return rzx_theta crs, comp_tones = [], [] control, target = None, None for time, inst in cx_sched.instructions: # Identify the CR pulses. if isinstance(inst, Play) and not isinstance(inst, ShiftPhase): if isinstance(inst.channel, ControlChannel): crs.append((time, inst)) # Identify the compensation tones. if isinstance(inst.channel, DriveChannel) and not isinstance(inst, ShiftPhase): if isinstance(inst.pulse, GaussianSquare): comp_tones.append((time, inst)) target = inst.channel.index control = q1 if target == q2 else q2 if control is None: raise QiskitError("Control qubit is None.") if target is None: raise QiskitError("Target qubit is None.") echo_x = self._inst_map.get("x", qubits=control) # Build the schedule # Stretch/compress the CR gates and compensation tones cr1 = self.rescale_cr_inst(crs[0][1], theta) cr2 = self.rescale_cr_inst(crs[1][1], theta) if len(comp_tones) == 0: comp1, comp2 = None, None elif len(comp_tones) == 2: comp1 = self.rescale_cr_inst(comp_tones[0][1], theta) comp2 = self.rescale_cr_inst(comp_tones[1][1], theta) else: raise QiskitError( "CX must have either 0 or 2 rotary tones between qubits %i and %i " "but %i were found." % (control, target, len(comp_tones))) # Build the schedule for the RZXGate rzx_theta = rzx_theta.insert(0, cr1) if comp1 is not None: rzx_theta = rzx_theta.insert(0, comp1) rzx_theta = rzx_theta.insert(comp1.duration, echo_x) time = comp1.duration + echo_x.duration rzx_theta = rzx_theta.insert(time, cr2) if comp2 is not None: rzx_theta = rzx_theta.insert(time, comp2) time = 2 * comp1.duration + echo_x.duration rzx_theta = rzx_theta.insert(time, echo_x) # Reverse direction of the ZX with Hadamard gates if control == qubits[0]: return rzx_theta else: rzc = self._inst_map.get("rz", [control], np.pi / 2) sxc = self._inst_map.get("sx", [control]) rzt = self._inst_map.get("rz", [target], np.pi / 2) sxt = self._inst_map.get("sx", [target]) h_sched = Schedule(name="hadamards") h_sched = h_sched.insert(0, rzc) h_sched = h_sched.insert(0, sxc) h_sched = h_sched.insert(sxc.duration, rzc) h_sched = h_sched.insert(0, rzt) h_sched = h_sched.insert(0, sxt) h_sched = h_sched.insert(sxc.duration, rzt) rzx_theta = h_sched.append(rzx_theta) return rzx_theta.append(h_sched)
def measure(qubits: List[int], backend=None, inst_map: Optional[InstructionScheduleMap] = None, meas_map: Optional[Union[List[List[int]], Dict[int, List[int]]]] = None, qubit_mem_slots: Optional[Dict[int, int]] = None, measure_name: str = 'measure') -> Schedule: """ Return a schedule which measures the requested qubits according to the given instruction mapping and measure map, or by using the defaults provided by the backend. By default, the measurement results for each qubit are trivially mapped to the qubit index. This behavior is overridden by qubit_mem_slots. For instance, to measure qubit 0 into MemorySlot(1), qubit_mem_slots can be provided as {0: 1}. Args: qubits: List of qubits to be measured. backend (BaseBackend): A backend instance, which contains hardware-specific data required for scheduling. inst_map: Mapping of circuit operations to pulse schedules. If None, defaults to the ``instruction_schedule_map`` of ``backend``. meas_map: List of sets of qubits that must be measured together. If None, defaults to the ``meas_map`` of ``backend``. qubit_mem_slots: Mapping of measured qubit index to classical bit index. measure_name: Name of the measurement schedule. Returns: A measurement schedule corresponding to the inputs provided. Raises: PulseError: If both ``inst_map`` or ``meas_map``, and ``backend`` is None. """ schedule = Schedule( name="Default measurement schedule for qubits {}".format(qubits)) try: inst_map = inst_map or backend.defaults().instruction_schedule_map meas_map = meas_map or backend.configuration().meas_map except AttributeError: raise PulseError( 'inst_map or meas_map, and backend cannot be None simultaneously') if isinstance(meas_map, List): meas_map = format_meas_map(meas_map) measure_groups = set() for qubit in qubits: measure_groups.add(tuple(meas_map[qubit])) for measure_group_qubits in measure_groups: if qubit_mem_slots is not None: unused_mem_slots = set(measure_group_qubits) - set( qubit_mem_slots.values()) try: default_sched = inst_map.get(measure_name, measure_group_qubits) except PulseError: raise PulseError( "We could not find a default measurement schedule called '{}'. " "Please provide another name using the 'measure_name' keyword " "argument. For assistance, the instructions which are defined are: " "{}".format(measure_name, inst_map.instructions)) for time, inst in default_sched.instructions: if qubit_mem_slots and isinstance(inst, (Acquire, AcquireInstruction)): if inst.channel.index in qubit_mem_slots: mem_slot = MemorySlot(qubit_mem_slots[inst.channel.index]) else: mem_slot = MemorySlot(unused_mem_slots.pop()) schedule = schedule.insert( time, Acquire(inst.duration, inst.channel, mem_slot=mem_slot)) elif qubit_mem_slots is None and isinstance( inst, (Acquire, AcquireInstruction)): schedule = schedule.insert(time, inst) # Measurement pulses should only be added if its qubit was measured by the user elif inst.channels[0].index in qubits: schedule = schedule.insert(time, inst) return schedule
def parameterized_cx(self, theta: float, q1: int, q2: int, phase_delta: float = 0., amp_increase: float = 0.): """ Args: theta: Rotation angle of the parameterized CZ gate. q1: First qubit. q2: Second qubit. phase_delta: Multiplies the CR90_u pulses samples by np.exp(1.0j*phase_delta). amp_increase: Multiplies the amplitude of the samples by (1. + amp_increase). """ cx_sched = self._inst_map.get('cx', qubits=(q1, q2)) zx_theta = Schedule(name=self.name(theta, phase_delta)) crs = [] comp_tones = [] shift_phases = [] control = None target = None for time, inst in cx_sched.instructions: if isinstance(inst, ShiftPhase) and time == 0: shift_phases.append(ShiftPhase(-theta, inst.channel)) # Identify the CR pulses. if isinstance(inst, Play) and not isinstance(inst, ShiftPhase): if isinstance(inst.channel, ControlChannel): crs.append((time, inst)) # Identify the compensation tones. if isinstance(inst.channel, DriveChannel) and not isinstance(inst, ShiftPhase): if isinstance(inst.pulse, GaussianSquare): comp_tones.append((time, inst)) target = inst.channel.index control = q1 if target == q2 else q2 if control is None: raise QiskitError('Control qubit is None.') if target is None: raise QiskitError('Target qubit is None.') # ntb - we do not care about the initial x90p # Id the X90 gate at the start of the schedule and rescale it. #x90 = cx_sched.filter(time_ranges=[(0, crs[0][0])], channels=[DriveChannel(target)]).instructions #if len(x90) != 1: # raise QiskitError('Could not extract initial X90.') #x90 = x90[0][1] echo_x = self._inst_map.get('x', qubits=control) # Build the schedule for inst in shift_phases: zx_theta = zx_theta.insert(0, inst) # ntb - these pre-pulses we do not want #zx_theta = zx_theta.insert(0, self._inst_map.get('x', qubits=control)) #zx_theta = zx_theta.insert(0, self.rescale_amp(x90, theta)) # Stretch/compress the CR gates and compensation tones cr1 = self.rescale_cr_inst(crs[0][1], theta) cr2 = self.rescale_cr_inst(crs[1][1], theta) comp1 = self.rescale_cr_inst(comp_tones[0][1], theta) comp2 = self.rescale_cr_inst(comp_tones[1][1], theta) if theta != 0.0: zx_theta = zx_theta.insert(0, cr1) zx_theta = zx_theta.insert(0, comp1) zx_theta = zx_theta.insert(0 + comp1.duration, echo_x) time = comp1.duration + echo_x.duration zx_theta = zx_theta.insert(time, cr2) zx_theta = zx_theta.insert(time, comp2) else: zx_theta = zx_theta.insert(0, echo_x) return zx_theta