def _append_instruction(self, other, qargs=None): """Update the current Statevector by applying an instruction.""" # Try evolving by a matrix operator (unitary-like evolution) mat = Operator._instruction_to_matrix(other) if mat is not None: self._data = self._evolve_operator(Operator(mat), qargs=qargs).data return # Otherwise try evolving by a Superoperator chan = SuperOp._instruction_to_superop(other) if chan is not None: # Evolve current state by the superoperator self._data = chan._evolve(self, qargs=qargs).data return # If the instruction doesn't have a matrix defined we use its # circuit decomposition definition if it exists, otherwise we # cannot compose this gate and raise an error. if other.definition is None: raise QiskitError('Cannot apply Instruction: {}'.format( other.name)) for instr, qregs, cregs in other.definition: if cregs: raise QiskitError( 'Cannot apply instruction with classical registers: {}'. format(instr.name)) # Get the integer position of the flat register if qargs is None: new_qargs = [tup.index for tup in qregs] else: new_qargs = [qargs[tup.index] for tup in qregs] self._append_instruction(instr, qargs=new_qargs)
def _append_instruction(self, other, qargs=None): """Update the current Statevector by applying an instruction.""" from qiskit.circuit.reset import Reset from qiskit.circuit.barrier import Barrier # Try evolving by a matrix operator (unitary-like evolution) mat = Operator._instruction_to_matrix(other) if mat is not None: self._data = self._evolve_operator(Operator(mat), qargs=qargs).data return # Special instruction types if isinstance(other, Reset): self._data = self.reset(qargs)._data return if isinstance(other, Barrier): return # Otherwise try evolving by a Superoperator chan = SuperOp._instruction_to_superop(other) if chan is not None: # Evolve current state by the superoperator self._data = chan._evolve(self, qargs=qargs).data return # If the instruction doesn't have a matrix defined we use its # circuit decomposition definition if it exists, otherwise we # cannot compose this gate and raise an error. if other.definition is None: raise QiskitError(f"Cannot apply Instruction: {other.name}") if not isinstance(other.definition, QuantumCircuit): raise QiskitError( "{} instruction definition is {}; expected QuantumCircuit". format(other.name, type(other.definition))) qubit_indices = { bit: idx for idx, bit in enumerate(other.definition.qubits) } for instruction in other.definition: if instruction.clbits: raise QiskitError( f"Cannot apply instruction with classical bits: {instruction.operation.name}" ) # Get the integer position of the flat register if qargs is None: new_qargs = [qubit_indices[tup] for tup in instruction.qubits] else: new_qargs = [ qargs[qubit_indices[tup]] for tup in instruction.qubits ] self._append_instruction(instruction.operation, qargs=new_qargs)
def __init__(self, data, input_dims=None, output_dims=None): """Initialize a PTM quantum channel operator.""" # 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.array(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._instruction_to_superop(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 ptm = _to_ptm(data.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 n_qubits = int(np.log2(input_dim)) if 2**n_qubits != input_dim: raise QiskitError("Input is not an n-qubit Pauli transfer matrix.") # 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__('PTM', ptm, input_dims, output_dims)
def __init__(self, data, input_dims=None, output_dims=None): """Initialize a quantum channel Chi-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 is not an N-qubit channel or cannot be initialized as a Chi-matrix. Additional Information ---------------------- If the input or output dimensions are None, they will be automatically determined from the input data. The Chi matrix 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)): # Initialize from raw numpy or list matrix. chi_mat = np.array(data, dtype=complex) # Determine input and output dimensions dim_l, dim_r = chi_mat.shape if dim_l != dim_r: raise QiskitError('Invalid Chi-matrix input.') if input_dims: input_dim = np.product(input_dims) if output_dims: output_dim = np.product(input_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 Chi-matrix input.") 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._instruction_to_superop(data) else: # We use the QuantumChannel init transform to intialize # 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 Chi object chi_mat = _to_chi(data.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 n_qubits = int(np.log2(input_dim)) if 2**n_qubits != input_dim: raise QiskitError("Input is not an n-qubit Chi matrix.") # 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__('Chi', chi_mat, input_dims, output_dims)
def __init__(self, data, input_dims=None, output_dims=None): """Initialize a quantum channel Stinespring 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 a list of Kraus matrices. Additional Information ---------------------- If the input or output dimensions are None, they will be automatically determined from the input data. This can fail for the Stinespring operator if the output dimension cannot be automatically determined. """ # If the input is a list or tuple we assume it is a pair of general # Stinespring matrices. If it is a numpy array we assume that it is # a single Stinespring matrix. if isinstance(data, (list, tuple, np.ndarray)): if not isinstance(data, tuple): # Convert single Stinespring set to length 1 tuple stine = (np.array(data, dtype=complex), None) if isinstance(data, tuple) and len(data) == 2: if data[1] is None: stine = (np.array(data[0], dtype=complex), None) else: stine = (np.array(data[0], dtype=complex), np.array(data[1], dtype=complex)) dim_left, dim_right = stine[0].shape # If two Stinespring matrices check they are same shape if stine[1] is not None: if stine[1].shape != (dim_left, dim_right): raise QiskitError("Invalid Stinespring input.") input_dim = dim_right if output_dims: output_dim = np.product(output_dims) else: output_dim = input_dim if dim_left % output_dim != 0: raise QiskitError("Invalid output_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 # convert it to a SuperOp data = SuperOp._instruction_to_superop(data) else: # We use the QuantumChannel init transform to intialize # other objects into a QuantumChannel or Operator object. data = self._init_transformer(data) data = self._init_transformer(data) input_dim, output_dim = data.dim # Now that the input is an operator we convert it to a # Stinespring operator stine = _to_stinespring(data.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) # Initialize either single or general Stinespring if stine[1] is None or (stine[1] == stine[0]).all(): # Standard Stinespring map super().__init__('Stinespring', (stine[0], None), input_dims=input_dims, output_dims=output_dims) else: # General (non-CPTP) Stinespring map super().__init__('Stinespring', stine, input_dims=input_dims, output_dims=output_dims)
def __init__(self, data, input_dims=None, output_dims=None): """Initialize a quantum channel Kraus 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 a list of Kraus matrices. 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 list of Numpy arrays of shape (2**N, 2**N) qubit systems will be used. If the input does not correspond to an N-qubit channel, it will assign a single subsystem with dimension specified by the shape of the input. """ # If the input is a list or tuple we assume it is a list of Kraus # matrices, if it is a numpy array we assume that it is a single Kraus # operator if isinstance(data, (list, tuple, np.ndarray)): # Check if it is a single unitary matrix A for channel: # E(rho) = A * rho * A^\dagger if isinstance(data, np.ndarray) or np.array(data).ndim == 2: # Convert single Kraus op to general Kraus pair kraus = ([np.array(data, dtype=complex)], None) shape = kraus[0][0].shape # Check if single Kraus set [A_i] for channel: # E(rho) = sum_i A_i * rho * A_i^dagger elif isinstance(data, list) and len(data) > 0: # Get dimensions from first Kraus op kraus = [np.array(data[0], dtype=complex)] shape = kraus[0].shape # Iterate over remaining ops and check they are same shape for i in data[1:]: op = np.array(i, dtype=complex) if op.shape != shape: raise QiskitError( "Kraus operators are different dimensions.") kraus.append(op) # Convert single Kraus set to general Kraus pair kraus = (kraus, None) # Check if generalized Kraus set ([A_i], [B_i]) for channel: # E(rho) = sum_i A_i * rho * B_i^dagger elif isinstance(data, tuple) and len(data) == 2 and len(data[0]) > 0: kraus_left = [np.array(data[0][0], dtype=complex)] shape = kraus_left[0].shape for i in data[0][1:]: op = np.array(i, dtype=complex) if op.shape != shape: raise QiskitError( "Kraus operators are different dimensions.") kraus_left.append(op) if data[1] is None: kraus = (kraus_left, None) else: kraus_right = [] for i in data[1]: op = np.array(i, dtype=complex) if op.shape != shape: raise QiskitError( "Kraus operators are different dimensions.") kraus_right.append(op) kraus = (kraus_left, kraus_right) else: raise QiskitError("Invalid input for Kraus channel.") 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._instruction_to_superop(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 Kraus kraus = _to_kraus(data.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() output_dim, input_dim = kraus[0][0].shape # 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) # Initialize either single or general Kraus if kraus[1] is None or np.allclose(kraus[0], kraus[1]): # Standard Kraus map super().__init__('Kraus', (kraus[0], None), input_dims, output_dims) else: # General (non-CPTP) Kraus map super().__init__('Kraus', kraus, input_dims, output_dims)