def __init__(self, data, input_dims=None, output_dims=None): """Initialize a quantum channel Choi matrix operator. Args: data (QuantumCircuit or Instruction or BaseOperator or matrix): data to initialize superoperator. input_dims (tuple): the input subsystem dimensions. [Default: None] output_dims (tuple): the output subsystem dimensions. [Default: None] Raises: QiskitError: if input data cannot be initialized as a Choi matrix. Additional Information: If the input or output dimensions are None, they will be automatically determined from the input data. If the input data is a Numpy array of shape (4**N, 4**N) qubit systems will be used. If the input operator is not an N-qubit operator, it will assign a single subsystem with dimension specified by the shape of the input. """ # If the input is a raw list or matrix we assume that it is # already a Choi matrix. if isinstance(data, (list, np.ndarray)): # Initialize from raw numpy or list matrix. choi_mat = np.asarray(data, dtype=complex) # Determine input and output dimensions dim_l, dim_r = choi_mat.shape if dim_l != dim_r: raise QiskitError('Invalid Choi-matrix input.') if input_dims: input_dim = np.product(input_dims) if output_dims: output_dim = np.product(output_dims) if output_dims is None and input_dims is None: output_dim = int(np.sqrt(dim_l)) input_dim = dim_l // output_dim elif input_dims is None: input_dim = dim_l // output_dim elif output_dims is None: output_dim = dim_l // input_dim # Check dimensions if input_dim * output_dim != dim_l: raise QiskitError("Invalid shape for input Choi-matrix.") else: # Otherwise we initialize by conversion from another Qiskit # object into the QuantumChannel. if isinstance(data, (QuantumCircuit, Instruction)): # If the input is a Terra QuantumCircuit or Instruction we # convert it to a SuperOp data = SuperOp._init_instruction(data) else: # We use the QuantumChannel init transform to initialize # other objects into a QuantumChannel or Operator object. data = self._init_transformer(data) input_dim, output_dim = data.dim # Now that the input is an operator we convert it to a Choi object rep = getattr(data, '_channel_rep', 'Operator') choi_mat = _to_choi(rep, data._data, input_dim, output_dim) if input_dims is None: input_dims = data.input_dims() if output_dims is None: output_dims = data.output_dims() # Check and format input and output dimensions input_dims = self._automatic_dims(input_dims, input_dim) output_dims = self._automatic_dims(output_dims, output_dim) super().__init__(choi_mat, input_dims, output_dims, 'Choi')
def __init__(self, data, input_dims=None, output_dims=None): """Initialize a PTM quantum channel operator. Args: data (QuantumCircuit or Instruction or BaseOperator or matrix): data to initialize superoperator. input_dims (tuple): the input subsystem dimensions. [Default: None] output_dims (tuple): the output subsystem dimensions. [Default: None] Raises: QiskitError: if input data is not an N-qubit channel or cannot be initialized as a PTM. Additional Information: If the input or output dimensions are None, they will be automatically determined from the input data. The PTM representation is only valid for N-qubit channels. """ # If the input is a raw list or matrix we assume that it is # already a Chi matrix. if isinstance(data, (list, np.ndarray)): # Should we force this to be real? ptm = np.asarray(data, dtype=complex) # Determine input and output dimensions dout, din = ptm.shape if input_dims: input_dim = np.product(input_dims) else: input_dim = int(np.sqrt(din)) if output_dims: output_dim = np.product(input_dims) else: output_dim = int(np.sqrt(dout)) if output_dim**2 != dout or input_dim**2 != din or input_dim != output_dim: raise QiskitError("Invalid shape for PTM matrix.") else: # Otherwise we initialize by conversion from another Qiskit # object into the QuantumChannel. if isinstance(data, (QuantumCircuit, Instruction)): # If the input is a Terra QuantumCircuit or Instruction we # convert it to a SuperOp data = SuperOp._init_instruction(data) else: # We use the QuantumChannel init transform to initialize # other objects into a QuantumChannel or Operator object. data = self._init_transformer(data) input_dim, output_dim = data.dim # Now that the input is an operator we convert it to a PTM object rep = getattr(data, "_channel_rep", "Operator") ptm = _to_ptm(rep, data._data, input_dim, output_dim) if input_dims is None: input_dims = data.input_dims() if output_dims is None: output_dims = data.output_dims() # Check input is N-qubit channel num_qubits = int(np.log2(input_dim)) if 2**num_qubits != input_dim or input_dim != output_dim: raise QiskitError("Input is not an n-qubit Pauli transfer matrix.") super().__init__(ptm, num_qubits=num_qubits)
def execute( experiments, backend, basis_gates=None, coupling_map=None, # circuit transpile options backend_properties=None, initial_layout=None, seed_transpiler=None, optimization_level=None, pass_manager=None, qobj_id=None, qobj_header=None, shots=1024, # common run options memory=False, max_credits=10, seed_simulator=None, default_qubit_los=None, default_meas_los=None, # schedule run options schedule_los=None, meas_level=MeasLevel.CLASSIFIED, meas_return=MeasReturnType.AVERAGE, memory_slots=None, memory_slot_size=100, rep_time=None, rep_delay=None, parameter_binds=None, schedule_circuit=False, inst_map=None, meas_map=None, scheduling_method=None, init_qubits=None, **run_config): """Execute a list of :class:`qiskit.circuit.QuantumCircuit` or :class:`qiskit.pulse.Schedule` on a backend. The execution is asynchronous, and a handle to a job instance is returned. Args: experiments (QuantumCircuit or list[QuantumCircuit] or Schedule or list[Schedule]): Circuit(s) or pulse schedule(s) to execute backend (BaseBackend or Backend): Backend to execute circuits on. 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. 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: #. 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 (BackendProperties): 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 (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: #. :class:`qiskit.transpiler.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]] 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. #. No optimization #. Light optimization #. Heavy optimization #. Highest optimization If None, level 1 will be chosen as default. pass_manager (PassManager): The pass manager to use during transpilation. If this arg is present, auto-selection of pass manager based on the transpile options will be turned off and this pass manager will be used directly. qobj_id (str): String identifier to annotate the Qobj qobj_header (QobjHeader or dict): User input that will be inserted in Qobj header, and will also be copied to the corresponding :class:`qiskit.result.Result` header. Headers do not affect the run. shots (int): Number of repetitions of each circuit, for sampling. Default: 1024 memory (bool): If True, per-shot measurement bitstrings are returned as well (provided the backend supports it). For OpenPulse jobs, only measurement level 2 supports this option. Default: False max_credits (int): Maximum credits to spend on job. Default: 10 seed_simulator (int): Random seed to control sampling, for when backend is a simulator default_qubit_los (list): List of default qubit LO frequencies in Hz default_meas_los (list): List of default meas LO frequencies in Hz schedule_los (None or list or dict or LoConfig): Experiment LO configurations, if specified the list is in the format:: list[Union[Dict[PulseChannel, float], LoConfig]] or Union[Dict[PulseChannel, float], LoConfig] meas_level (int or MeasLevel): Set the appropriate level of the measurement output for pulse experiments. meas_return (str or MeasReturn): Level of measurement data for the backend to return For ``meas_level`` 0 and 1: ``"single"`` returns information from every shot. ``"avg"`` returns average measurement output (averaged over number of shots). memory_slots (int): Number of classical memory slots used in this job. memory_slot_size (int): Size of each memory slot if the output is Level 0. rep_time (int): Time per program execution in seconds. Must be from the list provided by the backend (``backend.configuration().rep_times``). Defaults to the first entry. rep_delay (float): Delay between programs in seconds. Only supported on certain backends (``backend.configuration().dynamic_reprate_enabled`` ). If supported, ``rep_delay`` will be used instead of ``rep_time`` and must be from the range supplied by the backend (``backend.configuration().rep_delay_range``). Default is given by ``backend.configuration().default_rep_delay``. parameter_binds (list[dict]): List of Parameter bindings over which the set of experiments will be executed. Each list element (bind) should be of the form ``{Parameter1: value1, Parameter2: value2, ...}``. All binds will be executed across all experiments, e.g. if parameter_binds is a length-n list, and there are m experiments, a total of :math:`m x n` experiments will be run (one for each experiment/bind pair). schedule_circuit (bool): If ``True``, ``experiments`` will be converted to :class:`qiskit.pulse.Schedule` objects prior to execution. inst_map (InstructionScheduleMap): Mapping of circuit operations to pulse schedules. If None, defaults to the ``instruction_schedule_map`` of ``backend``. meas_map (list(list(int))): List of sets of qubits that must be measured together. If None, defaults to the ``meas_map`` of ``backend``. scheduling_method (str or list(str)): Optionally specify a particular scheduling method. init_qubits (bool): Whether to reset the qubits to the ground state for each shot. Default: ``True``. run_config (dict): Extra arguments used to configure the run (e.g. for Aer configurable backends). Refer to the backend documentation for details on these arguments. Note: for now, these keyword arguments will both be copied to the Qobj config, and passed to backend.run() Returns: BaseJob: returns job instance derived from BaseJob Raises: QiskitError: if the execution cannot be interpreted as either circuits or schedules Example: Construct a 5-qubit GHZ circuit and execute 4321 shots on a backend. .. jupyter-execute:: from qiskit import QuantumCircuit, execute, BasicAer backend = BasicAer.get_backend('qasm_simulator') qc = QuantumCircuit(5, 5) qc.h(0) qc.cx(0, range(1, 5)) qc.measure_all() job = execute(qc, backend, shots=4321) """ if isinstance(experiments, Schedule) or (isinstance(experiments, list) and isinstance(experiments[0], Schedule)): # do not transpile a schedule circuit if schedule_circuit: raise QiskitError( "Must supply QuantumCircuit to schedule circuit.") elif pass_manager is not None: # transpiling using pass_manager _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) experiments = pass_manager.run(experiments) else: # transpiling the circuits using given transpile options experiments = transpile(experiments, basis_gates=basis_gates, coupling_map=coupling_map, backend_properties=backend_properties, initial_layout=initial_layout, seed_transpiler=seed_transpiler, optimization_level=optimization_level, backend=backend) if schedule_circuit: experiments = schedule(circuits=experiments, backend=backend, inst_map=inst_map, meas_map=meas_map, method=scheduling_method) if isinstance(backend, BaseBackend): # assembling the circuits into a qobj to be run on the backend qobj = assemble(experiments, qobj_id=qobj_id, qobj_header=qobj_header, shots=shots, memory=memory, max_credits=max_credits, seed_simulator=seed_simulator, default_qubit_los=default_qubit_los, default_meas_los=default_meas_los, schedule_los=schedule_los, meas_level=meas_level, meas_return=meas_return, memory_slots=memory_slots, memory_slot_size=memory_slot_size, rep_time=rep_time, rep_delay=rep_delay, parameter_binds=parameter_binds, backend=backend, init_qubits=init_qubits, **run_config) # executing the circuits on the backend and returning the job start_time = time() job = backend.run(qobj, **run_config) end_time = time() _log_submission_time(start_time, end_time) elif isinstance(backend, Backend): start_time = time() job = backend.run(experiments, shots=shots, memory=memory, seed_simulator=seed_simulator, default_qubit_los=default_qubit_los, default_meas_los=default_meas_los, schedule_los=schedule_los, meas_level=meas_level, meas_return=meas_return, memory_slots=memory_slots, memory_slot_size=memory_slot_size, rep_time=rep_time, rep_delay=rep_delay, parameter_binds=parameter_binds, init_qubits=init_qubits, **run_config) end_time = time() _log_submission_time(start_time, end_time) else: raise QiskitError("Invalid backend type %s" % type(backend)) return job
def __init__(self, data, input_dims=None, output_dims=None): """Initialize a quantum channel Superoperator operator. Args: data (QuantumCircuit or Instruction or BaseOperator or matrix): data to initialize superoperator. input_dims (tuple): the input subsystem dimensions. [Default: None] output_dims (tuple): the output subsystem dimensions. [Default: None] Raises: QiskitError: if input data cannot be initialized as a superoperator. Additional Information: If the input or output dimensions are None, they will be automatically determined from the input data. If the input data is a Numpy array of shape (4**N, 4**N) qubit systems will be used. If the input operator is not an N-qubit operator, it will assign a single subsystem with dimension specified by the shape of the input. """ # If the input is a raw list or matrix we assume that it is # already a superoperator. if isinstance(data, (list, np.ndarray)): # We initialize directly from superoperator matrix super_mat = np.asarray(data, dtype=complex) # Determine total input and output dimensions dout, din = super_mat.shape input_dim = int(np.sqrt(din)) output_dim = int(np.sqrt(dout)) if output_dim**2 != dout or input_dim**2 != din: raise QiskitError("Invalid shape for SuperOp matrix.") op_shape = OpShape.auto(dims_l=output_dims, dims_r=input_dims, shape=(output_dim, input_dim)) else: # Otherwise we initialize by conversion from another Qiskit # object into the QuantumChannel. if isinstance(data, (QuantumCircuit, Instruction)): # If the input is a Terra QuantumCircuit or Instruction we # perform a simulation to construct the circuit superoperator. # This will only work if the circuit or instruction can be # defined in terms of instructions which have no classical # register components. The instructions can be gates, reset, # or Kraus instructions. Any conditional gates or measure # will cause an exception to be raised. data = self._init_instruction(data) else: # We use the QuantumChannel init transform to initialize # other objects into a QuantumChannel or Operator object. data = self._init_transformer(data) # Now that the input is an operator we convert it to a # SuperOp object op_shape = data._op_shape input_dim, output_dim = data.dim rep = getattr(data, '_channel_rep', 'Operator') super_mat = _to_superop(rep, data._data, input_dim, output_dim) # Initialize QuantumChannel super().__init__(super_mat, op_shape=op_shape)
def normalize(single_inpt): if abs(single_inpt) < 1e-14: return '0' val = single_inpt / np.pi if output == 'text': pi = 'pi' elif output == 'latex': pi = '\\pi' elif output == 'mpl': pi = '$\\pi$' else: raise QiskitError( 'pi_check parameter output should be text, latex, or mpl') if abs(val) >= 1 - eps: if abs(abs(val) - abs(round(val))) < eps: val = int(round(val)) if val == 1: str_out = '{}'.format(pi) elif val == -1: str_out = '-{}'.format(pi) else: str_out = '{}{}'.format(val, pi) return str_out val = np.pi / single_inpt if abs(abs(val) - abs(round(val))) < eps: val = int(round(val)) if val > 0: if output == 'latex': str_out = '\\frac{%s}{%s}' % (pi, abs(val)) else: str_out = '{}/{}'.format(pi, val) else: if output == 'latex': str_out = '\\frac{-%s}{%s}' % (pi, abs(val)) else: str_out = '-{}/{}'.format(pi, abs(val)) return str_out # Look for all fracs in 8 abs_val = abs(single_inpt) frac = np.where(np.abs(abs_val - FRAC_MESH) < 1e-8) if frac[0].shape[0]: numer = int(frac[1][0]) + 1 denom = int(frac[0][0]) + 1 if single_inpt < 0: numer *= -1 if numer == 1 and denom == 1: str_out = '{}'.format(pi) elif numer == -1 and denom == 1: str_out = '-{}'.format(pi) elif numer == 1: if output == 'latex': str_out = '\\frac{%s}{%s}' % (pi, denom) else: str_out = '{}/{}'.format(pi, denom) elif numer == -1: if output == 'latex': str_out = '\\frac{-%s}{%s}' % (pi, denom) else: str_out = '-{}/{}'.format(pi, denom) elif denom == 1: if output == 'latex': str_out = '\\frac{%s}{%s}' % (numer, pi) else: str_out = '{}/{}'.format(numer, pi) else: if output == 'latex': str_out = '\\frac{%s%s}{%s}' % (numer, pi, denom) else: str_out = '{}{}/{}'.format(numer, pi, denom) return str_out # nothing found str_out = '%.{}g'.format(ndigits) % single_inpt return str_out
def weyl_coordinates(U): """Computes the Weyl coordinates for a given two-qubit unitary matrix. Args: U (ndarray): Input two-qubit unitary. Returns: ndarray: Array of Weyl coordinates. Raises: QiskitError: Computed coordinates not in Weyl chamber. """ pi2 = np.pi / 2 pi4 = np.pi / 4 U = U / la.det(U)**(0.25) Up = _Bd.dot(U).dot(_B) M2 = Up.T.dot(Up) # M2 is a symmetric complex matrix. We need to decompose it as M2 = P D P^T where # P ∈ SO(4), D is diagonal with unit-magnitude elements. # D, P = la.eig(M2) # this can fail for certain kinds of degeneracy for _ in range(3): # FIXME: this randomized algorithm is horrendous M2real = np.random.normal() * M2.real + np.random.normal() * M2.imag _, P = la.eigh(M2real) D = P.T.dot(M2).dot(P).diagonal() if np.allclose(P.dot(np.diag(D)).dot(P.T), M2, rtol=1.0e-10, atol=1.0e-10): break else: raise QiskitError( "TwoQubitWeylDecomposition: failed to diagonalize M2. " "Please submit this output to " "https://github.com/Qiskit/qiskit-terra/issues/4159 " "Input %s" % U.tolist()) d = -np.angle(D) / 2 d[3] = -d[0] - d[1] - d[2] cs = np.mod((d[:3] + d[3]) / 2, 2 * np.pi) # Reorder the eigenvalues to get in the Weyl chamber cstemp = np.mod(cs, pi2) np.minimum(cstemp, pi2 - cstemp, cstemp) order = np.argsort(cstemp)[[1, 2, 0]] cs = cs[order] d[:3] = d[order] # Flip into Weyl chamber if cs[0] > pi2: cs[0] -= 3 * pi2 if cs[1] > pi2: cs[1] -= 3 * pi2 conjs = 0 if cs[0] > pi4: cs[0] = pi2 - cs[0] conjs += 1 if cs[1] > pi4: cs[1] = pi2 - cs[1] conjs += 1 if cs[2] > pi2: cs[2] -= 3 * pi2 if conjs == 1: cs[2] = pi2 - cs[2] if cs[2] > pi4: cs[2] -= pi2 return cs[[1, 0, 2]]
def add(self, gate): """Add instruction to set.""" if not isinstance(gate, Instruction): raise QiskitError("attempt to add non-Instruction" + " to InstructionSet") self.instructions.append(gate)
def __init__( self, name: str, instructions: Optional[Sequence[Instruction]] = None, default_states: Optional[Sequence[Union[Statevector, DensityMatrix]]] = None, qubit_states: Optional[Dict[int, Sequence[Union[Statevector, DensityMatrix]]]] = None, ): """Initialize a fitter preparation basis. Args: name: a name to identity the basis. instructions: list of 1-qubit instructions for preparing states from the :math:`|0^{\\otimes n}\\rangle` state. default_states: Optional, default density matrices prepared by the input instructions. If None these will be determined by ideal simulation of the preparation instructions. qubit_states: Optional, a dict with physical qubit keys and a list of density matrices prepared by the list of basis instructions for a specific qubit. The default states will be used for any qubits not specified in this dict. Raises: QiskitError: if input states or instructions are not valid, or no instructions or states are provided. """ if instructions is None and default_states is None and qubit_states is None: raise QiskitError( "LocalPreparationBasis must define at least one of instructions, " "default_states, or qubit_states." ) super().__init__(name) # Internal variables self._instructions = tuple() self._size = None self._default_states = None self._default_dim = None self._qubit_states = {} self._qubit_dim = {} self._qubits = set() self._custom_defaults = True # Format instructions so compatible types can be converted to # Instruction instances. if instructions is not None: self._instructions = _format_instructions(instructions) self._size = len(instructions) if default_states is None: default_states = self._instructions self._custom_defaults = False # Construct default states if default_states is not None: self._default_states = tuple(DensityMatrix(i).data for i in default_states) self._default_dim = self._default_states[0].shape[0] if self._size is None: self._size = len(self._default_states) elif len(self._default_states) != self._size: raise QiskitError( "Number of instructions and number of default states must be equal." ) # Construct states of specific qubits if provided qubit_states = qubit_states or {} for qubit, states in qubit_states.items(): if self._size is None: self._size = len(states) elif len(states) != self._size: raise QiskitError("Number of instructions and number of states must be equal.") qstates = tuple(DensityMatrix(i).data for i in states) self._qubit_states[qubit] = qstates self._qubit_dim[qubit] = qstates[0].shape[0] self._qubits.add(qubit) # Pseudo hash value to make basis hashable for LRU cached functions self._hash = hash( ( type(self), self._name, self._size, self._default_dim, self._custom_defaults, tuple(self._qubits), tuple(self._qubit_dim.values()), (type(i) for i in self._instructions), ) )
def process_fidelity(channel, target=None, require_cp=True, require_tp=False): r"""Return the process fidelity of a noisy quantum channel. The process fidelity :math:`F_{\text{pro}}(\mathcal{E}, \methcal{F})` between two quantum channels :math:`\mathcal{E}, \mathcal{F}` is given by .. math: F_{\text{pro}}(\mathcal{E}, \mathcal{F}) = F(\rho_{\mathcal{E}}, \rho_{\mathcal{F}}) where :math:`F` is the :func:`~qiskit.quantum_info.state_fidelity`, :math:`\rho_{\mathcal{E}} = \Lambda_{\mathcal{E}} / d` is the normalized :class:`~qiskit.quantum_info.Choi` matrix for the channel :math:`\mathcal{E}`, and :math:`d` is the input dimension of :math:`\mathcal{E}`. When the target channel is unitary this is equivalent to .. math:: F_{\text{pro}}(\mathcal{E}, U) = \frac{Tr[S_U^\dagger S_{\mathcal{E}}]}{d^2} where :math:`S_{\mathcal{E}}, S_{U}` are the :class:`~qiskit.quantum_info.SuperOp` matrices for the *input* quantum channel :math:`\mathcal{E}` and *target* unitary :math:`U` respectively, and :math:`d` is the input dimension of the channel. Args: channel (Operator or QuantumChannel): input quantum channel. target (Operator or QuantumChannel or None): target quantum channel. If `None` target is the identity operator [Default: None]. require_cp (bool): require channel to be completely-positive [Default: True]. require_tp (bool): require channel to be trace-preserving [Default: False]. Returns: float: The process fidelity :math:`F_{\text{pro}}`. Raises: QiskitError: if the channel and target do not have the same dimensions. QiskitError: if the channel and target are not completely-positive (with ``require_cp=True``) or not trace-preserving (with ``require_tp=True``). """ # Format inputs channel = _input_formatter(channel, SuperOp, 'process_fidelity', 'channel') target = _input_formatter(target, Operator, 'process_fidelity', 'target') if target: # Validate dimensions if channel.dim != target.dim: raise QiskitError( 'Input quantum channel and target unitary must have the same ' 'dimensions ({} != {}).'.format(channel.dim, target.dim)) # Validate complete-positivity and trace-preserving for label, chan in [('Input', channel), ('Target', target)]: if isinstance(chan, Operator) and (require_cp or require_tp): is_unitary = chan.is_unitary() # Validate as unitary if require_cp and not is_unitary: raise QiskitError( '{} channel is not completely-positive'.format(label)) if require_tp and not is_unitary: raise QiskitError( '{} channel is not trace-preserving'.format(label)) elif chan is not None: # Validate as QuantumChannel if require_cp and not chan.is_cp(): raise QiskitError( '{} channel is not completely-positive'.format(label)) if require_tp and not chan.is_tp(): raise QiskitError( '{} channel is not trace-preserving'.format(label)) if isinstance(target, Operator): # Compute fidelity with unitary target by applying the inverse # to channel and computing fidelity with the identity channel = channel @ target.adjoint() target = None input_dim, _ = channel.dim if target is None: # Compute process fidelity with identity channel if isinstance(channel, Operator): # |Tr[U]/dim| ** 2 fid = np.abs(np.trace(channel.data) / input_dim)**2 else: # Tr[S] / (dim ** 2) fid = np.trace(SuperOp(channel).data) / (input_dim**2) return float(np.real(fid)) # For comparing two non-unitary channels we compute the state fidelity of # the normalized Choi-matrices. This is equivalent to the previous definition # when the target is a unitary channel. state1 = DensityMatrix(Choi(channel).data / input_dim) state2 = DensityMatrix(Choi(target).data / input_dim) return state_fidelity(state1, state2, validate=False)
def op(self): """Returns the Instruction object corresponding to the op for the node else None""" if 'type' not in self.data_dict or self.data_dict['type'] != 'op': raise QiskitError("The node %s is not an op node" % (str(self))) return self.data_dict.get('op')
def __init__( self, name: str, instructions: Optional[Sequence[Instruction]] = None, default_povms: Optional[Sequence[POVM]] = None, qubit_povms: Optional[Dict[int, Sequence[POVM]]] = None, ): """Initialize a fitter preparation basis. Args: name: a name to identity the basis. instructions: list of instructions for rotating a desired measurement basis to the standard :math:`Z^{\\otimes n}` computational basis measurement. default_povms: Optional, list if positive operators valued measures (POVM) for of the measurement basis instructions. A POVM can be input as a list of effects (Statevector or DensityMatrix) for each possible measurement outcome of that basis, or as a single QuantumChannel. For the channel case the effects will be calculated by evolving the computation basis states by the adjoint of the channel. If None the input instructions will be used as the POVM channel. qubit_povms: Optional, a dict with physical qubit keys and a list of POVMs corresponding to each basis measurement instruction for the specific qubit. The default POVMs will be used for any qubits not specified in this dict. Raises: QiskitError: if the input instructions or POVMs are not valid, or if no instructions or POVMs are provided. """ if instructions is None and default_povms is None and qubit_povms is None: raise QiskitError( "LocalMeasurementBasis must define at least one of instructions, " "default_povms, or qubit_povms." ) super().__init__(name) # Internal variables self._instructions = tuple() self._size = None self._default_povms = None self._default_num_outcomes = None self._default_dim = None self._qubit_povms = {} self._qubit_num_outcomes = {} self._qubit_dim = {} self._qubits = set() self._custom_defaults = True # Format instructions so compatible types can be converted to # Instruction instances. if instructions is not None: self._instructions = _format_instructions(instructions) self._size = len(self._instructions) if default_povms is None: default_povms = instructions self._custom_defaults = False # Format default POVMs if default_povms is not None: self._default_povms = _format_povms(default_povms) self._default_num_outcomes = len(self._default_povms[0]) self._default_dim = self._default_povms[0][0].shape[0] if self._size is None: self._size = len(self._default_povms) elif len(self._default_povms) != self._size: raise QiskitError("Number of instructions and number of states must be equal.") if any(len(povm) != self._default_num_outcomes for povm in self._default_povms): raise QiskitError( "LocalMeasurementBasis default POVM elements must all have " "the same number of outcomes." ) # Format qubit POVMS qubit_povms = qubit_povms or {} for qubit, povms in qubit_povms.items(): f_povms = _format_povms(povms) num_outcomes = len(f_povms[0]) if any(len(povm) != num_outcomes for povm in f_povms): raise QiskitError( "LocalMeasurementBasis POVM elements must all have the " "same number of outcomes." ) self._qubit_povms[qubit] = f_povms self._qubit_num_outcomes[qubit] = num_outcomes self._qubit_dim[qubit] = f_povms[0][0].shape[0] self._qubits.add(qubit) # Pseudo hash value to make basis hashable for LRU cached functions self._hash = hash( ( type(self), self._name, self._size, self._default_dim, self._default_num_outcomes, self._custom_defaults, tuple(self._qubits), tuple(self._qubit_dim.values()), tuple(self._qubit_num_outcomes.values()), (type(i) for i in self._instructions), ) )
def from_labels(cls, labels): r"""Construct a StabilizerTable from a list of Pauli stabilizer strings. Pauli Stabilizer string labels are Pauli strings with an optional ``"+"`` or ``"-"`` character. If there is no +/-sign a + phase is used by default. .. list-table:: Stabilizer Representations :header-rows: 1 * - Label - Phase - Symplectic - Matrix - Pauli * - ``"+I"`` - 0 - :math:`[0, 0]` - :math:`\begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix}` - :math:`I` * - ``"-I"`` - 1 - :math:`[0, 0]` - :math:`\begin{bmatrix} -1 & 0 \\ 0 & -1 \end{bmatrix}` - :math:`-I` * - ``"X"`` - 0 - :math:`[1, 0]` - :math:`\begin{bmatrix} 0 & 1 \\ 1 & 0 \end{bmatrix}` - :math:`X` * - ``"-X"`` - 1 - :math:`[1, 0]` - :math:`\begin{bmatrix} 0 & -1 \\ -1 & 0 \end{bmatrix}` - :math:`-X` * - ``"Y"`` - 0 - :math:`[1, 1]` - :math:`\begin{bmatrix} 0 & 1 \\ -1 & 0 \end{bmatrix}` - :math:`iY` * - ``"-Y"`` - 1 - :math:`[1, 1]` - :math:`\begin{bmatrix} 0 & -1 \\ 1 & 0 \end{bmatrix}` - :math:`-iY` * - ``"Z"`` - 0 - :math:`[0, 1]` - :math:`\begin{bmatrix} 1 & 0 \\ 0 & -1 \end{bmatrix}` - :math:`Z` * - ``"-Z"`` - 1 - :math:`[0, 1]` - :math:`\begin{bmatrix} -1 & 0 \\ 0 & 1 \end{bmatrix}` - :math:`-Z` Args: labels (list): Pauli stabilizer string label(es). Returns: StabilizerTable: the constructed StabilizerTable. Raises: QiskitError: If the input list is empty or contains invalid Pauli stabilizer strings. """ if isinstance(labels, str): labels = [labels] n_paulis = len(labels) if n_paulis == 0: raise QiskitError("Input Pauli list is empty.") # Get size from first Pauli pauli, phase = cls._from_label(labels[0]) table = np.zeros((n_paulis, len(pauli)), dtype=bool) phases = np.zeros(n_paulis, dtype=bool) table[0], phases[0] = pauli, phase for i in range(1, n_paulis): table[i], phases[i] = cls._from_label(labels[i]) return cls(table, phases)
def compose(self, other, qargs=None, front=False): """Return the compose output product of two tables. This returns the combination of the compose product of all stabilizers in the current table with all stabilizers in the other table. The individual stabilizer compose product is given by +----------------------+----+----+----+----+ | :code:`A.compose(B)` | I | X | Y | Z | +======================+====+====+====+====+ | **I** | I | X | Y | Z | +----------------------+----+----+----+----+ | **X** | X | I | Z | -Y | +----------------------+----+----+----+----+ | **Y** | Y | -Z | -I | X | +----------------------+----+----+----+----+ | **Z** | Z | Y | -X | I | +----------------------+----+----+----+----+ If `front=True` the composition will be given by the :meth:`dot` method. **Example** .. jupyter-execute:: from qiskit.quantum_info.operators import StabilizerTable current = StabilizerTable.from_labels(['+I', '-X']) other = StabilizerTable.from_labels(['+X', '-Z']) print(current.compose(other)) Args: other (StabilizerTable): another StabilizerTable. qargs (None or list): qubits to apply compose product on (Default: None). front (bool): If True use `dot` composition method (default: False). Returns: StabilizerTable: the compose outer product table. Raises: QiskitError: if other cannot be converted to a StabilizerTable. """ if qargs is None: qargs = getattr(other, 'qargs', None) if not isinstance(other, StabilizerTable): other = StabilizerTable(other) if qargs is None and other.num_qubits != self.num_qubits: raise QiskitError( "other StabilizerTable must be on the same number of qubits.") if qargs and other.num_qubits != len(qargs): raise QiskitError( "Number of qubits in the other StabilizerTable does not match qargs." ) # Stack X and Z blocks for output size x1, x2 = self._block_stack(self.X, other.X) z1, z2 = self._block_stack(self.Z, other.Z) phase1, phase2 = self._block_stack(self.phase, other.phase) if qargs is not None: ret_x, ret_z = x1.copy(), z1.copy() x1 = x1[:, qargs] z1 = z1[:, qargs] ret_x[:, qargs] = x1 ^ x2 ret_z[:, qargs] = z1 ^ z2 pauli = np.hstack([ret_x, ret_z]) else: pauli = np.hstack((x1 ^ x2, z1 ^ z2)) # We pick up a minus sign for products: # Y.Y = -I, X.Y = -Z, Y.Z = -X, Z.X = -Y if front: minus = (x1 & z2 & (x2 | z1)) | (~x1 & x2 & z1 & ~z2) else: minus = (x2 & z1 & (x1 | z2)) | (~x2 & x1 & z2 & ~z1) phase_shift = np.array(np.sum(minus, axis=1) % 2, dtype=bool) phase = phase_shift ^ phase1 ^ phase2 return StabilizerTable(pauli, phase)
def run(self, dag): """Run the Unroller pass on `dag`. Args: dag (DAGCircuit): input dag Raises: QiskitError: if unable to unroll given the basis due to undefined decomposition rules (such as a bad basis) or excessive recursion. Returns: DAGCircuit: output unrolled dag """ if self.basis is None: return dag # Walk through the DAG and expand each non-basis node basic_insts = ['measure', 'reset', 'barrier', 'snapshot', 'delay'] for node in dag.op_nodes(): if node.op._directive: continue if node.name in basic_insts: # TODO: this is legacy behavior.Basis_insts should be removed that these # instructions should be part of the device-reported basis. Currently, no # backend reports "measure", for example. continue if node.name in self.basis: # If already a base, ignore. if isinstance(node.op, ControlledGate) and node.op._open_ctrl: pass else: continue # TODO: allow choosing other possible decompositions try: phase = node.op.definition.global_phase rule = node.op.definition.data except (TypeError, AttributeError) as err: raise QiskitError(f'Error decomposing node of instruction \'{node.name}\': ' f'{err}. Unable to define instruction \'{node.name}\' in the' f' given basis.') from err # Isometry gates definitions can have widths smaller than that of the # original gate, in which case substitute_node will raise. Fall back # to substitute_node_with_dag if an the width of the definition is # different that the width of the node. while rule and len(rule) == 1 and len(node.qargs) == len(rule[0][1]) == 1: if rule[0][0].name in self.basis: dag.global_phase += phase dag.substitute_node(node, rule[0][0], inplace=True) break try: phase += rule[0][0].definition.global_phase rule = rule[0][0].definition.data except (TypeError, AttributeError) as err: raise QiskitError(f'Error decomposing node of instruction \'{node.name}\': ' f'{err}. Unable to define instruction \'{rule[0][0].name}\'' f' in the given basis.') from err else: if not rule: if rule == []: # empty node dag.remove_op_node(node) dag.global_phase += phase continue # opaque node raise QiskitError("Cannot unroll the circuit to the given basis, %s. " "No rule to expand instruction %s." % (str(self.basis), node.op.name)) decomposition = circuit_to_dag(node.op.definition) unrolled_dag = self.run(decomposition) # recursively unroll ops dag.substitute_node_with_dag(node, unrolled_dag) return dag
def op(self): """Returns the Instruction object corresponding to the op for the node, else None""" if not self.type or self.type != 'op': raise QiskitError("The node %s is not an op node" % (str(self))) return self._op
def plot_circuit_layout(circuit, backend, view='virtual'): """Plot the layout of a circuit transpiled for a given target backend. Args: circuit (QuantumCircuit): Input quantum circuit. backend (BaseBackend): Target backend. view (str): Layout view: either 'virtual' or 'physical'. Returns: Figure: A matplotlib figure showing layout. Raises: QiskitError: Invalid view type given. VisualizationError: Circuit has no layout attribute. Example: .. jupyter-execute:: :hide-code: :hide-output: from qiskit.test.ibmq_mock import mock_get_backend mock_get_backend('FakeVigo') .. jupyter-execute:: import numpy as np from qiskit import QuantumCircuit, IBMQ, transpile from qiskit.visualization import plot_histogram, plot_gate_map, plot_circuit_layout from qiskit.tools.monitor import job_monitor import matplotlib.pyplot as plt %matplotlib inline IBMQ.load_account() ghz = QuantumCircuit(3, 3) ghz.h(0) for idx in range(1,3): ghz.cx(0,idx) ghz.measure(range(3), range(3)) provider = IBMQ.get_provider(hub='ibm-q') backend = provider.get_backend('ibmq_vigo') new_circ_lv3 = transpile(ghz, backend=backend, optimization_level=3) plot_circuit_layout(new_circ_lv3, backend) """ if circuit._layout is None: raise QiskitError('Circuit has no layout. ' 'Perhaps it has not been transpiled.') n_qubits = backend.configuration().n_qubits qubits = [] qubit_labels = [None] * n_qubits if view == 'virtual': for key, val in circuit._layout.get_virtual_bits().items(): if key.register.name != 'ancilla': qubits.append(val) qubit_labels[val] = key.index elif view == 'physical': for key, val in circuit._layout.get_physical_bits().items(): if val.register.name != 'ancilla': qubits.append(key) qubit_labels[key] = key else: raise VisualizationError("Layout view must be 'virtual' or 'physical'.") qcolors = ['#648fff'] * n_qubits for k in qubits: qcolors[k] = 'k' cmap = backend.configuration().coupling_map lcolors = ['#648fff'] * len(cmap) for idx, edge in enumerate(cmap): if edge[0] in qubits and edge[1] in qubits: lcolors[idx] = 'k' fig = plot_gate_map(backend, qubit_color=qcolors, qubit_labels=qubit_labels, line_color=lcolors) return fig
def _assemble_circuit( circuit: QuantumCircuit, run_config: RunConfig ) -> Tuple[QasmQobjExperiment, Optional[PulseLibrary]]: """Assemble one circuit. Args: circuit: circuit to assemble run_config: configuration of the runtime environment Returns: One experiment for the QasmQobj, and pulse library for pulse gates (which could be None) Raises: QiskitError: when the circuit has unit other than 'dt'. """ if circuit.unit != "dt": raise QiskitError( f"Unable to assemble circuit with unit '{circuit.unit}', which must be 'dt'." ) # header data num_qubits = 0 memory_slots = 0 qubit_labels = [] clbit_labels = [] qreg_sizes = [] creg_sizes = [] for qreg in circuit.qregs: qreg_sizes.append([qreg.name, qreg.size]) for j in range(qreg.size): qubit_labels.append([qreg.name, j]) num_qubits += qreg.size for creg in circuit.cregs: creg_sizes.append([creg.name, creg.size]) for j in range(creg.size): clbit_labels.append([creg.name, j]) memory_slots += creg.size qubit_indices = {qubit: idx for idx, qubit in enumerate(circuit.qubits)} clbit_indices = {clbit: idx for idx, clbit in enumerate(circuit.clbits)} # TODO: why do we need creq_sizes and qreg_sizes in header # TODO: we need to rethink memory_slots as they are tied to classical bit metadata = circuit.metadata if metadata is None: metadata = {} header = QobjExperimentHeader( qubit_labels=qubit_labels, n_qubits=num_qubits, qreg_sizes=qreg_sizes, clbit_labels=clbit_labels, memory_slots=memory_slots, creg_sizes=creg_sizes, name=circuit.name, global_phase=float(circuit.global_phase), metadata=metadata, ) # TODO: why do we need n_qubits and memory_slots in both the header and the config config = QasmQobjExperimentConfig(n_qubits=num_qubits, memory_slots=memory_slots) calibrations, pulse_library = _assemble_pulse_gates(circuit, run_config) if calibrations: config.calibrations = calibrations # Convert conditionals from QASM-style (creg ?= int) to qobj-style # (register_bit ?= 1), by assuming device has unlimited register slots # (supported only for simulators). Map all measures to a register matching # their clbit_index, create a new register slot for every conditional gate # and add a bfunc to map the creg=val mask onto the gating register bit. is_conditional_experiment = any( getattr(instruction.operation, "condition", None) for instruction in circuit.data ) max_conditional_idx = 0 instructions = [] for op_context in circuit.data: instruction = op_context.operation.assemble() # Add register attributes to the instruction qargs = op_context.qubits cargs = op_context.clbits if qargs: instruction.qubits = [qubit_indices[qubit] for qubit in qargs] if cargs: instruction.memory = [clbit_indices[clbit] for clbit in cargs] # If the experiment has conditional instructions, assume every # measurement result may be needed for a conditional gate. if instruction.name == "measure" and is_conditional_experiment: instruction.register = [clbit_indices[clbit] for clbit in cargs] # To convert to a qobj-style conditional, insert a bfunc prior # to the conditional instruction to map the creg ?= val condition # onto a gating register bit. if hasattr(instruction, "_condition"): ctrl_reg, ctrl_val = instruction._condition mask = 0 val = 0 if isinstance(ctrl_reg, Clbit): mask = 1 << clbit_indices[ctrl_reg] val = (ctrl_val & 1) << clbit_indices[ctrl_reg] else: for clbit in clbit_indices: if clbit in ctrl_reg: mask |= 1 << clbit_indices[clbit] val |= ((ctrl_val >> list(ctrl_reg).index(clbit)) & 1) << clbit_indices[ clbit ] conditional_reg_idx = memory_slots + max_conditional_idx conversion_bfunc = QasmQobjInstruction( name="bfunc", mask="0x%X" % mask, relation="==", val="0x%X" % val, register=conditional_reg_idx, ) instructions.append(conversion_bfunc) instruction.conditional = conditional_reg_idx max_conditional_idx += 1 # Delete condition attribute now that we have replaced it with # the conditional and bfunc del instruction._condition instructions.append(instruction) return ( QasmQobjExperiment(instructions=instructions, header=header, config=config), pulse_library, )
def plot_gate_map(backend, figsize=None, plot_directed=False, label_qubits=True, qubit_size=24, line_width=4, font_size=12, qubit_color=None, qubit_labels=None, line_color=None, font_color='w', ax=None): """Plots the gate map of a device. Args: backend (BaseBackend): A backend instance, figsize (tuple): Output figure size (wxh) in inches. plot_directed (bool): Plot directed coupling map. label_qubits (bool): Label the qubits. qubit_size (float): Size of qubit marker. line_width (float): Width of lines. font_size (int): Font size of qubit labels. qubit_color (list): A list of colors for the qubits qubit_labels (list): A list of qubit labels line_color (list): A list of colors for each line from coupling_map. font_color (str): The font color for the qubit labels. ax (Axes): A Matplotlib axes instance. Returns: Figure: A Matplotlib figure instance. Raises: QiskitError: if tried to pass a simulator. ImportError: if matplotlib not installed. Example: .. jupyter-execute:: :hide-code: :hide-output: from qiskit.test.ibmq_mock import mock_get_backend mock_get_backend('FakeVigo') .. jupyter-execute:: from qiskit import QuantumCircuit, execute, IBMQ from qiskit.visualization import plot_gate_map %matplotlib inline provider = IBMQ.load_account() accountProvider = IBMQ.get_provider(hub='ibm-q') backend = accountProvider.get_backend('ibmq_vigo') plot_gate_map(backend) """ if not HAS_MATPLOTLIB: raise ImportError('Must have Matplotlib installed.') if backend.configuration().simulator: raise QiskitError('Requires a device backend, not simulator.') input_axes = False if ax: input_axes = True mpl_data = {} mpl_data[1] = [[0, 0]] mpl_data[20] = [[0, 0], [0, 1], [0, 2], [0, 3], [0, 4], [1, 0], [1, 1], [1, 2], [1, 3], [1, 4], [2, 0], [2, 1], [2, 2], [2, 3], [2, 4], [3, 0], [3, 1], [3, 2], [3, 3], [3, 4]] mpl_data[15] = [[0, 0], [0, 1], [0, 2], [0, 3], [0, 4], [0, 5], [0, 6], [1, 7], [1, 6], [1, 5], [1, 4], [1, 3], [1, 2], [1, 1], [1, 0]] mpl_data[16] = [[1, 0], [0, 0], [0, 1], [0, 2], [0, 3], [0, 4], [0, 5], [0, 6], [0, 7], [1, 7], [1, 6], [1, 5], [1, 4], [1, 3], [1, 2], [1, 1]] mpl_data[5] = [[1, 0], [0, 1], [1, 1], [1, 2], [2, 1]] mpl_data[28] = [[0, 2], [0, 3], [0, 4], [0, 5], [0, 6], [1, 2], [1, 6], [2, 0], [2, 1], [2, 2], [2, 3], [2, 4], [2, 5], [2, 6], [2, 7], [2, 8], [3, 0], [3, 4], [3, 8], [4, 0], [4, 1], [4, 2], [4, 3], [4, 4], [4, 5], [4, 6], [4, 7], [4, 8]] mpl_data[53] = [[0, 2], [0, 3], [0, 4], [0, 5], [0, 6], [1, 2], [1, 6], [2, 0], [2, 1], [2, 2], [2, 3], [2, 4], [2, 5], [2, 6], [2, 7], [2, 8], [3, 0], [3, 4], [3, 8], [4, 0], [4, 1], [4, 2], [4, 3], [4, 4], [4, 5], [4, 6], [4, 7], [4, 8], [5, 2], [5, 6], [6, 0], [6, 1], [6, 2], [6, 3], [6, 4], [6, 5], [6, 6], [6, 7], [6, 8], [7, 0], [7, 4], [7, 8], [8, 0], [8, 1], [8, 2], [8, 3], [8, 4], [8, 5], [8, 6], [8, 7], [8, 8], [9, 2], [9, 6]] config = backend.configuration() n_qubits = config.n_qubits cmap = config.coupling_map if qubit_labels is None: qubit_labels = list(range(n_qubits)) else: if len(qubit_labels) != n_qubits: raise QiskitError('Length of qubit labels ' 'does not equal number ' 'of qubits.') if n_qubits in mpl_data.keys(): grid_data = mpl_data[n_qubits] else: if not input_axes: fig, ax = plt.subplots(figsize=(5, 5)) # pylint: disable=invalid-name ax.axis('off') return fig x_max = max([d[1] for d in grid_data]) y_max = max([d[0] for d in grid_data]) max_dim = max(x_max, y_max) if figsize is None: if n_qubits == 1 or (x_max / max_dim > 0.33 and y_max / max_dim > 0.33): figsize = (5, 5) else: figsize = (9, 3) if ax is None: fig, ax = plt.subplots(figsize=figsize) # pylint: disable=invalid-name ax.axis('off') # set coloring if qubit_color is None: qubit_color = ['#648fff'] * config.n_qubits if line_color is None: line_color = ['#648fff'] * len(cmap) if cmap else [] # Add lines for couplings if n_qubits != 1: for ind, edge in enumerate(cmap): is_symmetric = False if edge[::-1] in cmap: is_symmetric = True y_start = grid_data[edge[0]][0] x_start = grid_data[edge[0]][1] y_end = grid_data[edge[1]][0] x_end = grid_data[edge[1]][1] if is_symmetric: if y_start == y_end: x_end = (x_end - x_start) / 2 + x_start elif x_start == x_end: y_end = (y_end - y_start) / 2 + y_start else: x_end = (x_end - x_start) / 2 + x_start y_end = (y_end - y_start) / 2 + y_start ax.add_artist(plt.Line2D([x_start, x_end], [-y_start, -y_end], color=line_color[ind], linewidth=line_width, zorder=0)) if plot_directed: dx = x_end - x_start # pylint: disable=invalid-name dy = y_end - y_start # pylint: disable=invalid-name if is_symmetric: x_arrow = x_start + dx * 0.95 y_arrow = -y_start - dy * 0.95 dx_arrow = dx * 0.01 dy_arrow = -dy * 0.01 head_width = 0.15 else: x_arrow = x_start + dx * 0.5 y_arrow = -y_start - dy * 0.5 dx_arrow = dx * 0.2 dy_arrow = -dy * 0.2 head_width = 0.2 ax.add_patch(mpatches.FancyArrow(x_arrow, y_arrow, dx_arrow, dy_arrow, head_width=head_width, length_includes_head=True, edgecolor=None, linewidth=0, facecolor=line_color[ind], zorder=1)) # Add circles for qubits for var, idx in enumerate(grid_data): _idx = [idx[1], -idx[0]] width = _GraphDist(qubit_size, ax, True) height = _GraphDist(qubit_size, ax, False) ax.add_artist(mpatches.Ellipse( _idx, width, height, color=qubit_color[var], zorder=1)) if label_qubits: ax.text(*_idx, s=qubit_labels[var], horizontalalignment='center', verticalalignment='center', color=font_color, size=font_size, weight='bold') ax.set_xlim([-1, x_max + 1]) ax.set_ylim([-(y_max + 1), 1]) if not input_axes: if get_backend() in ['module://ipykernel.pylab.backend_inline', 'nbAgg']: plt.close(fig) return fig return None
def from_label(cls, label): """Return a tensor product of Pauli X,Y,Z eigenstates. .. list-table:: Single-qubit state labels :header-rows: 1 * - Label - Statevector * - ``"0"`` - :math:`[1, 0]` * - ``"1"`` - :math:`[0, 1]` * - ``"+"`` - :math:`[1 / \\sqrt{2}, 1 / \\sqrt{2}]` * - ``"-"`` - :math:`[1 / \\sqrt{2}, -1 / \\sqrt{2}]` * - ``"r"`` - :math:`[1 / \\sqrt{2}, i / \\sqrt{2}]` * - ``"l"`` - :math:`[1 / \\sqrt{2}, -i / \\sqrt{2}]` Args: label (string): a eigenstate string ket label (see table for allowed values). Returns: Statevector: The N-qubit basis state density matrix. Raises: QiskitError: if the label contains invalid characters, or the length of the label is larger than an explicitly specified num_qubits. """ # Check label is valid if re.match(r'^[01rl\-+]+$', label) is None: raise QiskitError('Label contains invalid characters.') # We can prepare Z-eigenstates by converting the computational # basis bit-string to an integer and preparing that unit vector # However, for X-basis states, we will prepare a Z-eigenstate first # then apply Hadamard gates to rotate 0 and 1s to + and -. z_label = label xy_states = False if re.match('^[01]+$', label) is None: # We have X or Y eigenstates so replace +,r with 0 and # -,l with 1 and prepare the corresponding Z state xy_states = True z_label = z_label.replace('+', '0') z_label = z_label.replace('r', '0') z_label = z_label.replace('-', '1') z_label = z_label.replace('l', '1') # Initialize Z eigenstate vector num_qubits = len(label) data = np.zeros(1 << num_qubits, dtype=complex) pos = int(z_label, 2) data[pos] = 1 state = Statevector(data) if xy_states: # Apply hadamards to all qubits in X eigenstates x_mat = np.array([[1, 1], [1, -1]], dtype=complex) / np.sqrt(2) # Apply S.H to qubits in Y eigenstates y_mat = np.dot(np.diag([1, 1j]), x_mat) for qubit, char in enumerate(reversed(label)): if char in ['+', '-']: state = state.evolve(x_mat, qargs=[qubit]) elif char in ['r', 'l']: state = state.evolve(y_mat, qargs=[qubit]) return state
def _assemble_experiments( schedules: List[pulse.Schedule], 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 frequency setting is specified. If the frequency is specified, ' 'it should be configured the same for all schedules, configured for each ' 'schedule, or a list of frequencies should be provided for a single ' 'frequency sweep schedule.') instruction_converter = getattr(run_config, 'instruction_converter', converters.InstructionToQobjConverter) instruction_converter = instruction_converter(qobj.PulseQobjInstruction, **run_config.to_dict()) schedules = [ sched if isinstance(sched, pulse.Schedule) else pulse.Schedule(sched) for sched in schedules ] compressed_schedules = transforms.compress_pulses(schedules) user_pulselib = {} experiments = [] for idx, schedule in enumerate(compressed_schedules): qobj_instructions, max_memory_slot = _assemble_instructions( schedule, instruction_converter, run_config, user_pulselib) # 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=schedule.name or 'Experiment-%d' % idx) 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 circuit_to_gate(circuit, parameter_map=None): """Build a ``Gate`` object from a ``QuantumCircuit``. The gate is anonymous (not tied to a named quantum register), and so can be inserted into another circuit. The gate will have the same string name as the circuit. Args: circuit (QuantumCircuit): the input circuit. parameter_map (dict): For parameterized circuits, a mapping from parameters in the circuit to parameters to be used in the gate. If None, existing circuit parameters will also parameterize the Gate. Raises: QiskitError: if circuit is non-unitary or if parameter_map is not compatible with circuit Return: Gate: a Gate equivalent to the action of the input circuit. Upon decomposition, this gate will yield the components comprising the original circuit. """ if circuit.clbits: raise QiskitError('Circuit with classical bits cannot be converted ' 'to gate.') for inst, _, _ in circuit.data: if not isinstance(inst, Gate): raise QiskitError('One or more instructions in this instruction ' 'cannot be converted to a gate') if parameter_map is None: parameter_dict = {p: p for p in circuit.parameters} else: parameter_dict = circuit._unroll_param_dict(parameter_map) if parameter_dict.keys() != circuit.parameters: raise QiskitError(('parameter_map should map all circuit parameters. ' 'Circuit parameters: {}, parameter_map: {}').format( circuit.parameters, parameter_dict)) gate = Gate(name=circuit.name, num_qubits=sum([qreg.size for qreg in circuit.qregs]), params=sorted(parameter_dict.values(), key=lambda p: p.name)) gate.condition = None def find_bit_position(bit): """find the index of a given bit (Register, int) within a flat ordered list of bits of the circuit """ if isinstance(bit, Qubit): ordered_regs = circuit.qregs else: ordered_regs = circuit.cregs reg_index = ordered_regs.index(bit.register) return sum([reg.size for reg in ordered_regs[:reg_index]]) + bit.index target = circuit.copy() target._substitute_parameters(parameter_dict) definition = target.data if gate.num_qubits > 0: q = QuantumRegister(gate.num_qubits, 'q') # The 3rd parameter in the output tuple) is hard coded to [] because # Gate objects do not have cregs set and we've verified that all # instructions are gates definition = list( map( lambda x: (x[0], list(map(lambda y: q[find_bit_position(y)], x[1])), []), definition)) gate.definition = definition return gate
def __init__(self, data, dims=None): """Initialize a density matrix object. Args: data (np.ndarray or list or matrix_like or QuantumCircuit or qiskit.circuit.Instruction): A statevector, quantum instruction or an object with a ``to_operator`` or ``to_matrix`` method from which the density matrix can be constructed. If a vector the density matrix is constructed as the projector of that vector. If a quantum instruction, the density matrix is constructed by assuming all qubits are initialized in the zero state. dims (int or tuple or list): Optional. The subsystem dimension of the state (See additional information). Raises: QiskitError: if input data is not valid. Additional Information: The ``dims`` kwarg can be None, an integer, or an iterable of integers. * ``Iterable`` -- the subsystem dimensions are the values in the list with the total number of subsystems given by the length of the list. * ``Int`` or ``None`` -- the leading dimension of the input matrix specifies the total dimension of the density matrix. If it is a power of two the state will be initialized as an N-qubit state. If it is not a power of two the state will have a single d-dimensional subsystem. """ if isinstance(data, (list, np.ndarray)): # Finally we check if the input is a raw matrix in either a # python list or numpy array format. self._data = np.asarray(data, dtype=complex) elif isinstance(data, (QuantumCircuit, Instruction)): # If the data is a circuit or an instruction use the classmethod # to construct the DensityMatrix object self._data = DensityMatrix.from_instruction(data)._data elif hasattr(data, 'to_operator'): # If the data object has a 'to_operator' attribute this is given # higher preference than the 'to_matrix' method for initializing # an Operator object. op = data.to_operator() self._data = op.data if dims is None: dims = op.output_dims() elif hasattr(data, 'to_matrix'): # If no 'to_operator' attribute exists we next look for a # 'to_matrix' attribute to a matrix that will be cast into # a complex numpy matrix. self._data = np.asarray(data.to_matrix(), dtype=complex) else: raise QiskitError("Invalid input data format for DensityMatrix") # Convert statevector into a density matrix ndim = self._data.ndim shape = self._data.shape if ndim == 2 and shape[0] == shape[1]: pass # We good elif ndim == 1: self._data = np.outer(self._data, np.conj(self._data)) elif ndim == 2 and shape[1] == 1: self._data = np.reshape(self._data, shape[0]) else: raise QiskitError( "Invalid DensityMatrix input: not a square matrix.") super().__init__(op_shape=OpShape.auto( shape=self._data.shape, dims_l=dims, dims_r=dims))
def circuit_to_gate(circuit, parameter_map=None, equivalence_library=None, label=None): """Build a :class:`.Gate` object from a :class:`.QuantumCircuit`. The gate is anonymous (not tied to a named quantum register), and so can be inserted into another circuit. The gate will have the same string name as the circuit. Args: circuit (QuantumCircuit): the input circuit. parameter_map (dict): For parameterized circuits, a mapping from parameters in the circuit to parameters to be used in the gate. If None, existing circuit parameters will also parameterize the Gate. equivalence_library (EquivalenceLibrary): Optional equivalence library where the converted gate will be registered. label (str): Optional gate label. Raises: QiskitError: if circuit is non-unitary or if parameter_map is not compatible with circuit Return: Gate: a Gate equivalent to the action of the input circuit. Upon decomposition, this gate will yield the components comprising the original circuit. """ # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit if circuit.clbits: raise QiskitError( "Circuit with classical bits cannot be converted to gate.") for inst, _, _ in circuit.data: if not isinstance(inst, Gate): raise QiskitError( ("One or more instructions cannot be converted to" ' a gate. "{}" is not a gate instruction').format(inst.name)) if parameter_map is None: parameter_dict = {p: p for p in circuit.parameters} else: parameter_dict = circuit._unroll_param_dict(parameter_map) if parameter_dict.keys() != circuit.parameters: raise QiskitError(("parameter_map should map all circuit parameters. " "Circuit parameters: {}, parameter_map: {}").format( circuit.parameters, parameter_dict)) gate = Gate( name=circuit.name, num_qubits=circuit.num_qubits, params=[*parameter_dict.values()], label=label, ) gate.condition = None target = circuit.assign_parameters(parameter_dict, inplace=False) if equivalence_library is not None: equivalence_library.add_equivalence(gate, target) rules = target.data if gate.num_qubits > 0: q = QuantumRegister(gate.num_qubits, "q") qubit_map = {bit: q[idx] for idx, bit in enumerate(circuit.qubits)} # The 3rd parameter in the output tuple) is hard coded to [] because # Gate objects do not have cregs set and we've verified that all # instructions are gates rules = [(inst, [qubit_map[y] for y in qargs], []) for inst, qargs, _ in rules] qc = QuantumCircuit(q, name=gate.name, global_phase=target.global_phase) for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) gate.definition = qc return gate
def _append_circuit(self, circuit, qargs=None): """Update BasePauli inplace by applying a Clifford circuit. Args: circuit (QuantumCircuit or Instruction): the gate or composite gate to apply. qargs (list or None): The qubits to apply gate to. Returns: BasePauli: the updated Pauli. Raises: QiskitError: if input gate cannot be decomposed into Clifford gates. """ if isinstance(circuit, Barrier): return self if qargs is None: qargs = list(range(self.num_qubits)) if isinstance(circuit, QuantumCircuit): gate = circuit.to_instruction() else: gate = circuit # Basis Clifford Gates basis_1q = { 'i': _evolve_i, 'id': _evolve_i, 'iden': _evolve_i, 'x': _evolve_x, 'y': _evolve_y, 'z': _evolve_z, 'h': _evolve_h, 's': _evolve_s, 'sdg': _evolve_sdg, 'sinv': _evolve_sdg } basis_2q = { 'cx': _evolve_cx, 'cz': _evolve_cz, 'cy': _evolve_cy, 'swap': _evolve_swap } # Non-Clifford gates non_clifford = ['t', 'tdg', 'ccx', 'ccz'] if isinstance(gate, str): # Check if gate is a valid Clifford basis gate string if gate not in basis_1q and gate not in basis_2q: raise QiskitError( "Invalid Clifford gate name string {}".format(gate)) name = gate else: # Assume gate is an Instruction name = gate.name # Apply gate if it is a Clifford basis gate if name in non_clifford: raise QiskitError( "Cannot update Pauli with non-Clifford gate {}".format(name)) if name in basis_1q: if len(qargs) != 1: raise QiskitError("Invalid qubits for 1-qubit gate.") return basis_1q[name](self, qargs[0]) if name in basis_2q: if len(qargs) != 2: raise QiskitError("Invalid qubits for 2-qubit gate.") return basis_2q[name](self, qargs[0], qargs[1]) # If not a Clifford basis gate we try to unroll the gate and # raise an exception if unrolling reaches a non-Clifford gate. if gate.definition is None: raise QiskitError('Cannot apply Instruction: {}'.format(gate.name)) if not isinstance(gate.definition, QuantumCircuit): raise QiskitError( '{} instruction definition is {}; expected QuantumCircuit'. format(gate.name, type(gate.definition))) flat_instr = gate.definition bit_indices = { bit: index for bits in [flat_instr.qubits, flat_instr.clbits] for index, bit in enumerate(bits) } for instr, qregs, cregs in flat_instr: if cregs: raise QiskitError( 'Cannot apply Instruction with classical registers: {}'. format(instr.name)) # Get the integer position of the flat register new_qubits = [qargs[bit_indices[tup]] for tup in qregs] self._append_circuit(instr, new_qubits) # Since the individual gate evolution functions don't take mod # of phase we update it at the end self._phase %= 4 return self
def _check_dups(self, qubits): """Raise exception if list of qubits contains duplicates.""" squbits = set(qubits) if len(squbits) != len(qubits): raise QiskitError("duplicate qubit arguments")
def compose(self, other, qargs=None, front=False, inplace=False): """Return the composition of Paulis. Args: a ({cls}): an operator object. b ({cls}): an operator object. qargs (list or None): Optional, qubits to apply dot product on (default: None). inplace (bool): If True update in-place (default: False). Returns: {cls}: The operator a.compose(b) Raises: QiskitError: if number of qubits of other does not match qargs. """.format(cls=type(self).__name__) # Validation if qargs is None and other.num_qubits != self.num_qubits: raise QiskitError( "other {} must be on the same number of qubits.".format( type(self).__name__)) if qargs and other.num_qubits != len(qargs): raise QiskitError( "Number of qubits of the other {} does not match qargs.". format(type(self).__name__)) if other._num_paulis not in [1, self._num_paulis]: raise QiskitError("Incompatible BasePaulis. Second list must " "either have 1 or the same number of Paulis.") # Compute phase shift if qargs is not None: x1, z1 = self._x[:, qargs], self._z[:, qargs] else: x1, z1 = self._x, self._z x2, z2 = other._x, other._z # Get phase shift phase = self._phase + other._phase if front: phase += 2 * np.sum(np.logical_and(x1, z2), axis=1) else: phase += 2 * np.sum(np.logical_and(z1, x2), axis=1) # Update Pauli x = np.logical_xor(x1, x2) z = np.logical_xor(z1, z2) if qargs is None: if not inplace: return BasePauli(z, x, phase) # Inplace update self._x = x self._z = z self._phase = phase return self # Qargs update ret = self if inplace else self.copy() ret._x[:, qargs] = x ret._z[:, qargs] = z ret._phase = np.mod(phase, 4) return ret
def _check_conflicting_argument(**kargs): conflicting_args = [arg for arg, value in kargs.items() if value] if conflicting_args: raise QiskitError( "The parameters pass_manager conflicts with the following " "parameter(s): {}.".format(', '.join(conflicting_args)))
def __init__(self, data=None, num_qubits=None, validate=True): """Initialize a CNOTDihedral operator object. Args: data (CNOTDihedral or QuantumCircuit or ~qiskit.circuit.Instruction): Optional, operator to initialize. num_qubits (int): Optional, initialize an empty CNOTDihedral operator. validate (bool): if True, validates the CNOTDihedral element. Raises: QiskitError: if the type is invalid. QiskitError: if validate=True and the CNOTDihedral element is invalid. """ if num_qubits: # initialize n-qubit identity self._num_qubits = num_qubits # phase polynomial self.poly = SpecialPolynomial(self._num_qubits) # n x n invertible matrix over Z_2 self.linear = np.eye(self._num_qubits, dtype=np.int8) # binary shift, n coefficients in Z_2 self.shift = np.zeros(self._num_qubits, dtype=np.int8) # Initialize from another CNOTDihedral by sharing the underlying # poly, linear and shift elif isinstance(data, CNOTDihedral): self.linear = data.linear self.shift = data.shift self.poly = data.poly # Initialize from ScalarOp as N-qubit identity discarding any global phase elif isinstance(data, ScalarOp): if not data.is_unitary() or set( data._input_dims) != {2} or data.num_qubits is None: raise QiskitError( "Can only initialize from N-qubit identity ScalarOp.") self._num_qubits = data.num_qubits # phase polynomial self.poly = SpecialPolynomial(self._num_qubits) # n x n invertible matrix over Z_2 self.linear = np.eye(self._num_qubits, dtype=np.int8) # binary shift, n coefficients in Z_2 self.shift = np.zeros(self._num_qubits, dtype=np.int8) # Initialize from a QuantumCircuit or Instruction object elif isinstance(data, (QuantumCircuit, Instruction)): self._num_qubits = data.num_qubits elem = self._from_circuit(data) self.poly = elem.poly self.linear = elem.linear self.shift = elem.shift elif isinstance(data, Pauli): self._num_qubits = data.num_qubits elem = self._from_circuit(data.to_instruction()) self.poly = elem.poly self.linear = elem.linear self.shift = elem.shift else: raise QiskitError("Invalid input type for CNOTDihedral class.") # Initialize BaseOperator super().__init__(num_qubits=self._num_qubits) # Validate the CNOTDihedral element if validate and not self._is_valid(): raise QiskitError("Invalid CNOTDihedral element.")
def pulse_drawer(samples, duration, dt=None, interp_method='None', filename=None, interactive=False, dpi=150, nop=1000, size=(6, 5)): """Plot the interpolated envelope of pulse Args: samples (ndarray): Data points of complex pulse envelope. duration (int): Pulse length (number of points). dt (float): Time interval of samples. interp_method (str): Method of interpolation (set `None` for turn off the interpolation). filename (str): Name required to save pulse image. interactive (bool): When set true show the circuit in a new window (this depends on the matplotlib backend being used supporting this). dpi (int): Resolution of saved image. nop (int): Data points for interpolation. size (tuple): Size of figure. Returns: matplotlib.figure: A matplotlib figure object for the pulse envelope. Raises: ImportError: when the output methods requieres non-installed libraries. QiskitError: when invalid interpolation method is specified. """ try: from matplotlib import pyplot as plt except ImportError: raise ImportError('pulse_drawer need matplotlib. ' 'Run "pip install matplotlib" before.') if dt: _dt = dt else: _dt = 1 re_y = np.real(samples) im_y = np.imag(samples) image = plt.figure(figsize=size) ax0 = image.add_subplot(111) if interp_method == 'CubicSpline': # spline interpolation, use mid-point of dt time = np.arange(0, duration + 1) * _dt + 0.5 * _dt cs_ry = CubicSpline(time[:-1], re_y) cs_iy = CubicSpline(time[:-1], im_y) _time = np.linspace(0, duration * _dt, nop) _re_y = cs_ry(_time) _im_y = cs_iy(_time) elif interp_method == 'None': # pseudo-DAC output time = np.arange(0, duration + 1) * _dt _time = np.r_[time[0], np.repeat(time[1:-1], 2), time[-1]] _re_y = np.repeat(re_y, 2) _im_y = np.repeat(im_y, 2) else: raise QiskitError('Invalid interpolation method "%s"' % interp_method) # plot ax0.fill_between(x=_time, y1=_re_y, y2=np.zeros_like(_time), facecolor='red', alpha=0.3, edgecolor='red', linewidth=1.5, label='real part') ax0.fill_between(x=_time, y1=_im_y, y2=np.zeros_like(_time), facecolor='blue', alpha=0.3, edgecolor='blue', linewidth=1.5, label='imaginary part') ax0.set_xlim(0, duration * _dt) ax0.grid(b=True, linestyle='-') ax0.legend(bbox_to_anchor=(0.5, 1.00), loc='lower center', ncol=2, frameon=False, fontsize=14) if filename: image.savefig(filename, dpi=dpi, bbox_inches='tight') plt.close(image) if image and interactive: plt.show(image) return image
def circuit_to_instruction(circuit, parameter_map=None, equivalence_library=None): """Build an ``Instruction`` object from a ``QuantumCircuit``. The instruction is anonymous (not tied to a named quantum register), and so can be inserted into another circuit. The instruction will have the same string name as the circuit. Args: circuit (QuantumCircuit): the input circuit. parameter_map (dict): For parameterized circuits, a mapping from parameters in the circuit to parameters to be used in the instruction. If None, existing circuit parameters will also parameterize the instruction. equivalence_library (EquivalenceLibrary): Optional equivalence library where the converted instruction will be registered. Raises: QiskitError: if parameter_map is not compatible with circuit Return: qiskit.circuit.Instruction: an instruction equivalent to the action of the input circuit. Upon decomposition, this instruction will yield the components comprising the original circuit. Example: .. jupyter-execute:: from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit from qiskit.converters import circuit_to_instruction %matplotlib inline q = QuantumRegister(3, 'q') c = ClassicalRegister(3, 'c') circ = QuantumCircuit(q, c) circ.h(q[0]) circ.cx(q[0], q[1]) circ.measure(q[0], c[0]) circ.rz(0.5, q[1]).c_if(c, 2) circuit_to_instruction(circ) """ # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit if parameter_map is None: parameter_dict = {p: p for p in circuit.parameters} else: parameter_dict = circuit._unroll_param_dict(parameter_map) if parameter_dict.keys() != circuit.parameters: raise QiskitError(('parameter_map should map all circuit parameters. ' 'Circuit parameters: {}, parameter_map: {}').format( circuit.parameters, parameter_dict)) instruction = Instruction( name=circuit.name, num_qubits=sum([qreg.size for qreg in circuit.qregs]), num_clbits=sum([creg.size for creg in circuit.cregs]), params=sorted(parameter_dict.values(), key=lambda p: p.name)) instruction.condition = None def find_bit_position(bit): """find the index of a given bit (Register, int) within a flat ordered list of bits of the circuit """ if isinstance(bit, Qubit): ordered_regs = circuit.qregs else: ordered_regs = circuit.cregs reg_index = ordered_regs.index(bit.register) return sum([reg.size for reg in ordered_regs[:reg_index]]) + bit.index target = circuit.assign_parameters(parameter_dict, inplace=False) if equivalence_library is not None: equivalence_library.add_equivalence(instruction, target) definition = target.data regs = [] if instruction.num_qubits > 0: q = QuantumRegister(instruction.num_qubits, 'q') regs.append(q) if instruction.num_clbits > 0: c = ClassicalRegister(instruction.num_clbits, 'c') regs.append(c) definition = list( map( lambda x: (x[0], list(map(lambda y: q[find_bit_position(y)], x[1])), list(map(lambda y: c[find_bit_position(y)], x[2]))), definition)) # fix condition for rule in definition: condition = rule[0].condition if condition: reg, val = condition if reg.size == c.size: rule[0].condition = (c, val) else: raise QiskitError( 'Cannot convert condition in circuit with ' 'multiple classical registers to instruction') qc = QuantumCircuit(*regs, name=instruction.name) for instr, qargs, cargs in definition: qc._append(instr, qargs, cargs) if circuit.global_phase: qc.global_phase = circuit.global_phase instruction.definition = qc return instruction