def _compute_gradients(self, circuit: QuantumCircuit) -> List[Tuple[complex, complex]]: kwargs = {'statevector_mode': self.quantum_instance.is_statevector} logger.info('Constructing evaluation circuits...') total_evaluation_circuits = list(parallel_map( _circ_eval, self.commutators, task_kwargs={**kwargs, 'wave_function': circuit}, num_processes=aqua_globals.num_processes )) total_evaluation_circuits = [item for sublist in total_evaluation_circuits for item in sublist] logger.info('Removing duplicate circuits') final_circs = [] for circ in total_evaluation_circuits: if not fast_circuit_inclusion(circ, final_circs): final_circs.append(circ) logger.info('Finished removing duplicate circuits') logger.debug('Executing {} circuits for gradient evaluation...'.format(len(final_circs))) result = self.quantum_instance.execute(final_circs) logger.debug('Computing {} gradients...'.format(len(self.commutators))) grads = list(parallel_map( _compute_grad, self.commutators, task_kwargs={**kwargs, 'result': result}, num_processes=aqua_globals.num_processes )) self.adapt_step_history['Total num evals'] += len(final_circs) logger.debug('Computed gradients: {}'.format(grads)) return [abs(tup[0]) for tup in grads]
def to_weighted_pauli_operator(operator): """ Converting a given operator to `WeightedPauliOperator` Args: operator (WeightedPauliOperator | TPBGroupedWeightedPauliOperator | MatrixOperator | Operator): one of supported operator type Returns: WeightedPauliOperator: the converted weighted pauli operator """ if operator.__class__ == WeightedPauliOperator: return operator elif operator.__class__ == TPBGroupedWeightedPauliOperator: # destroy the grouping but keep z2 symmetries info return WeightedPauliOperator(paulis=operator.paulis, z2_symmetries=operator.z2_symmetries, name=operator.name) elif operator.__class__ == MatrixOperator: if operator.is_empty(): return WeightedPauliOperator(paulis=[]) logger.warning( "Converting time from a MatrixOperator to a Pauli-type Operator grows exponentially. " "If you are converting a system with large number of qubits, it will take time. " "You can turn on DEBUG logging to check the progress.") num_qubits = operator.num_qubits coeff = 2**(-num_qubits) paulis = [] possible_basis = 'IXYZ' if operator.dia_matrix is not None: possible_basis = 'IZ' if logger.isEnabledFor(logging.DEBUG): logger.debug( "Converting a MatrixOperator to a Pauli-type Operator:") TextProgressBar(sys.stderr) results = parallel_map(_conversion, [ basis for basis in itertools.product(possible_basis, repeat=num_qubits) ], task_kwargs={"matrix": operator._matrix}, num_processes=aqua_globals.num_processes) for trace_value, pauli in results: weight = trace_value * coeff if weight != 0.0 and np.abs(weight) > operator.atol: paulis.append([weight, pauli]) return WeightedPauliOperator(paulis, z2_symmetries=operator.z2_symmetries, name=operator.name) elif operator.__class__ == Operator: warnings.warn( "The `Operator` class is deprecated. Please use `WeightedPauliOperator` or " "`TPBGroupedWeightedPauliOperator` or `MatrixOperator` instead", DeprecationWarning) return operator.to_weighted_pauli_operator() else: raise AquaError( "Unsupported type to convert to WeightedPauliOperator: {}".format( operator.__class__))
def commutators(self) -> List[BaseOperator]: if self._coms is not None: return self._coms logger.info('Computing commutators...') self._coms = list(parallel_map( _commutator, self.operator_pool.pool, task_kwargs={'hamiltonian': self.hamiltonian, 'gradient_tolerance': self.grad_tol}, num_processes=aqua_globals.num_processes )) # type: List[BaseOperator] logger.info('Computed {} commutators'.format(len(self._coms))) if all(isinstance(op, WeightedPauliOperator) for op in self._coms): new_coms = [] new_pool = [] for com, op in zip(self._coms, self.operator_pool.pool): if len(com.paulis) > 0: new_coms.append(com) new_pool.append(op) self._coms = new_coms self.operator_pool._pool = new_pool logger.info('Dropped commuting terms, new pool has size {}'.format(len(self._coms))) else: logger.info( 'Dropping commuting terms currently only supported for WeightedPauliOperator class') if len(self._coms) == 0: raise ValueError('List of commutators is empty.') return self._coms
def to_weighted_pauli_operator( operator: Union[WeightedPauliOperator, TPBGroupedWeightedPauliOperator, MatrixOperator]) \ -> WeightedPauliOperator: """ Converting a given operator to `WeightedPauliOperator` Args: operator: one of supported operator type Returns: The converted weighted pauli operator Raises: OpflowError: Unsupported type to convert Warnings: Converting time from a MatrixOperator to a Pauli-type Operator grows exponentially. If you are converting a system with large number of qubits, it will take time. You can turn on DEBUG logging to check the progress. """ if operator.__class__ == WeightedPauliOperator: return cast(WeightedPauliOperator, operator) elif operator.__class__ == TPBGroupedWeightedPauliOperator: # destroy the grouping but keep z2 symmetries info op_tpb = cast(TPBGroupedWeightedPauliOperator, operator) return WeightedPauliOperator(paulis=op_tpb.paulis, z2_symmetries=op_tpb.z2_symmetries, name=op_tpb.name) elif operator.__class__ == MatrixOperator: op_m = cast(MatrixOperator, operator) if op_m.is_empty(): return WeightedPauliOperator(paulis=[]) if op_m.num_qubits > 10: logger.warning("Converting time from a MatrixOperator to a Pauli-type Operator grows " "exponentially. If you are converting a system with large number of " "qubits, it will take time. And now you are converting a %s-qubit " "Hamiltonian. You can turn on DEBUG logging to check the progress." "", op_m.num_qubits) num_qubits = op_m.num_qubits coeff = 2 ** (-num_qubits) paulis = [] possible_basis = 'IXYZ' if op_m.dia_matrix is not None: possible_basis = 'IZ' if logger.isEnabledFor(logging.DEBUG): logger.debug("Converting a MatrixOperator to a Pauli-type Operator:") results = parallel_map(_conversion, list(itertools.product(possible_basis, repeat=num_qubits)), task_kwargs={"matrix": op_m._matrix}, num_processes=aqua_globals.num_processes) for trace_value, pauli in results: weight = trace_value * coeff if weight != 0.0 and np.abs(weight) > op_m.atol: paulis.append([weight, pauli]) return WeightedPauliOperator(paulis, z2_symmetries=operator.z2_symmetries, name=operator.name) else: raise OpflowError("Unsupported type to convert to WeightedPauliOperator: " "{}".format(operator.__class__))
def assemble_circuits(circuits, run_config, qobj_id, qobj_header): """Assembles a list of circuits into a qobj that can be run on the backend. Args: circuits (list[QuantumCircuit]): circuit(s) 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: QasmQobj: the qobj to be run on the backends """ qobj_config = QasmQobjConfig() if run_config: qobj_config = QasmQobjConfig(**run_config.to_dict()) qubit_sizes = [] memory_slot_sizes = [] for circ in circuits: num_qubits = 0 memory_slots = 0 for qreg in circ.qregs: num_qubits += qreg.size for creg in circ.cregs: memory_slots += creg.size qubit_sizes.append(num_qubits) memory_slot_sizes.append(memory_slots) qobj_config.memory_slots = max(memory_slot_sizes) qobj_config.n_qubits = max(qubit_sizes) experiments = parallel_map(_assemble_circuit, circuits) return QasmQobj(qobj_id=qobj_id, config=qobj_config, experiments=experiments, header=qobj_header)
def transpile(circuits, backend=None, basis_gates=None, coupling_map=None, initial_layout=None, seed_mapper=None, pass_manager=None): """transpile one or more circuits. Args: circuits (QuantumCircuit or list[QuantumCircuit]): circuits to compile backend (BaseBackend): a backend to compile for basis_gates (list[str]): list of basis gate names supported by the target. Default: ['u1','u2','u3','cx','id'] coupling_map (list): coupling map (perhaps custom) to target in mapping initial_layout (list): initial layout of qubits in mapping seed_mapper (int): random seed for the swap_mapper pass_manager (PassManager): a pass_manager for the transpiler stages Returns: QuantumCircuit or list[QuantumCircuit]: transpiled circuit(s). Raises: TranspilerError: if args are not complete for the transpiler to function """ return_form_is_single = False if isinstance(circuits, QuantumCircuit): circuits = [circuits] return_form_is_single = True # Check for valid parameters for the experiments. basis_gates = basis_gates or backend.configuration().basis_gates if coupling_map: coupling_map = coupling_map elif backend: # This needs to be removed once Aer 0.2 is out coupling_map = getattr(backend.configuration(), 'coupling_map', None) else: coupling_map = None if not basis_gates: raise TranspilerError('no basis_gates or backend to compile to') # Convert integer list format to Layout if isinstance(initial_layout, list) and \ all(isinstance(elem, int) for elem in initial_layout): if isinstance(circuits, list): circ = circuits[0] else: circ = circuits initial_layout = Layout.generate_from_intlist(initial_layout, *circ.qregs) if initial_layout is not None and not isinstance(initial_layout, Layout): initial_layout = Layout(initial_layout) circuits = parallel_map(_transpilation, circuits, task_kwargs={'basis_gates': basis_gates, 'coupling_map': coupling_map, 'initial_layout': initial_layout, 'seed_mapper': seed_mapper, 'pass_manager': pass_manager}) if return_form_is_single: return circuits[0] return circuits
def run_monte_carlo_experiments(op_system): """ Runs monte carlo experiments for a given op_system Parameters: op_system (PulseSimDescription): container for simulation information Returns: tuple: two lists with experiment results Raises: Exception: if initial state is of incorrect format """ if not op_system.initial_state.isket: raise Exception("Initial state must be a state vector.") # set num_cpus to the value given in settings if none in Options if not op_system.ode_options.num_cpus: op_system.ode_options.num_cpus = CPU_COUNT # setup seeds array seed = op_system.global_data.get( 'seed', np.random.randint(np.iinfo(np.int32).max - 1)) prng = np.random.RandomState(seed) for exp in op_system.experiments: exp['seed'] = prng.randint(np.iinfo(np.int32).max - 1) map_kwargs = {'num_processes': op_system.ode_options.num_cpus} exp_results = [] exp_times = [] for exp in op_system.experiments: start = time.time() rng = np.random.RandomState(exp['seed']) seeds = rng.randint(np.iinfo(np.int32).max - 1, size=op_system.global_data['shots']) exp_res = parallel_map(monte_carlo_evolution, seeds, task_args=( exp, op_system, ), **map_kwargs) # exp_results is a list for each shot # so transform back to an array of shots exp_res2 = [] for exp_shot in exp_res: exp_res2.append(exp_shot[0].tolist()) end = time.time() exp_times.append(end - start) exp_results.append(np.array(exp_res2)) return exp_results, exp_times
def _run_several_circuits(self, circuits): """Run all the passes on each of the circuits in the circuits list Args: circuits (list[QuantumCircuit]): circuit to transform via all the registered passes Returns: list[QuantumCircuit]: Transformed circuits. """ return parallel_map(PassManager._in_parallel, circuits, task_kwargs={'pm_dill': dill.dumps(self)})
def _run_several_circuits(self, circuits, output_name=None, callback=None): """Run all the passes on each of the circuits in the circuits list Returns: list[QuantumCircuit]: Transformed circuits. """ # TODO support for List(output_name) and List(callback) del output_name del callback return parallel_map(PassManager._in_parallel, circuits, task_kwargs={'pm_dill': dill.dumps(self)})
def run_unitary_experiments(pulse_sim_desc, pulse_de_model, solver_options=None): """ Runs unitary experiments for a given op_system Parameters: pulse_sim_desc (PulseSimDescription): description of pulse simulation pulse_de_model (PulseInternalDEModel): description of de model solver_options (PulseSimOptions): options Returns: tuple: two lists with experiment results Raises: Exception: if initial state is of incorrect format """ solver_options = PulseSimOptions( ) if solver_options is None else solver_options if not pulse_sim_desc.initial_state.isket: raise Exception("Initial state must be a state vector.") y0 = pulse_sim_desc.initial_state.full().ravel() # set num_cpus to the value given in settings if none in Options if not solver_options.num_cpus: solver_options.num_cpus = CPU_COUNT # setup seeds array seed = pulse_sim_desc.seed or np.random.randint(np.iinfo(np.int32).max - 1) prng = np.random.RandomState(seed) for exp in pulse_sim_desc.experiments: exp['seed'] = prng.randint(np.iinfo(np.int32).max - 1) map_kwargs = {'num_processes': solver_options.num_cpus} # run simulation on each experiment in parallel start = time.time() exp_results = parallel_map(_full_simulation, pulse_sim_desc.experiments, task_args=( y0, pulse_sim_desc, pulse_de_model, solver_options, ), **map_kwargs) end = time.time() exp_times = (np.ones(len(pulse_sim_desc.experiments)) * (end - start) / len(pulse_sim_desc.experiments)) return exp_results, exp_times
def assemble_circuits(circuits: List[QuantumCircuit], run_config: RunConfig, qobj_id: int, qobj_header: QobjHeader) -> QasmQobj: """Assembles a list of circuits into a qobj that can be run on the backend. Args: circuits: circuit(s) to assemble run_config: configuration of the runtime environment qobj_id: identifier for the generated qobj qobj_header: header to pass to the results Returns: The qobj to be run on the backends """ qobj_config = QasmQobjConfig() if run_config: qobj_config = QasmQobjConfig(**run_config.to_dict()) qubit_sizes = [] memory_slot_sizes = [] for circ in circuits: num_qubits = 0 memory_slots = 0 for qreg in circ.qregs: num_qubits += qreg.size for creg in circ.cregs: memory_slots += creg.size qubit_sizes.append(num_qubits) memory_slot_sizes.append(memory_slots) qobj_config.memory_slots = max(memory_slot_sizes) qobj_config.n_qubits = max(qubit_sizes) experiments_and_pulse_libs = parallel_map(_assemble_circuit, circuits, [run_config]) experiments = [] pulse_library = {} for exp, lib in experiments_and_pulse_libs: experiments.append(exp) if lib: pulse_library.update(lib) if pulse_library: qobj_config.pulse_library = [ PulseLibraryItem(name=name, samples=samples) for name, samples in pulse_library.items() ] experiments, calibrations = _extract_common_calibrations(experiments) if calibrations and calibrations.gates: qobj_config.calibrations = calibrations return QasmQobj(qobj_id=qobj_id, config=qobj_config, experiments=experiments, header=qobj_header)
def transpile(circuits, backend=None, basis_gates=None, coupling_map=None, initial_layout=None, seed_mapper=None, pass_manager=None): """transpile one or more circuits. Args: circuits (QuantumCircuit or list[QuantumCircuit]): circuits to compile backend (BaseBackend): a backend to compile for basis_gates (list[str]): list of basis gate names supported by the target. Default: ['u1','u2','u3','cx','id'] coupling_map (list): coupling map (perhaps custom) to target in mapping initial_layout (list): initial layout of qubits in mapping seed_mapper (int): random seed for the swap_mapper pass_manager (PassManager): a pass_manager for the transpiler stages Returns: QuantumCircuit or list[QuantumCircuit]: transpiled circuit(s). Raises: TranspilerError: if args are not complete for the transpiler to function """ return_form_is_single = False if isinstance(circuits, QuantumCircuit): circuits = [circuits] return_form_is_single = True # Check for valid parameters for the experiments. basis_gates = basis_gates or backend.configuration().basis_gates coupling_map = coupling_map or getattr(backend.configuration(), 'coupling_map', None) if not basis_gates: raise TranspilerError('no basis_gates or backend to compile to') circuits = parallel_map(_transpilation, circuits, task_kwargs={ 'basis_gates': basis_gates, 'coupling_map': coupling_map, 'initial_layout': initial_layout, 'seed_mapper': seed_mapper, 'pass_manager': pass_manager }) if return_form_is_single: return circuits[0] return circuits
def _run_several_circuits( self, circuits: List[QuantumCircuit], output_name: str = None, callback: Callable = None ) -> List[QuantumCircuit]: """Run all the passes on the specified ``circuits``. Args: circuits: Circuits to transform via all the registered passes. output_name: The output circuit name. If ``None``, it will be set to the same as the input circuit name. callback: A callback function that will be called after each pass execution. Returns: The transformed circuits. """ # TODO support for List(output_name) and List(callback) del output_name del callback return parallel_map( PassManager._in_parallel, circuits, task_kwargs={"pm_dill": dill.dumps(self)} )
def run_unitary_experiments(op_system): """ Runs unitary experiments for a given op_system Parameters: op_system (PulseSimDescription): container for simulation information Returns: tuple: two lists with experiment results Raises: Exception: if initial state is of incorrect format """ if not op_system.initial_state.isket: raise Exception("Initial state must be a state vector.") # set num_cpus to the value given in settings if none in Options if not op_system.ode_options.num_cpus: op_system.ode_options.num_cpus = CPU_COUNT # setup seeds array seed = op_system.global_data.get('seed', np.random.randint(np.iinfo(np.int32).max - 1)) prng = np.random.RandomState(seed) for exp in op_system.experiments: exp['seed'] = prng.randint(np.iinfo(np.int32).max - 1) map_kwargs = {'num_processes': op_system.ode_options.num_cpus} # run simulation on each experiment in parallel start = time.time() exp_results = parallel_map(_full_simulation, op_system.experiments, task_args=(op_system, ), **map_kwargs ) end = time.time() exp_times = (np.ones(len(op_system.experiments)) * (end - start) / len(op_system.experiments)) return exp_results, exp_times
def assemble_circuits(circuits: List[QuantumCircuit], run_config: RunConfig, qobj_id: int, qobj_header: QobjHeader) -> QasmQobj: """Assembles a list of circuits into a qobj that can be run on the backend. Args: circuits: circuit(s) to assemble run_config: configuration of the runtime environment qobj_id: identifier for the generated qobj qobj_header: header to pass to the results Returns: The qobj to be run on the backends """ # assemble the circuit experiments experiments_and_pulse_libs = parallel_map(_assemble_circuit, circuits, [run_config]) experiments = [] pulse_library = {} for exp, lib in experiments_and_pulse_libs: experiments.append(exp) if lib: pulse_library.update(lib) # extract common calibrations experiments, calibrations = _extract_common_calibrations(experiments) # configure LO freqs per circuit lo_converter = converters.LoConfigConverter(QasmQobjExperimentConfig, **run_config.to_dict()) experiments = _configure_experiment_los(experiments, lo_converter, run_config) qobj_config = QasmQobjConfig() if run_config: qobj_config_dict = run_config.to_dict() # remove LO ranges, not needed in qobj qobj_config_dict.pop("qubit_lo_range", None) qobj_config_dict.pop("meas_lo_range", None) # convert LO frequencies to GHz, if they exist if "qubit_lo_freq" in qobj_config_dict: qobj_config_dict["qubit_lo_freq"] = [ freq / 1e9 for freq in qobj_config_dict["qubit_lo_freq"] ] if "meas_lo_freq" in qobj_config_dict: qobj_config_dict["meas_lo_freq"] = [ freq / 1e9 for freq in qobj_config_dict["meas_lo_freq"] ] # override default los if single ``schedule_los`` entry set schedule_los = qobj_config_dict.pop("schedule_los", []) if len(schedule_los) == 1: lo_dict = schedule_los[0] q_los = lo_converter.get_qubit_los(lo_dict) # Hz -> GHz if q_los: qobj_config_dict["qubit_lo_freq"] = [ freq / 1e9 for freq in q_los ] m_los = lo_converter.get_meas_los(lo_dict) if m_los: qobj_config_dict["meas_lo_freq"] = [ freq / 1e9 for freq in m_los ] qobj_config = QasmQobjConfig(**qobj_config_dict) qubit_sizes = [] memory_slot_sizes = [] for circ in circuits: num_qubits = 0 memory_slots = 0 for qreg in circ.qregs: num_qubits += qreg.size for creg in circ.cregs: memory_slots += creg.size qubit_sizes.append(num_qubits) memory_slot_sizes.append(memory_slots) qobj_config.memory_slots = max(memory_slot_sizes) qobj_config.n_qubits = max(qubit_sizes) if pulse_library: qobj_config.pulse_library = [ PulseLibraryItem(name=name, samples=samples) for name, samples in pulse_library.items() ] if calibrations and calibrations.gates: qobj_config.calibrations = calibrations return QasmQobj(qobj_id=qobj_id, config=qobj_config, experiments=experiments, header=qobj_header)
def run_monte_carlo_experiments(pulse_sim_desc, pulse_de_model, solver_options=None): """ Runs monte carlo experiments for a given op_system Parameters: pulse_sim_desc (PulseSimDescription): description of pulse simulation pulse_de_model (PulseInternalDEModel): description of de model solver_options (PulseSimOptions): options Returns: tuple: two lists with experiment results Raises: Exception: if initial state is of incorrect format """ solver_options = PulseSimOptions( ) if solver_options is None else solver_options if not pulse_sim_desc.initial_state.data.ndim != 1: raise Exception("Initial state must be a state vector.") y0 = pulse_sim_desc.initial_state.data.ravel() # set num_cpus to the value given in settings if none in Options if not solver_options.num_cpus: solver_options.num_cpus = CPU_COUNT # setup seeds array seed = pulse_sim_desc.seed or np.random.randint(np.iinfo(np.int32).max - 1) prng = np.random.RandomState(seed) for exp in pulse_sim_desc.experiments: exp['seed'] = prng.randint(np.iinfo(np.int32).max - 1) map_kwargs = {'num_processes': solver_options.num_cpus} exp_results = [] exp_times = [] # needs to be configured ahead of time pulse_de_model._config_internal_data() for exp in pulse_sim_desc.experiments: start = time.time() rng = np.random.RandomState(exp['seed']) seeds = rng.randint(np.iinfo(np.int32).max - 1, size=pulse_sim_desc.shots) exp_res = parallel_map(monte_carlo_evolution, seeds, task_args=( exp, y0, pulse_sim_desc, pulse_de_model, solver_options, ), **map_kwargs) # exp_results is a list for each shot # so transform back to an array of shots exp_res2 = [] for exp_shot in exp_res: exp_res2.append(exp_shot[0].tolist()) end = time.time() exp_times.append(end - start) exp_results.append(np.array(exp_res2)) return exp_results, exp_times
def test_parallel_schedule_names(self): """Verify unique schedule names in parallel""" out_schedules = parallel_map(_build_simple_schedule, list(range(10))) names = [schedule.name for schedule in out_schedules] self.assertEqual(len(names), len(set(names)))
def test_parallel_circuit_names(self): """Verify unique circuit names in parallel""" out_circs = parallel_map(_build_simple_circuit, list(range(10))) names = [circ.name for circ in out_circs] self.assertEqual(len(names), len(set(names)))
def test_parallel(self): """Test parallel_map""" ans = parallel_map(_parfunc, list(range(10))) self.assertEqual(ans, list(range(10)))
def schedule( circuits: Union[QuantumCircuit, List[QuantumCircuit]], backend: Optional[Backend] = None, inst_map: Optional[InstructionScheduleMap] = None, meas_map: Optional[List[List[int]]] = None, dt: Optional[float] = None, method: Optional[Union[str, List[str]]] = None, ) -> Union[Schedule, List[Schedule]]: """ Schedule a circuit to a pulse ``Schedule``, using the backend, according to any specified methods. Supported methods are documented in :py:mod:`qiskit.scheduler.schedule_circuit`. Args: circuits: The quantum circuit or circuits to translate backend: 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 ``backend``\'s ``instruction_schedule_map`` meas_map: List of sets of qubits that must be measured together. If ``None``, defaults to the ``backend``\'s ``meas_map`` dt: The output sample rate of backend control electronics. For scheduled circuits which contain time information, dt is required. If not provided, it will be obtained from the backend configuration method: Optionally specify a particular scheduling method Returns: A pulse ``Schedule`` that implements the input circuit Raises: QiskitError: If ``inst_map`` and ``meas_map`` are not passed and ``backend`` is not passed """ arg_circuits_list = isinstance(circuits, list) start_time = time() if backend and getattr(backend, "version", 0) > 1: if inst_map is None: inst_map = backend.instruction_schedule_map if meas_map is None: meas_map = backend.meas_map if dt is None: dt = backend.dt else: if inst_map is None: if backend is None: raise QiskitError( "Must supply either a backend or InstructionScheduleMap for scheduling passes." ) defaults = backend.defaults() if defaults is None: raise QiskitError( "The backend defaults are unavailable. The backend may not support pulse." ) inst_map = defaults.instruction_schedule_map if meas_map is None: if backend is None: raise QiskitError( "Must supply either a backend or a meas_map for scheduling passes." ) meas_map = backend.configuration().meas_map if dt is None: if backend is not None: dt = backend.configuration().dt schedule_config = ScheduleConfig(inst_map=inst_map, meas_map=meas_map, dt=dt) circuits = circuits if isinstance(circuits, list) else [circuits] schedules = parallel_map(schedule_circuit, circuits, (schedule_config, method)) end_time = time() _log_schedule_time(start_time, end_time) if arg_circuits_list: return schedules else: return schedules[0]
def run(self): """Runs the solver. """ map_kwargs = {'num_processes': self.op_system.ode_options.num_cpus} # exp_results from the solvers return the values of the measurement # operators # If no collapse terms, and only measurements at end # can do a single shot. # exp_results is a list of '0' and '1' # where '0' occurs with probability 1-<M> # and '1' occurs with probability <M> # M is the measurement operator, which is a projector # into one of the qubit states (usually |1>) if self.op_system.can_sample: start = time.time() exp_results = parallel_map(unitary_evolution, self.op_system.experiments, task_args=(self.op_system, ), **map_kwargs) end = time.time() exp_times = (np.ones(len(self.op_system.experiments)) * (end - start) / len(self.op_system.experiments)) # need to simulate each trajectory, so shots*len(experiments) times # Do a for-loop over experiments, and do shots in parallel_map else: exp_results = [] exp_times = [] for exp in self.op_system.experiments: start = time.time() rng = np.random.RandomState(exp['seed']) seeds = rng.randint(np.iinfo(np.int32).max - 1, size=self.op_system.global_data['shots']) exp_res = parallel_map(monte_carlo, seeds, task_args=( exp, self.op_system, ), **map_kwargs) # exp_results is a list for each shot # so transform back to an array of shots exp_res2 = [] for exp_shot in exp_res: exp_res2.append(exp_shot[0].tolist()) end = time.time() exp_times.append(end - start) exp_results.append(np.array(exp_res2)) # format the data into the proper output all_results = [] for idx_exp, exp in enumerate(self.op_system.experiments): m_lev = self.op_system.global_data['meas_level'] m_ret = self.op_system.global_data['meas_return'] # populate the results dictionary results = { 'seed_simulator': exp['seed'], 'shots': self.op_system.global_data['shots'], 'status': 'DONE', 'success': True, 'time_taken': exp_times[idx_exp], 'header': exp['header'], 'meas_level': m_lev, 'meas_return': m_ret, 'data': {} } if self.op_system.can_sample: memory = exp_results[idx_exp][0] results['data']['statevector'] = [] for coef in exp_results[idx_exp][1]: results['data']['statevector'].append( [np.real(coef), np.imag(coef)]) results['header']['ode_t'] = exp_results[idx_exp][2] else: memory = exp_results[idx_exp] # meas_level 2 return the shots if m_lev == 2: # convert the memory **array** into a n # integer # e.g. [1,0] -> 2 int_mem = memory.dot(np.power(2.0, np.arange( memory.shape[1]))).astype(int) # if the memory flag is set return each shot if self.op_system.global_data['memory']: hex_mem = [hex(val) for val in int_mem] results['data']['memory'] = hex_mem # Get hex counts dict unique = np.unique(int_mem, return_counts=True) hex_dict = {} for kk in range(unique[0].shape[0]): key = hex(unique[0][kk]) hex_dict[key] = unique[1][kk] results['data']['counts'] = hex_dict # meas_level 1 returns the <n> elif m_lev == 1: if m_ret == 'avg': memory = [np.mean(memory, 0)] # convert into the right [real, complex] pair form for json # this should be cython? results['data']['memory'] = [] for mem_shot in memory: results['data']['memory'].append([]) for mem_slot in mem_shot: results['data']['memory'][-1].append( [np.real(mem_slot), np.imag(mem_slot)]) if m_ret == 'avg': results['data']['memory'] = results['data']['memory'][0] all_results.append(results) if not self.op_system.use_cpp_ode_func: _cython_build_cleanup(self.op_system.global_data['rhs_file_name']) return all_results
def find_optim_param_energy(self, preferred_op = None, preferred_op_mode = False, previous_circuit = None) -> dict: """method: find_optim_param_energy finds the optimum operator+parameter pair for the next iteration of the ansatz args: preferred_op - an operator (weightedpaulioperator) predetermined to be the next operator in the ansatz (essentially converts this method to rotosolve instead of rotoselect) preferred_op_mode - a flag (boolean) to choose whether or not preferred op should be used returns: dictionary with: optimal operator, name of optimal operator (for output purposes), optimal parameter, optimal energy, A, B, and C for Asin(theta + B) + C """ A = np.empty(0) C = np.empty(0) final_circs = [] E_list = [] #measure energies (theta = 0, theta = pi/4, theta = -pi/4) if(self.adapt_step_history['Total number energy iterations'] == 0): wavefunc = self.initial_state.construct_circuit() Energy_0_circ = self.hamiltonian.construct_evaluation_circuit(wavefunc, True) result = self.quantum_instance.execute(Energy_0_circ) Energy_0 = np.real(self.hamiltonian.evaluate_with_result(result, True)[0]) self.adapt_step_history['Total num evals'] += 1 else: op_list = self._current_operator_list bounds=[(-np.pi, +np.pi)]*len(op_list) vf = ADAPTVariationalForm(op_list, bounds, self.initial_state) wavefunc = vf.construct_circuit(self.adapt_step_history['optimal_parameters'][-1]) Energy_0 = self.adapt_step_history['energy_history'][-1] if preferred_op_mode: pool = preferred_op else: pool = self.operator_pool.pool args = [] kwargs = {'ham': self.hamiltonian, 'energy_step_tol': self.energy_step_tol, 'parameter': np.pi/4} op_list_pi4 = list(parallel_map( Generate_op, pool, args, kwargs )) kwargs['parameter'] = -np.pi/4 op_list_negpi4 = list(parallel_map( Generate_op, self.operator_pool.pool, args, kwargs )) op_list = op_list_pi4 + op_list_negpi4 kwargs = {'statevector_mode': self.quantum_instance.is_statevector} total_evaluation_circuits = list(parallel_map( _circ_eval, op_list, task_kwargs={**kwargs, 'wave_function': wavefunc}, num_processes=aqua_globals.num_processes )) total_evaluation_circuits = [item for sublist in total_evaluation_circuits for item in sublist] logger.info('Removing duplicate circuits') for circ in total_evaluation_circuits: if not fast_circuit_inclusion(circ, final_circs): final_circs.append(circ) result = self.quantum_instance.execute(final_circs) Energies = list(parallel_map( _compute_energy, op_list, task_kwargs={**kwargs, 'result': result}, num_processes=aqua_globals.num_processes )) for entry in Energies: E_list.append(np.real(entry[0])) cutoff = int(len(E_list)/2) Energy_pi4 = np.array(E_list[0:cutoff]) Energy_negpi4 = np.array(E_list[cutoff:]) #calculate minimum energy + A,B, and C from measured energies B = np.arctan2(( -Energy_negpi4 - Energy_pi4 + 2*Energy_0),(Energy_pi4 - Energy_negpi4)) Optim_param_array = (-B - np.pi/2)/2 X = np.sin(B) Y = np.sin(B + np.pi/2) Z = np.sin(B - np.pi/2) for i in range(0,(len(Energy_negpi4)-1)): if Y[i] != 0: C = np.append(C, (Energy_0-Energy_pi4[i]*(X[i]/Y[i]))/(1-X[i]/Y[i])) A = np.append(A, (Energy_pi4[i] - C[-1])/Y[i]) else: C = np.append(C, Energy_pi4[i]) A = np.append(A, (Energy_0 - C[-1])/X[i]) Optim_energy_array = C - A #find minimum energy index Optim_param_pos = np.argmin(Optim_energy_array) min_energy = Optim_energy_array[Optim_param_pos] Optim_param = Optim_param_array[Optim_param_pos] #CPU has limit on smallest number to be calculated - looks like its somewhere around 1e-16 #manually set this to zero as it should be zero. if min_energy > Energy_0 and abs(Optim_param) < 2e-16: Optim_param = 0 min_energy = Energy_0 #find optimum operator Optim_operator = self.operator_pool.pool[Optim_param_pos] Optim_operator_name = self.operator_pool.pool[Optim_param_pos].print_details() #keep track of number of quantum evaluations self.adapt_step_history['Total num evals'] += len(final_circs) return {'Newly Minimized Energy': min_energy, 'Next Parameter value': Optim_param, 'Next Operator identity': Optim_operator, 'Next Operator Name': Optim_operator_name, 'A': A[Optim_param_pos], 'B': B[Optim_param_pos], 'C': C[Optim_param_pos]}
def run_unitary_experiments(op_system): """ Runs unitary experiments for a given op_system Parameters: op_system (PulseSimDescription): container for simulation information Returns: tuple: two lists with experiment results Raises: Exception: if initial state is of incorrect format """ if not op_system.initial_state.isket: raise Exception("Initial state must be a state vector.") # set num_cpus to the value given in settings if none in Options if not op_system.ode_options.num_cpus: op_system.ode_options.num_cpus = CPU_COUNT # setup seeds array seed = op_system.global_data.get('seed', np.random.randint(np.iinfo(np.int32).max - 1)) prng = np.random.RandomState(seed) for exp in op_system.experiments: exp['seed'] = prng.randint(np.iinfo(np.int32).max - 1) map_kwargs = {'num_processes': op_system.ode_options.num_cpus} # set up full simulation, i.e. combining different (ideally modular) computational # resources into one function def full_simulation(exp, op_system): psi, ode_t = unitary_evolution(exp, op_system) # ############### # do measurement # ############### rng = np.random.RandomState(exp['seed']) shots = op_system.global_data['shots'] # Init memory memory = np.zeros((shots, op_system.global_data['memory_slots']), dtype=np.uint8) qubits = [] memory_slots = [] tlist = exp['tlist'] for acq in exp['acquire']: if acq[0] == tlist[-1]: qubits += list(acq[1]) memory_slots += list(acq[2]) qubits = np.array(qubits, dtype='uint32') memory_slots = np.array(memory_slots, dtype='uint32') probs = occ_probabilities(qubits, psi, op_system.global_data['measurement_ops']) rand_vals = rng.rand(memory_slots.shape[0] * shots) write_shots_memory(memory, memory_slots, probs, rand_vals) return [memory, psi, ode_t] # run simulation on each experiment in parallel start = time.time() exp_results = parallel_map(full_simulation, op_system.experiments, task_args=(op_system,), **map_kwargs ) end = time.time() exp_times = (np.ones(len(op_system.experiments)) * (end - start) / len(op_system.experiments)) return exp_results, exp_times
def transpile(circuits: Union[QuantumCircuit, List[QuantumCircuit]], backend: Optional[Union[Backend, BaseBackend]] = None, basis_gates: Optional[List[str]] = None, coupling_map: Optional[Union[CouplingMap, List[List[int]]]] = None, backend_properties: Optional[BackendProperties] = None, initial_layout: Optional[Union[Layout, Dict, List]] = None, layout_method: Optional[str] = None, routing_method: Optional[str] = None, translation_method: Optional[str] = None, scheduling_method: Optional[str] = None, instruction_durations: Optional[InstructionDurationsType] = None, dt: Optional[float] = None, seed_transpiler: Optional[int] = None, optimization_level: Optional[int] = None, pass_manager: Optional[PassManager] = None, callback: Optional[Callable[[BasePass, DAGCircuit, float, PropertySet, int], Any]] = None, output_name: Optional[Union[str, List[str]]] = None) -> Union[QuantumCircuit, List[QuantumCircuit]]: """Transpile one or more circuits, according to some desired transpilation targets. All arguments may be given as either a singleton or list. In case of a list, the length must be equal to the number of circuits being transpiled. Transpilation is done in parallel using multiprocessing. Args: circuits: Circuit(s) to transpile backend: If set, transpiler options are automatically grabbed from ``backend.configuration()`` and ``backend.properties()``. If any other option is explicitly set (e.g., ``coupling_map``), it will override the backend's. .. note:: The backend arg is purely for convenience. The resulting circuit may be run on any backend as long as it is compatible. basis_gates: List of basis gate names to unroll to (e.g: ``['u1', 'u2', 'u3', 'cx']``). If ``None``, do not unroll. coupling_map: Coupling map (perhaps custom) to target in mapping. Multiple formats are supported: #. ``CouplingMap`` instance #. List, must be given as an adjacency matrix, where each entry specifies all two-qubit interactions supported by backend, e.g: ``[[0, 1], [0, 3], [1, 2], [1, 5], [2, 5], [4, 1], [5, 3]]`` backend_properties: properties returned by a backend, including information on gate errors, readout errors, qubit coherence times, etc. Find a backend that provides this information with: ``backend.properties()`` initial_layout: Initial position of virtual qubits on physical qubits. If this layout makes the circuit compatible with the coupling_map constraints, it will be used. The final layout is not guaranteed to be the same, as the transpiler may permute qubits through swaps or other means. Multiple formats are supported: #. ``Layout`` instance #. Dict * virtual to physical:: {qr[0]: 0, qr[1]: 3, qr[2]: 5} * physical to virtual:: {0: qr[0], 3: qr[1], 5: qr[2]} #. List * virtual to physical:: [0, 3, 5] # virtual qubits are ordered (in addition to named) * physical to virtual:: [qr[0], None, None, qr[1], None, qr[2]] layout_method: Name of layout selection pass ('trivial', 'dense', 'noise_adaptive', 'sabre') routing_method: Name of routing pass ('basic', 'lookahead', 'stochastic', 'sabre', 'none') translation_method: Name of translation pass ('unroller', 'translator', 'synthesis') scheduling_method: Name of scheduling pass. * ``'as_soon_as_possible'``: Schedule instructions greedily, as early as possible on a qubit resource. alias: ``'asap'``) * ``'as_late_as_possible'``: Schedule instructions late, i.e. keeping qubits in the ground state when possible. (alias: ``'alap'``) If ``None``, no scheduling will be done. instruction_durations: Durations of instructions. The gate lengths defined in ``backend.properties`` are used as default. They are overwritten if this ``instruction_durations`` is specified. The format of ``instruction_durations`` must be as follows. The `instruction_durations` must be given as a list of tuples [(instruction_name, qubits, duration, unit), ...]. | [('cx', [0, 1], 12.3, 'ns'), ('u3', [0], 4.56, 'ns')] | [('cx', [0, 1], 1000), ('u3', [0], 300)] If unit is omitted, the default is 'dt', which is a sample time depending on backend. If the time unit is 'dt', the duration must be an integer. dt: Backend sample time (resolution) in seconds. If ``None`` (default), ``backend.configuration().dt`` is used. seed_transpiler: Sets random seed for the stochastic parts of the transpiler optimization_level: How much optimization to perform on the circuits. Higher levels generate more optimized circuits, at the expense of longer transpilation time. * 0: no optimization * 1: light optimization * 2: heavy optimization * 3: even heavier optimization If ``None``, level 1 will be chosen as default. pass_manager: The pass manager to use for a custom pipeline of transpiler passes. If this arg is present, all other args will be ignored and the pass manager will be used directly (Qiskit will not attempt to auto-select a pass manager based on transpile options). callback: A callback function that will be called after each pass execution. The function will be called with 5 keyword arguments, | ``pass_``: the pass being run. | ``dag``: the dag output of the pass. | ``time``: the time to execute the pass. | ``property_set``: the property set. | ``count``: the index for the pass execution. The exact arguments passed expose the internals of the pass manager, and are subject to change as the pass manager internals change. If you intend to reuse a callback function over multiple releases, be sure to check that the arguments being passed are the same. To use the callback feature, define a function that will take in kwargs dict and access the variables. For example:: def callback_func(**kwargs): pass_ = kwargs['pass_'] dag = kwargs['dag'] time = kwargs['time'] property_set = kwargs['property_set'] count = kwargs['count'] ... transpile(circ, callback=callback_func) output_name: A list with strings to identify the output circuits. The length of the list should be exactly the length of the ``circuits`` parameter. Returns: The transpiled circuit(s). Raises: TranspilerError: in case of bad inputs to transpiler (like conflicting parameters) or errors in passes """ arg_circuits_list = isinstance(circuits, list) circuits = circuits if arg_circuits_list else [circuits] # transpiling schedules is not supported yet. start_time = time() if all(isinstance(c, Schedule) for c in circuits): warnings.warn("Transpiling schedules is not supported yet.", UserWarning) end_time = time() _log_transpile_time(start_time, end_time) if arg_circuits_list: return circuits else: return circuits[0] if pass_manager is not None: _check_conflicting_argument(optimization_level=optimization_level, basis_gates=basis_gates, coupling_map=coupling_map, seed_transpiler=seed_transpiler, backend_properties=backend_properties, initial_layout=initial_layout, layout_method=layout_method, routing_method=routing_method, translation_method=translation_method, backend=backend) warnings.warn("The parameter pass_manager in transpile is being deprecated. " "The preferred way to tranpile a circuit using a custom pass manager is" " pass_manager.run(circuit)", DeprecationWarning, stacklevel=2) return pass_manager.run(circuits, output_name=output_name, callback=callback) if optimization_level is None: # Take optimization level from the configuration or 1 as default. config = user_config.get_config() optimization_level = config.get('transpile_optimization_level', 1) if scheduling_method is not None and backend is None and not instruction_durations: warnings.warn("When scheduling circuits without backend," " 'instruction_durations' should be usually provided.", UserWarning) # Get transpile_args to configure the circuit transpilation job(s) transpile_args = _parse_transpile_args(circuits, backend, basis_gates, coupling_map, backend_properties, initial_layout, layout_method, routing_method, translation_method, scheduling_method, instruction_durations, dt, seed_transpiler, optimization_level, callback, output_name) _check_circuits_coupling_map(circuits, transpile_args, backend) # Transpile circuits in parallel circuits = parallel_map(_transpile_circuit, list(zip(circuits, transpile_args))) end_time = time() _log_transpile_time(start_time, end_time) if arg_circuits_list: return circuits else: return circuits[0]
def transpile( circuits: Union[QuantumCircuit, List[QuantumCircuit]], backend: Optional[Union[Backend, BaseBackend]] = None, basis_gates: Optional[List[str]] = None, inst_map: Optional[List[InstructionScheduleMap]] = None, coupling_map: Optional[Union[CouplingMap, List[List[int]]]] = None, backend_properties: Optional[BackendProperties] = None, initial_layout: Optional[Union[Layout, Dict, List]] = None, layout_method: Optional[str] = None, routing_method: Optional[str] = None, translation_method: Optional[str] = None, scheduling_method: Optional[str] = None, instruction_durations: Optional[InstructionDurationsType] = None, dt: Optional[float] = None, approximation_degree: Optional[float] = None, timing_constraints: Optional[Dict[str, int]] = None, seed_transpiler: Optional[int] = None, optimization_level: Optional[int] = None, callback: Optional[Callable[ [BasePass, DAGCircuit, float, PropertySet, int], Any]] = None, output_name: Optional[Union[str, List[str]]] = None, unitary_synthesis_method: str = "default", unitary_synthesis_plugin_config: dict = None, target: Target = None, ) -> Union[QuantumCircuit, List[QuantumCircuit]]: """Transpile one or more circuits, according to some desired transpilation targets. All arguments may be given as either a singleton or list. In case of a list, the length must be equal to the number of circuits being transpiled. Transpilation is done in parallel using multiprocessing. Args: circuits: Circuit(s) to transpile backend: If set, transpiler options are automatically grabbed from ``backend.configuration()`` and ``backend.properties()``. If any other option is explicitly set (e.g., ``coupling_map``), it will override the backend's. .. note:: The backend arg is purely for convenience. The resulting circuit may be run on any backend as long as it is compatible. basis_gates: List of basis gate names to unroll to (e.g: ``['u1', 'u2', 'u3', 'cx']``). If ``None``, do not unroll. inst_map: Mapping of unrolled gates to pulse schedules. If this is not provided, transpiler tries to get from the backend. If any user defined calibration is found in the map and this is used in a circuit, transpiler attaches the custom gate definition to the circuit. This enables one to flexibly override the low-level instruction implementation. This feature is available iff the backend supports the pulse gate experiment. coupling_map: Coupling map (perhaps custom) to target in mapping. Multiple formats are supported: #. ``CouplingMap`` instance #. List, must be given as an adjacency matrix, where each entry specifies all two-qubit interactions supported by backend, e.g: ``[[0, 1], [0, 3], [1, 2], [1, 5], [2, 5], [4, 1], [5, 3]]`` backend_properties: properties returned by a backend, including information on gate errors, readout errors, qubit coherence times, etc. Find a backend that provides this information with: ``backend.properties()`` initial_layout: Initial position of virtual qubits on physical qubits. If this layout makes the circuit compatible with the coupling_map constraints, it will be used. The final layout is not guaranteed to be the same, as the transpiler may permute qubits through swaps or other means. Multiple formats are supported: #. ``Layout`` instance #. Dict * virtual to physical:: {qr[0]: 0, qr[1]: 3, qr[2]: 5} * physical to virtual:: {0: qr[0], 3: qr[1], 5: qr[2]} #. List * virtual to physical:: [0, 3, 5] # virtual qubits are ordered (in addition to named) * physical to virtual:: [qr[0], None, None, qr[1], None, qr[2]] layout_method: Name of layout selection pass ('trivial', 'dense', 'noise_adaptive', 'sabre') routing_method: Name of routing pass ('basic', 'lookahead', 'stochastic', 'sabre', 'none') translation_method: Name of translation pass ('unroller', 'translator', 'synthesis') scheduling_method: Name of scheduling pass. * ``'as_soon_as_possible'``: Schedule instructions greedily, as early as possible on a qubit resource. (alias: ``'asap'``) * ``'as_late_as_possible'``: Schedule instructions late, i.e. keeping qubits in the ground state when possible. (alias: ``'alap'``) If ``None``, no scheduling will be done. instruction_durations: Durations of instructions. Applicable only if scheduling_method is specified. The gate lengths defined in ``backend.properties`` are used as default. They are overwritten if this ``instruction_durations`` is specified. The format of ``instruction_durations`` must be as follows. The `instruction_durations` must be given as a list of tuples [(instruction_name, qubits, duration, unit), ...]. | [('cx', [0, 1], 12.3, 'ns'), ('u3', [0], 4.56, 'ns')] | [('cx', [0, 1], 1000), ('u3', [0], 300)] If unit is omitted, the default is 'dt', which is a sample time depending on backend. If the time unit is 'dt', the duration must be an integer. dt: Backend sample time (resolution) in seconds. If ``None`` (default), ``backend.configuration().dt`` is used. approximation_degree (float): heuristic dial used for circuit approximation (1.0=no approximation, 0.0=maximal approximation) timing_constraints: An optional control hardware restriction on instruction time resolution. A quantum computer backend may report a set of restrictions, namely: - granularity: An integer value representing minimum pulse gate resolution in units of ``dt``. A user-defined pulse gate should have duration of a multiple of this granularity value. - min_length: An integer value representing minimum pulse gate length in units of ``dt``. A user-defined pulse gate should be longer than this length. - pulse_alignment: An integer value representing a time resolution of gate instruction starting time. Gate instruction should start at time which is a multiple of the alignment value. - acquire_alignment: An integer value representing a time resolution of measure instruction starting time. Measure instruction should start at time which is a multiple of the alignment value. This information will be provided by the backend configuration. If the backend doesn't have any restriction on the instruction time allocation, then ``timing_constraints`` is None and no adjustment will be performed. seed_transpiler: Sets random seed for the stochastic parts of the transpiler optimization_level: How much optimization to perform on the circuits. Higher levels generate more optimized circuits, at the expense of longer transpilation time. * 0: no optimization * 1: light optimization * 2: heavy optimization * 3: even heavier optimization If ``None``, level 1 will be chosen as default. callback: A callback function that will be called after each pass execution. The function will be called with 5 keyword arguments, | ``pass_``: the pass being run. | ``dag``: the dag output of the pass. | ``time``: the time to execute the pass. | ``property_set``: the property set. | ``count``: the index for the pass execution. The exact arguments passed expose the internals of the pass manager, and are subject to change as the pass manager internals change. If you intend to reuse a callback function over multiple releases, be sure to check that the arguments being passed are the same. To use the callback feature, define a function that will take in kwargs dict and access the variables. For example:: def callback_func(**kwargs): pass_ = kwargs['pass_'] dag = kwargs['dag'] time = kwargs['time'] property_set = kwargs['property_set'] count = kwargs['count'] ... transpile(circ, callback=callback_func) output_name: A list with strings to identify the output circuits. The length of the list should be exactly the length of the ``circuits`` parameter. unitary_synthesis_method (str): The name of the unitary synthesis method to use. By default 'default' is used, which is the only method included with qiskit. If you have installed any unitary synthesis plugins you can use the name exported by the plugin. unitary_synthesis_plugin_config: An optional configuration dictionary that will be passed directly to the unitary synthesis plugin. By default this setting will have no effect as the default unitary synthesis method does not take custom configuration. This should only be necessary when a unitary synthesis plugin is specified with the ``unitary_synthesis`` argument. As this is custom for each unitary synthesis plugin refer to the plugin documentation for how to use this option. target: A backend transpiler target. Normally this is specified as part of the ``backend`` argument, but if you have manually constructed a :class:`~qiskit.transpiler.Target` object you can specify it manually here. This will override the target from ``backend``. Returns: The transpiled circuit(s). Raises: TranspilerError: in case of bad inputs to transpiler (like conflicting parameters) or errors in passes """ arg_circuits_list = isinstance(circuits, list) circuits = circuits if arg_circuits_list else [circuits] if not circuits: return [] # transpiling schedules is not supported yet. start_time = time() if all(isinstance(c, Schedule) for c in circuits): warnings.warn("Transpiling schedules is not supported yet.", UserWarning) end_time = time() _log_transpile_time(start_time, end_time) if arg_circuits_list: return circuits else: return circuits[0] if optimization_level is None: # Take optimization level from the configuration or 1 as default. config = user_config.get_config() optimization_level = config.get("transpile_optimization_level", 1) if (scheduling_method is not None and backend is None and target is None and not instruction_durations): warnings.warn( "When scheduling circuits without backend," " 'instruction_durations' should be usually provided.", UserWarning, ) # Get transpile_args to configure the circuit transpilation job(s) transpile_args = _parse_transpile_args( circuits, backend, basis_gates, inst_map, coupling_map, backend_properties, initial_layout, layout_method, routing_method, translation_method, scheduling_method, instruction_durations, dt, approximation_degree, seed_transpiler, optimization_level, callback, output_name, timing_constraints, unitary_synthesis_method, unitary_synthesis_plugin_config, target, ) _check_circuits_coupling_map(circuits, transpile_args, backend) # Transpile circuits in parallel circuits = parallel_map(_transpile_circuit, list(zip(circuits, transpile_args))) end_time = time() _log_transpile_time(start_time, end_time) if arg_circuits_list: return circuits else: return circuits[0]
def transpile(circuits, backend=None, basis_gates=None, coupling_map=None, backend_properties=None, initial_layout=None, seed_transpiler=None, optimization_level=None, pass_manager=None): """transpile one or more circuits, according to some desired transpilation targets. All arguments may be given as either singleton or list. In case of list, the length must be equal to the number of circuits being transpiled. Transpilation is done in parallel using multiprocessing. Args: circuits (QuantumCircuit or list[QuantumCircuit]): Circuit(s) to transpile backend (BaseBackend): If set, transpiler options are automatically grabbed from backend.configuration() and backend.properties(). If any other option is explicitly set (e.g. coupling_map), it will override the backend's. Note: the backend arg is purely for convenience. The resulting circuit may be run on any backend as long as it is compatible. basis_gates (list[str]): List of basis gate names to unroll to. e.g: ['u1', 'u2', 'u3', 'cx'] If None, do not unroll. coupling_map (CouplingMap or list): Coupling map (perhaps custom) to target in mapping. Multiple formats are supported: a. CouplingMap instance b. list Must be given as an adjacency matrix, where each entry specifies all two-qubit interactions supported by backend e.g: [[0, 1], [0, 3], [1, 2], [1, 5], [2, 5], [4, 1], [5, 3]] backend_properties (BackendProperties): properties returned by a backend, including information on gate errors, readout errors, qubit coherence times, etc. For a backend that provides this information, it can be obtained with: ``backend.properties()`` initial_layout (Layout or dict or list): Initial position of virtual qubits on physical qubits. If this layout makes the circuit compatible with the coupling_map constraints, it will be used. The final layout is not guaranteed to be the same, as the transpiler may permute qubits through swaps or other means. Multiple formats are supported: a. Layout instance b. dict virtual to physical: {qr[0]: 0, qr[1]: 3, qr[2]: 5} physical to virtual: {0: qr[0], 3: qr[1], 5: qr[2]} c. list virtual to physical: [0, 3, 5] # virtual qubits are ordered (in addition to named) physical to virtual: [qr[0], None, None, qr[1], None, qr[2]] seed_transpiler (int): sets random seed for the stochastic parts of the transpiler optimization_level (int): How much optimization to perform on the circuits. Higher levels generate more optimized circuits, at the expense of longer transpilation time. 0: no optimization 1: light optimization 2: heavy optimization 3: even heavier optimization pass_manager (PassManager): The pass manager to use for a custom pipeline of transpiler passes. If this arg is present, all other args will be ignored and the pass manager will be used directly (Qiskit will not attempt to auto-select a pass manager based on transpile options). Returns: QuantumCircuit or list[QuantumCircuit]: transpiled circuit(s). Raises: TranspilerError: in case of bad inputs to transpiler or errors in passes """ # transpiling schedules is not supported yet. if isinstance(circuits, Schedule) or \ (isinstance(circuits, list) and all(isinstance(c, Schedule) for c in circuits)): return circuits if optimization_level is None: config = user_config.get_config() optimization_level = config.get('transpile_optimization_level', None) # Get TranspileConfig(s) to configure the circuit transpilation job(s) circuits = circuits if isinstance(circuits, list) else [circuits] transpile_configs = _parse_transpile_args(circuits, backend, basis_gates, coupling_map, backend_properties, initial_layout, seed_transpiler, optimization_level, pass_manager) # Check circuit width against number of qubits in coupling_map(s) coupling_maps_list = list(config.coupling_map for config in transpile_configs) for circuit, parsed_coupling_map in zip(circuits, coupling_maps_list): # If coupling_map is not None if isinstance(parsed_coupling_map, CouplingMap): n_qubits = len(circuit.qubits) max_qubits = parsed_coupling_map.size() if n_qubits > max_qubits: raise TranspilerError( 'Number of qubits ({}) '.format(n_qubits) + 'in {} '.format(circuit.name) + 'is greater than maximum ({}) '.format(max_qubits) + 'in the coupling_map') # Transpile circuits in parallel circuits = parallel_map(_transpile_circuit, list(zip(circuits, transpile_configs))) if len(circuits) == 1: return circuits[0] return circuits
def transpile(circuits, backend=None, basis_gates=None, coupling_map=None, backend_properties=None, initial_layout=None, seed_transpiler=None, optimization_level=None, pass_manager=None, callback=None, output_name=None): """Transpile one or more circuits, according to some desired transpilation targets. All arguments may be given as either singleton or list. In case of list, the length must be equal to the number of circuits being transpiled. Transpilation is done in parallel using multiprocessing. Args: circuits (QuantumCircuit or list[QuantumCircuit]): Circuit(s) to transpile backend (BaseBackend): If set, transpiler options are automatically grabbed from backend.configuration() and backend.properties(). If any other option is explicitly set (e.g. coupling_map), it will override the backend's. Note: the backend arg is purely for convenience. The resulting circuit may be run on any backend as long as it is compatible. basis_gates (list[str]): List of basis gate names to unroll to. e.g:: ['u1', 'u2', 'u3', 'cx'] If None, do not unroll. coupling_map (CouplingMap or list): Coupling map (perhaps custom) to target in mapping. Multiple formats are supported: 1. CouplingMap instance 2. list Must be given as an adjacency matrix, where each entry specifies all two-qubit interactions supported by backend e.g:: [[0, 1], [0, 3], [1, 2], [1, 5], [2, 5], [4, 1], [5, 3]] backend_properties (BackendProperties): properties returned by a backend, including information on gate errors, readout errors, qubit coherence times, etc. For a backend that provides this information, it can be obtained with: ``backend.properties()`` initial_layout (Layout or dict or list): Initial position of virtual qubits on physical qubits. If this layout makes the circuit compatible with the coupling_map constraints, it will be used. The final layout is not guaranteed to be the same, as the transpiler may permute qubits through swaps or other means. Multiple formats are supported: 1. Layout instance 2. dict * virtual to physical:: {qr[0]: 0, qr[1]: 3, qr[2]: 5} * physical to virtual:: {0: qr[0], 3: qr[1], 5: qr[2]} 3. list * virtual to physical:: [0, 3, 5] # virtual qubits are ordered (in addition to named) * physical to virtual:: [qr[0], None, None, qr[1], None, qr[2]] seed_transpiler (int): sets random seed for the stochastic parts of the transpiler optimization_level (int): How much optimization to perform on the circuits. Higher levels generate more optimized circuits, at the expense of longer transpilation time. * 0: no optimization * 1: light optimization * 2: heavy optimization * 3: even heavier optimization If None, level 1 will be chosen as default. pass_manager (PassManager): The pass manager to use for a custom pipeline of transpiler passes. If this arg is present, all other args will be ignored and the pass manager will be used directly (Qiskit will not attempt to auto-select a pass manager based on transpile options). callback (func): A callback function that will be called after each pass execution. The function will be called with 5 keyword arguments: pass_ (Pass): the pass being run dag (DAGCircuit): the dag output of the pass time (float): the time to execute the pass property_set (PropertySet): the property set count (int): the index for the pass execution The exact arguments pass expose the internals of the pass manager and are subject to change as the pass manager internals change. If you intend to reuse a callback function over multiple releases be sure to check that the arguments being passed are the same. To use the callback feature you define a function that will take in kwargs dict and access the variables. For example:: def callback_func(**kwargs): pass_ = kwargs['pass_'] dag = kwargs['dag'] time = kwargs['time'] property_set = kwargs['property_set'] count = kwargs['count'] ... transpile(circ, callback=callback_func) output_name (str or list[str]) : A list with strings to identify the output circuits. The length of `list[str]` should be exactly the length of `circuits` parameter. Returns: QuantumCircuit or list[QuantumCircuit]: transpiled circuit(s). Raises: TranspilerError: in case of bad inputs to transpiler or errors in passes """ # transpiling schedules is not supported yet. if isinstance(circuits, Schedule) or \ (isinstance(circuits, list) and all(isinstance(c, Schedule) for c in circuits)): return circuits if optimization_level is None: config = user_config.get_config() optimization_level = config.get('transpile_optimization_level', None) # Get TranspileConfig(s) to configure the circuit transpilation job(s) circuits = circuits if isinstance(circuits, list) else [circuits] transpile_configs = _parse_transpile_args(circuits, backend, basis_gates, coupling_map, backend_properties, initial_layout, seed_transpiler, optimization_level, pass_manager, callback, output_name) # Check circuit width against number of qubits in coupling_map(s) coupling_maps_list = list(config.coupling_map for config in transpile_configs) for circuit, parsed_coupling_map in zip(circuits, coupling_maps_list): # If coupling_map is not None or n_qubits == 1 n_qubits = len(circuit.qubits) max_qubits = None if isinstance(parsed_coupling_map, CouplingMap): max_qubits = parsed_coupling_map.size() # If coupling_map is None, the limit might be in the backend (like in 1Q devices) elif backend is not None and not backend.configuration().simulator: max_qubits = backend.configuration().n_qubits if max_qubits is not None and (n_qubits > max_qubits): raise TranspilerError( 'Number of qubits ({}) '.format(n_qubits) + 'in {} '.format(circuit.name) + 'is greater than maximum ({}) '.format(max_qubits) + 'in the coupling_map') # Transpile circuits in parallel circuits = parallel_map(_transpile_circuit, list(zip(circuits, transpile_configs))) if len(circuits) == 1: return circuits[0] return circuits