def _1Q_frame_change_schedule(self, phi, fc_phi, total_samples, dur_drive1, dur_drive2): """Creates schedule for frame change test. Does a pulse w/ phase phi of duration dur_drive1, then frame change of phase fc_phi, then another pulse of phase phi of duration dur_drive2. The different durations for the pulses allow manipulation of rotation angles on Bloch sphere Args: phi (float): drive phase (phi in Hamiltonian) fc_phi (float): phase for frame change total_samples (int): length of pulses dur_drive1 (int): duration of first pulse dur_drive2 (int): duration of second pulse Returns: schedule (pulse schedule): schedule for frame change test """ phase = np.exp(1j * phi) drive_pulse_1 = SamplePulse(phase * np.ones(dur_drive1), name='drive_pulse_1') drive_pulse_2 = SamplePulse(phase * np.ones(dur_drive2), name='drive_pulse_2') # frame change fc_pulse = FrameChange(phase=fc_phi, name='fc') # set up acquire command acq_cmd = pulse.Acquire(duration=total_samples) # add commands to schedule schedule = pulse.Schedule(name='fc_schedule') schedule |= drive_pulse_1(DriveChannel(0)) schedule += fc_pulse(DriveChannel(0)) schedule += drive_pulse_2(DriveChannel(0)) schedule |= acq_cmd(AcquireChannel(0), MemorySlot(0)) << schedule.duration return schedule
def _simple_1Q_schedule(self, phi, total_samples, shape="square", gauss_sigma=0): """Creates schedule for single pulse test Args: phi (float): drive phase (phi in Hamiltonian) total_samples (int): length of pulses shape (str): shape of the pulse; defaults to square pulse gauss_sigma (float): std dev for gaussian pulse if shape=="gaussian" Returns: schedule (pulse schedule): schedule for this test """ # set up pulse command phase = np.exp(1j * phi) drive_pulse = None if shape == "square": const_pulse = np.ones(total_samples) drive_pulse = SamplePulse(phase * const_pulse, name='drive_pulse') if shape == "gaussian": times = 1.0 * np.arange(total_samples) gaussian = np.exp(-times**2 / 2 / gauss_sigma**2) drive_pulse = SamplePulse(phase * gaussian, name='drive_pulse') # set up acquire command acq_cmd = pulse.Acquire(duration=total_samples) # add commands into a schedule for first qubit schedule = pulse.Schedule(name='drive_pulse') schedule |= drive_pulse(DriveChannel(0)) schedule |= acq_cmd(AcquireChannel(0), MemorySlot(0)) << schedule.duration return schedule
def setUp(self): self.linear = SamplePulse(np.arange(0, 0.01), name='linear') self.pulse_library = [PulseLibraryItem(name=self.linear.name, samples=self.linear.samples.tolist())] self.converter = QobjToInstructionConverter(self.pulse_library, buffer=0) self.num_qubits = 2
def _schedule_2Q_interaction(self, total_samples): """Creates schedule for testing two qubit interaction. Specifically, do a pi pulse on qub 0 so it starts in the `1` state (drive channel) and then apply constant pulses to each qubit (on control channel 1). This will allow us to test a swap gate. Args: total_samples (int): length of pulses Returns: schedule (pulse schedule): schedule for 2q experiment """ # set up const pulse const_pulse = SamplePulse(np.ones(total_samples), name='const_pulse') # set u channel uchannel = 1 # gives omega1-omega0 (we will set equal, so don't need negation) # add commands to schedule schedule = pulse.Schedule(name='2q_schedule') schedule |= const_pulse(DriveChannel(0)) # pi pulse drive schedule += const_pulse(ControlChannel(uchannel)) << schedule.duration # u chan pulse acq_cmd = pulse.Acquire(duration=total_samples) acq_sched = acq_cmd(AcquireChannel(0), MemorySlot(0)) acq_sched += acq_cmd(AcquireChannel(1), MemorySlot(1)) schedule |= acq_sched << schedule.duration return schedule
def test_drive_instruction(self): """Test converted qobj from Play.""" converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) instruction = Play(SamplePulse(np.arange(0, 0.01), name='linear'), DriveChannel(0)) valid_qobj = PulseQobjInstruction(name='linear', ch='d0', t0=0) self.assertEqual(converter(0, instruction), valid_qobj)
def setUp(self): self.linear = SamplePulse(np.arange(0, 0.01), name='linear') self.pulse_library = [PulseLibraryItem(name=self.linear.name, samples=self.linear.samples.tolist())] self.converter = QobjToInstructionConverter(self.pulse_library, buffer=0) self.device = PulseChannelSpec(n_qubits=2, n_control=0, n_registers=2)
def test_drive_instruction(self): """Test converted qobj from PulseInstruction.""" converter = PulseQobjConverter(PulseQobjInstruction, meas_level=2) command = SamplePulse(np.arange(0, 0.01), name='linear') instruction = command(self.device.q[0].drive) valid_qobj = PulseQobjInstruction(name='linear', ch='d0', t0=0) self.assertEqual(converter(0, instruction), valid_qobj)
def test_deprecated_drive_instruction(self): """Test converted qobj from PulseInstruction.""" converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) command = SamplePulse(np.arange(0, 0.01), name='linear') with self.assertWarns(DeprecationWarning): instruction = command(DriveChannel(0)) valid_qobj = PulseQobjInstruction(name='linear', ch='d0', t0=0) self.assertEqual(converter(0, instruction), valid_qobj)
def setUp(self): self.linear = SamplePulse(np.arange(0, 0.01), name='linear') self.pulse_library = [ PulseLibraryItem(name=self.linear.name, samples=self.linear.samples.tolist()) ] self.converter = QobjToInstructionConverter(self.pulse_library, buffer=0) self.device = DeviceSpecification(qubits=[ Qubit(0, DriveChannel(0), MeasureChannel(0), AcquireChannel(0)) ], registers=[RegisterSlot(0)], mem_slots=[MemorySlot(0)])
def assemble_schedules(schedules, qobj_id, qobj_header, run_config): """Assembles a list of schedules into a qobj which can be run on the backend. Args: schedules (list[Schedule]): schedules to assemble qobj_id (int): identifier for the generated qobj qobj_header (QobjHeader): header to pass to the results run_config (RunConfig): configuration of the runtime environment Returns: PulseQobj: the Qobj to be run on the backends Raises: QiskitError: when invalid schedules or configs are provided """ if hasattr(run_config, 'instruction_converter'): instruction_converter = run_config.instruction_converter else: instruction_converter = InstructionToQobjConverter qobj_config = run_config.to_dict() qubit_lo_freq = qobj_config.get('qubit_lo_freq', None) if qubit_lo_freq is None: raise QiskitError('qubit_lo_freq must be supplied.') meas_lo_freq = qobj_config.get('meas_lo_freq', None) if meas_lo_freq is None: raise QiskitError('meas_lo_freq must be supplied.') qubit_lo_range = qobj_config.pop('qubit_lo_range', None) meas_lo_range = qobj_config.pop('meas_lo_range', None) meas_map = qobj_config.pop('meas_map', None) # convert enums to serialized values meas_return = qobj_config.get('meas_return', 'avg') if isinstance(meas_return, MeasReturnType): qobj_config['meas_return'] = meas_return.value meas_level = qobj_config.get('meas_return', 2) if isinstance(meas_level, MeasLevel): qobj_config['meas_level'] = meas_level.value instruction_converter = instruction_converter(PulseQobjInstruction, **qobj_config) lo_converter = LoConfigConverter(PulseQobjExperimentConfig, qubit_lo_range=qubit_lo_range, meas_lo_range=meas_lo_range, **qobj_config) memory_slot_size = 0 # Pack everything into the Qobj qobj_schedules = [] user_pulselib = {} for idx, schedule in enumerate(schedules): # instructions max_memory_slot = 0 qobj_instructions = [] # Instructions are returned as tuple of shifted time and instruction for shift, instruction in schedule.instructions: # TODO: support conditional gate if isinstance(instruction, DelayInstruction): # delay instructions are ignored as timing is explicit within qobj continue elif isinstance(instruction, PulseInstruction): name = instruction.command.name if name in user_pulselib and instruction.command != user_pulselib[name]: name = "{0}-{1:x}".format(name, hash(instruction.command.samples.tostring())) instruction = PulseInstruction( command=SamplePulse(name=name, samples=instruction.command.samples), name=instruction.name, channel=instruction.channels[0]) # add samples to pulse library user_pulselib[name] = instruction.command elif isinstance(instruction, AcquireInstruction): max_memory_slot = max(max_memory_slot, *[slot.index for slot in instruction.mem_slots]) if meas_map: # verify all acquires satisfy meas_map _validate_meas_map(instruction, meas_map) converted_instruction = instruction_converter(shift, instruction) qobj_instructions.append(converted_instruction) # memory slot size is memory slot index + 1 because index starts from zero exp_memory_slot_size = max_memory_slot + 1 memory_slot_size = max(memory_slot_size, exp_memory_slot_size) # experiment header # TODO: add other experimental header items (see circuit assembler) qobj_experiment_header = QobjExperimentHeader( memory_slots=exp_memory_slot_size, name=schedule.name or 'Experiment-%d' % idx ) qobj_schedules.append({ 'header': qobj_experiment_header, 'instructions': qobj_instructions }) # set number of memoryslots qobj_config['memory_slots'] = memory_slot_size # setup pulse_library qobj_config['pulse_library'] = [PulseLibraryItem(name=pulse.name, samples=pulse.samples) for pulse in user_pulselib.values()] # create qobj experiment field experiments = [] schedule_los = qobj_config.pop('schedule_los', []) if len(schedule_los) == 1: lo_dict = schedule_los[0] # update global config q_los = lo_converter.get_qubit_los(lo_dict) if q_los: qobj_config['qubit_lo_freq'] = q_los m_los = lo_converter.get_meas_los(lo_dict) if m_los: qobj_config['meas_lo_freq'] = m_los if schedule_los: # multiple frequency setups if len(qobj_schedules) == 1: # frequency sweep for lo_dict in schedule_los: experiments.append(PulseQobjExperiment( instructions=qobj_schedules[0]['instructions'], header=qobj_schedules[0]['header'], config=lo_converter(lo_dict) )) elif len(qobj_schedules) == len(schedule_los): # n:n setup for lo_dict, schedule in zip(schedule_los, qobj_schedules): experiments.append(PulseQobjExperiment( instructions=schedule['instructions'], header=schedule['header'], config=lo_converter(lo_dict) )) else: raise QiskitError('Invalid LO setting is specified. ' 'The LO should be configured for each schedule, or ' 'single setup for all schedules (unique), or ' 'multiple setups for a single schedule (frequency sweep),' 'or no LO configured at all.') else: # unique frequency setup for schedule in qobj_schedules: experiments.append(PulseQobjExperiment( instructions=schedule['instructions'], header=schedule['header'], )) qobj_config = PulseQobjConfig(**qobj_config) return PulseQobj(qobj_id=qobj_id, config=qobj_config, experiments=experiments, header=qobj_header)
def _assemble_instructions( schedule: Schedule, instruction_converter: InstructionToQobjConverter, run_config: RunConfig, user_pulselib: Dict[str, Command]) -> Tuple[List[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: schedule: 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 command), and the maximum number of readout memory slots used by this Schedule. """ max_memory_slot = 0 qobj_instructions = [] acquire_instruction_map = defaultdict(list) for time, instruction in schedule.instructions: if isinstance(instruction, ParametricInstruction): pulse_shape = ParametricPulseShapes(type(instruction.command)).name if pulse_shape not in run_config.parametric_pulses: # Convert to SamplePulse if the backend does not support it instruction = PulseInstruction( instruction.command.get_sample_pulse(), instruction.channels[0], name=instruction.name) if isinstance(instruction, PulseInstruction): name = instruction.command.name if name in user_pulselib and instruction.command != user_pulselib[ name]: name = "{0}-{1:x}".format( name, hash(instruction.command.samples.tostring())) instruction = PulseInstruction(command=SamplePulse( name=name, samples=instruction.command.samples), name=instruction.name, channel=instruction.channels[0]) # add samples to pulse library user_pulselib[name] = instruction.command if isinstance(instruction, AcquireInstruction): max_memory_slot = max( max_memory_slot, *[slot.index for slot in instruction.mem_slots]) # 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.command)].append(instruction) continue if isinstance(instruction, (DelayInstruction, Delay)): # delay instructions are ignored as timing is explicit within qobj 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, _), instructions in acquire_instruction_map.items(): qubits, mem_slots, reg_slots = _bundle_channel_indices( instructions) qobj_instructions.append( instruction_converter.convert_single_acquires( time, instructions[0], qubits=qubits, memory_slot=mem_slots, register_slot=reg_slots)) return qobj_instructions, max_memory_slot