def assertScheduleEqual(self, program, target): """Assert an error when two pulse programs are not equal. .. note:: Two programs are converted into standard execution format then compared. """ self.assertEqual(target_qobj_transform(program), target_qobj_transform(target))
def test_call_initialize_with_parameter(self): """Test call instruction with parameterized subroutine with initial dict.""" init_dict = {self.param1: 0.1, self.param2: 0.5} call = instructions.Call(subroutine=self.function, value_dict=init_dict) with pulse.build() as ref_sched: pulse.play(pulse.Gaussian(160, 0.1, 40), pulse.DriveChannel(0)) pulse.play(pulse.Gaussian(160, 0.5, 40), pulse.DriveChannel(0)) pulse.play(pulse.Gaussian(160, 0.1, 40), pulse.DriveChannel(0)) self.assertEqual(target_qobj_transform(call.assigned_subroutine()), target_qobj_transform(ref_sched))
def test_disassemble_single_schedule(self): """Test disassembling a single schedule.""" d0 = pulse.DriveChannel(0) d1 = pulse.DriveChannel(1) with pulse.build(self.backend) as sched: with pulse.align_right(): pulse.play(pulse.library.Constant(10, 1.0), d0) pulse.set_phase(1.0, d0) pulse.shift_phase(3.11, d0) pulse.set_frequency(1e9, d0) pulse.shift_frequency(1e7, d0) pulse.delay(20, d0) pulse.delay(10, d1) pulse.play(pulse.library.Constant(8, 0.1), d1) pulse.measure_all() qobj = assemble(sched, backend=self.backend, shots=2000) scheds, run_config_out, _ = disassemble(qobj) run_config_out = RunConfig(**run_config_out) self.assertEqual(run_config_out.memory_slots, 2) self.assertEqual(run_config_out.shots, 2000) self.assertEqual(run_config_out.memory, False) self.assertEqual(run_config_out.meas_level, 2) self.assertEqual(run_config_out.meas_lo_freq, self.backend.defaults().meas_freq_est) self.assertEqual(run_config_out.qubit_lo_freq, self.backend.defaults().qubit_freq_est) self.assertEqual(run_config_out.rep_time, 99) self.assertEqual(len(scheds), 1) self.assertEqual(scheds[0], target_qobj_transform(sched))
def test_assign_parameters_to_call(self): """Test create schedule by calling subroutine and assign parameters to it.""" init_dict = {self.param1: 0.1, self.param2: 0.5} with pulse.build() as test_sched: pulse.call(self.function) test_sched = test_sched.assign_parameters(value_dict=init_dict) test_sched = inline_subroutines(test_sched) with pulse.build() as ref_sched: pulse.play(pulse.Gaussian(160, 0.1, 40), pulse.DriveChannel(0)) pulse.play(pulse.Gaussian(160, 0.5, 40), pulse.DriveChannel(0)) pulse.play(pulse.Gaussian(160, 0.1, 40), pulse.DriveChannel(0)) self.assertEqual(target_qobj_transform(test_sched), target_qobj_transform(ref_sched))
def test_call_subroutine_with_different_parameters(self): """Test call subroutines with different parameters in the same schedule.""" init_dict1 = {self.param1: 0.1, self.param2: 0.5} init_dict2 = {self.param1: 0.3, self.param2: 0.7} with pulse.build() as test_sched: pulse.call(self.function, value_dict=init_dict1) pulse.call(self.function, value_dict=init_dict2) with pulse.build() as ref_sched: pulse.play(pulse.Gaussian(160, 0.1, 40), pulse.DriveChannel(0)) pulse.play(pulse.Gaussian(160, 0.5, 40), pulse.DriveChannel(0)) pulse.play(pulse.Gaussian(160, 0.1, 40), pulse.DriveChannel(0)) pulse.play(pulse.Gaussian(160, 0.3, 40), pulse.DriveChannel(0)) pulse.play(pulse.Gaussian(160, 0.7, 40), pulse.DriveChannel(0)) pulse.play(pulse.Gaussian(160, 0.3, 40), pulse.DriveChannel(0)) self.assertEqual(target_qobj_transform(test_sched), target_qobj_transform(ref_sched))
def test_scheduler_with_params_not_bound(self): """Test scheduler with parameters defined but not bound""" x = Parameter("amp") qc = QuantumCircuit(2) qc.append(Gate("pulse_gate", 1, [x]), [0]) with build() as expected_schedule: play(Gaussian(duration=160, amp=x, sigma=40), DriveChannel(0)) qc.add_calibration(gate="pulse_gate", qubits=[0], schedule=expected_schedule, params=[x]) sched = schedule(qc, self.backend) self.assertEqual(sched, transforms.target_qobj_transform(expected_schedule))
def test_subroutine_not_transpiled(self): """Test called circuit is frozen as a subroutine.""" subprogram = circuit.QuantumCircuit(1) subprogram.x(0) transpiler_settings = {"optimization_level": 2} with pulse.build(self.backend, default_transpiler_settings=transpiler_settings) as schedule: pulse.call(subprogram) pulse.call(subprogram) self.assertNotEqual(len(target_qobj_transform(schedule).instructions), 0)
def test_disassemble_parametric_pulses(self): """Test disassembling multiple schedules all should have the same config.""" d0 = pulse.DriveChannel(0) with pulse.build(self.backend) as sched: with pulse.align_right(): pulse.play(pulse.library.Constant(10, 1.0), d0) pulse.play(pulse.library.Gaussian(10, 1.0, 2.0), d0) pulse.play(pulse.library.GaussianSquare(10, 1.0, 2.0, 3), d0) pulse.play(pulse.library.Drag(10, 1.0, 2.0, 0.1), d0) qobj = assemble(sched, backend=self.backend, shots=2000) scheds, _, _ = disassemble(qobj) self.assertEqual(scheds[0], target_qobj_transform(sched))
def test_disassemble_multiple_schedules(self): """Test disassembling multiple schedules, all should have the same config.""" d0 = pulse.DriveChannel(0) d1 = pulse.DriveChannel(1) with pulse.build(self.backend) as sched0: with pulse.align_right(): pulse.play(pulse.library.Constant(10, 1.0), d0) pulse.set_phase(1.0, d0) pulse.shift_phase(3.11, d0) pulse.set_frequency(1e9, d0) pulse.shift_frequency(1e7, d0) pulse.delay(20, d0) pulse.delay(10, d1) pulse.play(pulse.library.Constant(8, 0.1), d1) pulse.measure_all() with pulse.build(self.backend) as sched1: with pulse.align_right(): pulse.play(pulse.library.Constant(8, 0.1), d0) pulse.play(pulse.library.Waveform([0.0, 1.0]), d1) pulse.set_phase(1.1, d0) pulse.shift_phase(3.5, d0) pulse.set_frequency(2e9, d0) pulse.shift_frequency(3e7, d1) pulse.delay(20, d1) pulse.delay(10, d0) pulse.play(pulse.library.Constant(8, 0.4), d1) pulse.measure_all() qobj = assemble([sched0, sched1], backend=self.backend, shots=2000) scheds, run_config_out, _ = disassemble(qobj) run_config_out = RunConfig(**run_config_out) self.assertEqual(run_config_out.memory_slots, 2) self.assertEqual(run_config_out.shots, 2000) self.assertEqual(run_config_out.memory, False) self.assertEqual(len(scheds), 2) self.assertEqual(scheds[0], target_qobj_transform(sched0)) self.assertEqual(scheds[1], target_qobj_transform(sched1))
def test_call_with_common_parameter(self): """Test call subroutine with parameter that is defined multiple times.""" amp = circuit.Parameter("amp") with pulse.build() as subroutine: pulse.play(pulse.Gaussian(160, amp, 40), pulse.DriveChannel(0)) pulse.play(pulse.Gaussian(320, amp, 80), pulse.DriveChannel(0)) with pulse.build() as main_prog: pulse.call(subroutine, amp=0.1) assigned_sched = target_qobj_transform(main_prog) play_0 = assigned_sched.instructions[0][1] play_1 = assigned_sched.instructions[1][1] self.assertEqual(play_0.pulse.amp, 0.1) self.assertEqual(play_1.pulse.amp, 0.1)
def test_call_with_parameters(self): """Test call subroutine with parameters.""" amp = circuit.Parameter("amp") with pulse.build() as subroutine: pulse.play(pulse.Gaussian(160, amp, 40), pulse.DriveChannel(0)) with pulse.build() as main_prog: pulse.call(subroutine, amp=0.1) pulse.call(subroutine, amp=0.3) self.assertEqual(main_prog.is_parameterized(), False) assigned_sched = target_qobj_transform(main_prog) play_0 = assigned_sched.instructions[0][1] play_1 = assigned_sched.instructions[1][1] self.assertEqual(play_0.pulse.amp, 0.1) self.assertEqual(play_1.pulse.amp, 0.3)
def test_call_with_parameter_name_collision(self): """Test call subroutine with duplicated parameter names.""" amp1 = circuit.Parameter("amp") amp2 = circuit.Parameter("amp") sigma = circuit.Parameter("sigma") with pulse.build() as subroutine: pulse.play(pulse.Gaussian(160, amp1, sigma), pulse.DriveChannel(0)) pulse.play(pulse.Gaussian(160, amp2, sigma), pulse.DriveChannel(0)) with pulse.build() as main_prog: pulse.call(subroutine, value_dict={amp1: 0.1, amp2: 0.2}, sigma=40) assigned_sched = target_qobj_transform(main_prog) play_0 = assigned_sched.instructions[0][1] play_1 = assigned_sched.instructions[1][1] self.assertEqual(play_0.pulse.amp, 0.1) self.assertEqual(play_0.pulse.sigma, 40) self.assertEqual(play_1.pulse.amp, 0.2) self.assertEqual(play_1.pulse.sigma, 40)
def assertScheduleEqual(self, target, reference): """Check if two block are equal schedule representation.""" self.assertEqual(transforms.target_qobj_transform(target), reference)
def _assemble_experiments( schedules: List[Union["schedule.ScheduleComponent", Tuple[int, "schedule.ScheduleComponent"]]], lo_converter: converters.LoConfigConverter, run_config: RunConfig, ) -> Tuple[List[qobj.PulseQobjExperiment], Dict[str, Any]]: """Assembles a list of schedules into PulseQobjExperiments, and returns related metadata that will be assembled into the Qobj configuration. Args: schedules: Schedules to assemble. lo_converter: The configured frequency converter and validator. run_config: Configuration of the runtime environment. Returns: The list of assembled experiments, and the dictionary of related experiment config. Raises: QiskitError: when frequency settings are not compatible with the experiments. """ freq_configs = [ lo_converter(lo_dict) for lo_dict in getattr(run_config, "schedule_los", []) ] if len(schedules) > 1 and len(freq_configs) not in [0, 1, len(schedules)]: raise QiskitError( "Invalid 'schedule_los' setting specified. If specified, it should be " "either have a single entry to apply the same LOs for each schedule or " "have length equal to the number of schedules.") instruction_converter = getattr(run_config, "instruction_converter", converters.InstructionToQobjConverter) instruction_converter = instruction_converter(qobj.PulseQobjInstruction, **run_config.to_dict()) formatted_schedules = [ transforms.target_qobj_transform(sched) for sched in schedules ] compressed_schedules = transforms.compress_pulses(formatted_schedules) user_pulselib = {} experiments = [] for idx, sched in enumerate(compressed_schedules): qobj_instructions, max_memory_slot = _assemble_instructions( sched, instruction_converter, run_config, user_pulselib) metadata = sched.metadata if metadata is None: metadata = {} # TODO: add other experimental header items (see circuit assembler) qobj_experiment_header = qobj.QobjExperimentHeader( memory_slots=max_memory_slot + 1, # Memory slots are 0 indexed name=sched.name or "Experiment-%d" % idx, metadata=metadata, ) experiment = qobj.PulseQobjExperiment(header=qobj_experiment_header, instructions=qobj_instructions) if freq_configs: # This handles the cases where one frequency setting applies to all experiments and # where each experiment has a different frequency freq_idx = idx if len(freq_configs) != 1 else 0 experiment.config = freq_configs[freq_idx] experiments.append(experiment) # Frequency sweep if freq_configs and len(experiments) == 1: experiment = experiments[0] experiments = [] for freq_config in freq_configs: experiments.append( qobj.PulseQobjExperiment( header=experiment.header, instructions=experiment.instructions, config=freq_config, )) # Top level Qobj configuration experiment_config = { "pulse_library": [ qobj.PulseLibraryItem(name=name, samples=samples) for name, samples in user_pulselib.items() ], "memory_slots": max(exp.header.memory_slots for exp in experiments), } return experiments, experiment_config
def _assemble_instructions( sched: Union[pulse.Schedule, pulse.ScheduleBlock], instruction_converter: converters.InstructionToQobjConverter, run_config: RunConfig, user_pulselib: Dict[str, List[complex]], ) -> Tuple[List[qobj.PulseQobjInstruction], int]: """Assembles the instructions in a schedule into a list of PulseQobjInstructions and returns related metadata that will be assembled into the Qobj configuration. Lookup table for pulses defined in all experiments are registered in ``user_pulselib``. This object should be mutable python dictionary so that items are properly updated after each instruction assemble. The dictionary is not returned to avoid redundancy. Args: sched: Schedule to assemble. instruction_converter: A converter instance which can convert PulseInstructions to PulseQobjInstructions. run_config: Configuration of the runtime environment. user_pulselib: User pulse library from previous schedule. Returns: A list of converted instructions, the user pulse library dictionary (from pulse name to pulse samples), and the maximum number of readout memory slots used by this Schedule. """ sched = transforms.target_qobj_transform(sched) max_memory_slot = 0 qobj_instructions = [] acquire_instruction_map = defaultdict(list) for time, instruction in sched.instructions: if isinstance(instruction, instructions.Play) and isinstance( instruction.pulse, library.ParametricPulse): pulse_shape = ParametricPulseShapes(type(instruction.pulse)).name if pulse_shape not in run_config.parametric_pulses: instruction = instructions.Play( instruction.pulse.get_waveform(), instruction.channel, name=instruction.name) if isinstance(instruction, instructions.Play) and isinstance( instruction.pulse, library.Waveform): name = hashlib.sha256(instruction.pulse.samples).hexdigest() instruction = instructions.Play( library.Waveform(name=name, samples=instruction.pulse.samples), channel=instruction.channel, name=name, ) user_pulselib[name] = instruction.pulse.samples # ignore explicit delay instrs on acq channels as they are invalid on IBMQ backends; # timing of other instrs will still be shifted appropriately if isinstance(instruction, instructions.Delay) and isinstance( instruction.channel, channels.AcquireChannel): continue if isinstance(instruction, instructions.Acquire): if instruction.mem_slot: max_memory_slot = max(max_memory_slot, instruction.mem_slot.index) # Acquires have a single AcquireChannel per inst, but we have to bundle them # together into the Qobj as one instruction with many channels acquire_instruction_map[(time, instruction.duration)].append(instruction) continue qobj_instructions.append(instruction_converter(time, instruction)) if acquire_instruction_map: if hasattr(run_config, "meas_map"): _validate_meas_map(acquire_instruction_map, run_config.meas_map) for (time, _), instrs in acquire_instruction_map.items(): qobj_instructions.append( instruction_converter.convert_bundled_acquires(time, instrs), ) return qobj_instructions, max_memory_slot
def _schedule_loader(self, program: Union[pulse.Schedule, pulse.ScheduleBlock]): """Load Schedule instance. This function is sub-routine of py:method:`load_program`. Args: program: `Schedule` to draw. """ program = target_qobj_transform(program, remove_directives=False) # initialize scale values self.chan_scales = {} for chan in program.channels: if isinstance(chan, pulse.channels.DriveChannel): self.chan_scales[chan] = self.formatter["channel_scaling.drive"] elif isinstance(chan, pulse.channels.MeasureChannel): self.chan_scales[chan] = self.formatter["channel_scaling.measure"] elif isinstance(chan, pulse.channels.ControlChannel): self.chan_scales[chan] = self.formatter["channel_scaling.control"] elif isinstance(chan, pulse.channels.AcquireChannel): self.chan_scales[chan] = self.formatter["channel_scaling.acquire"] else: self.chan_scales[chan] = 1.0 # create charts mapper = self.layout["chart_channel_map"] for name, chans in mapper( channels=program.channels, formatter=self.formatter, device=self.device ): chart = Chart(parent=self, name=name) # add standard pulse instructions for chan in chans: chart.load_program(program=program, chan=chan) # add barriers barrier_sched = program.filter( instruction_types=[pulse.instructions.RelativeBarrier], channels=chans ) for t0, _ in barrier_sched.instructions: inst_data = types.BarrierInstruction(t0, self.device.dt, chans) for gen in self.generator["barrier"]: obj_generator = partial(gen, formatter=self.formatter, device=self.device) for data in obj_generator(inst_data): chart.add_data(data) # add chart axis chart_axis = types.ChartAxis(name=chart.name, channels=chart.channels) for gen in self.generator["chart"]: obj_generator = partial(gen, formatter=self.formatter, device=self.device) for data in obj_generator(chart_axis): chart.add_data(data) self.charts.append(chart) # add snapshot data to global snapshot_sched = program.filter(instruction_types=[pulse.instructions.Snapshot]) for t0, inst in snapshot_sched.instructions: inst_data = types.SnapshotInstruction(t0, self.device.dt, inst.label, inst.channels) for gen in self.generator["snapshot"]: obj_generator = partial(gen, formatter=self.formatter, device=self.device) for data in obj_generator(inst_data): self.global_charts.add_data(data) # calculate axis break self.time_breaks = self._calculate_axis_break(program)