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._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 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 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.asarray(data, dtype=complex), None) if isinstance(data, tuple) and len(data) == 2: if data[1] is None: stine = (np.asarray(data[0], dtype=complex), None) else: stine = (np.asarray(data[0], dtype=complex), np.asarray(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._init_instruction(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 rep = getattr(data, '_channel_rep', 'Operator') stine = _to_stinespring(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__((stine[0], None), input_dims=input_dims, output_dims=output_dims, channel_rep='Stinespring') else: # General (non-CPTP) Stinespring map super().__init__(stine, input_dims=input_dims, output_dims=output_dims, channel_rep='Stinespring')
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.asarray(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.asarray(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.asarray(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.asarray(data[0][0], dtype=complex)] shape = kraus_left[0].shape for i in data[0][1:]: op = np.asarray(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.asarray(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._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 Kraus rep = getattr(data, '_channel_rep', 'Operator') kraus = _to_kraus(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[0], None), input_dims, output_dims, 'Kraus') else: # General (non-CPTP) Kraus map super().__init__(kraus, input_dims, output_dims, 'Kraus')
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.") 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 # 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) op_shape = data._op_shape output_dim, input_dim = op_shape.shape # 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) super().__init__(choi_mat, op_shape=op_shape)
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._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 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 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 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, input_dims, output_dims, 'PTM')