def power(self, n, inplace=False): """Return the compose of a QuantumChannel with itself n times. Args: n (int): the number of times to compose with self (n>0). inplace (bool): If True modify the current object inplace [Default: False] Returns: SuperOp: the n-times composition channel as a SuperOp object. Raises: QiskitError: if the input and output dimensions of the QuantumChannel are not equal, or the power is not a positive integer. """ if not isinstance(n, int) or n < 1: raise QiskitError("Can only power with positive integer powers.") if self._input_dim != self._output_dim: raise QiskitError("Can only power with input_dim = output_dim.") # Override base class power so we can implement more efficiently # using Numpy.matrix_power if inplace: if n == 1: return self self._data = np.linalg.matrix_power(self._data, n) return self # Return new object return SuperOp(np.linalg.matrix_power(self._data, n), *self.dims)
def compose(self, other, qubits=None, front=False): """Return the composition channel self∘other. Args: other (QuantumChannel): a quantum channel subclass. qubits (list): a list of subsystem positions to compose other on. front (bool): If False compose in standard order other(self(input)) otherwise compose in reverse order self(other(input)) [default: False] Returns: Stinespring: The composition channel as a Stinespring object. Raises: QiskitError: if other cannot be converted to a channel or has incompatible dimensions. """ if qubits is not None: # TODO raise QiskitError("NOT IMPLEMENTED: subsystem composition.") # Convert other to Kraus if not isinstance(other, Kraus): other = Kraus(other) # Check dimensions match up if front and self._input_dim != other._output_dim: raise QiskitError( 'input_dim of self must match output_dim of other') if not front and self._output_dim != other._input_dim: raise QiskitError( 'input_dim of other must match output_dim of self') # Since we cannot directly compose two channels in Stinespring # representation we convert to the Kraus representation return Stinespring(Kraus(self).compose(other, front=front))
def power(self, n, inplace=False): """Return the compose of a QuantumChannel with itself n times. Args: n (int): the number of times to compose with self (n>0). inplace (bool): If True modify the current object inplace [Default: False] Returns: QuantumChannel: the n-times composition channel. Raises: QiskitError: if the input and output dimensions of the QuantumChannel are not equal, or the power is not a positive integer. """ if not isinstance(n, int) or n < 1: raise QiskitError("Can only power with positive integer powers.") if self._input_dim != self._output_dim: raise QiskitError("Can only power with input_dim = output_dim.") # Update inplace if inplace: if n == 1: return self # cache current state to apply n-times cache = self.copy() for _ in range(1, n): self.compose(cache, inplace=True) return self # Return new object ret = self.copy() for _ in range(1, n): ret = ret.compose(self) return ret
def subtract(self, other, inplace=False): """Return the QuantumChannel self - other. Args: other (QuantumChannel): a quantum channel subclass inplace (bool): If True modify the current object inplace [Default: False] Returns: SuperOp: the linear subtraction self - other as SuperOp object. Raises: QiskitError: if other is not a QuantumChannel subclass, or has incompatible dimensions. """ if not issubclass(other.__class__, QuantumChannel): raise QiskitError('other is not a QuantumChannel subclass') if self.dims != other.dims: raise QiskitError("other QuantumChannel dimensions are not equal") if not isinstance(other, SuperOp): other = SuperOp(other) if inplace: self._data -= other.data return self input_dim, output_dim = self.dims return SuperOp(self._data - other.data, input_dim, output_dim)
def compose(self, other, front=False): """Return the composition channel self∘other. Args: other (QuantumChannel): a quantum channel subclass. front (bool): If False compose in standard order other(self(input)) otherwise compose in reverse order self(other(input)) [default: False] Returns: Stinespring: The composition channel as a Stinespring object. Raises: QiskitError: if other is not a QuantumChannel subclass, or has incompatible dimensions. """ if not issubclass(other.__class__, QuantumChannel): raise QiskitError('other is not a QuantumChannel subclass') # Check dimensions match up if front and self._input_dim != other._output_dim: raise QiskitError( 'input_dim of self must match output_dim of other') if not front and self._output_dim != other._input_dim: raise QiskitError( 'input_dim of other must match output_dim of self') # Since we cannot directly compose two channels in Stinespring # representation we convert to the Kraus representation return Stinespring(Kraus(self).compose(other, front=front))
def __init__(self, data, input_dim=None, output_dim=None): # Check if input is a quantum channel object # If so we disregard the dimension kwargs if issubclass(data.__class__, QuantumChannel): input_dim, output_dim = data.dims choi_mat = _to_choi(data.rep, data._data, input_dim, output_dim) else: choi_mat = np.array(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 output_dim is None and input_dim is None: output_dim = int(np.sqrt(dim_l)) input_dim = dim_l // output_dim elif input_dim is None: input_dim = dim_l // output_dim elif output_dim is None: output_dim = dim_l // input_dim # Check dimensions if input_dim * output_dim != dim_l: raise QiskitError( "Invalid input and output dimension for Choi-matrix input." ) super().__init__('Choi', choi_mat, input_dim, output_dim)
def measure(self, qubit, cbit): """Measure quantum bit into classical bit (tuples). Args: qubit (QuantumRegister|tuple): quantum register cbit (ClassicalRegister|tuple): classical register Returns: qiskit.Instruction: the attached measure instruction. Raises: QiskitError: if qubit is not in this circuit or bad format; if cbit is not in this circuit or not creg. """ if isinstance(qubit, QuantumRegister) and isinstance(cbit, ClassicalRegister) \ and len(qubit) == len(cbit): instructions = InstructionSet() for i in range(qubit.size): instructions.add(self.measure((qubit, i), (cbit, i))) return instructions elif isinstance(qubit, QuantumRegister) and isinstance( cbit, ClassicalRegister) and len(qubit) != len(cbit): raise QiskitError( "qubit (%s) and cbit (%s) should have the same length" % (len(qubit), len(cbit))) elif not (isinstance(qubit, tuple) and isinstance(cbit, tuple)): raise QiskitError( "Both qubit <%s> and cbit <%s> should be Registers or formated as tuples. " "Hint: You can use subscript eg. cbit[0] to convert it into tuple." % (type(qubit).__name__, type(cbit).__name__)) self._check_qubit(qubit) self._check_creg(cbit[0]) cbit[0].check_range(cbit[1]) return self._attach(Measure(qubit, cbit, self))
def _append_instruction(self, obj, qargs=None): """Update the current Operator by apply an instruction.""" if isinstance(obj, Instruction): mat = None if hasattr(obj, 'to_matrix'): # If instruction is a gate first we see if it has a # `to_matrix` definition and if so use that. try: mat = obj.to_matrix() except QiskitError: pass if mat is not None: # Perform the composition and inplace update the current state # of the operator op = self.compose(mat, qargs=qargs) self._data = op.data else: # 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 obj.definition is None: raise QiskitError('Cannot apply Instruction: {}'.format( obj.name)) for instr, qregs, cregs in obj.definition: if cregs: raise QiskitError( 'Cannot apply instruction with classical registers: {}' .format(instr.name)) # Get the integer position of the flat register new_qargs = [tup[1] for tup in qregs] self._append_instruction(instr, qargs=new_qargs) else: raise QiskitError('Input is not an instruction.')
def _reshape(self, input_dims=None, output_dims=None): """Reshape input and output dimensions of operator. Arg: input_dims (tuple): new subsystem input dimensions. output_dims (tuple): new subsystem output dimensions. Returns: Operator: returns self with reshaped input and output dimensions. Raises: QiskitError: if combined size of all subsystem input dimension or subsystem output dimensions is not constant. """ if input_dims is not None: if np.product(input_dims) != self._input_dim: raise QiskitError( "Reshaped input_dims are incompatible with combined input dimension." ) self._input_dims = tuple(input_dims) if output_dims is not None: if np.product(output_dims) != self._output_dim: raise QiskitError( "Reshaped input_dims are incompatible with combined input dimension." ) self._output_dims = tuple(output_dims) return self
def to_instruction(self): """Convert to a Kraus or UnitaryGate circuit instruction. If the channel is unitary it will be added as a unitary gate, otherwise it will be added as a kraus simulator instruction. Returns: Instruction: A kraus instruction for the channel. Raises: QiskitError: if input data is not an N-qubit CPTP quantum channel. """ from qiskit.circuit.instruction import Instruction # Check if input is an N-qubit CPTP channel. n_qubits = int(np.log2(self._input_dim)) if self._input_dim != self._output_dim or 2**n_qubits != self._input_dim: raise QiskitError( 'Cannot convert QuantumChannel to Instruction: channel is not an N-qubit channel.' ) if not self.is_cptp(): raise QiskitError( 'Cannot convert QuantumChannel to Instruction: channel is not CPTP.' ) # Next we convert to the Kraus representation. Since channel is CPTP we know # that there is only a single set of Kraus operators kraus, _ = _to_kraus(self.rep, self._data, *self.dim) # If we only have a single Kraus operator then the channel is # a unitary channel so can be converted to a UnitaryGate. We do this by # converting to an Operator and using its to_instruction method if len(kraus) == 1: return Operator(kraus[0]).to_instruction() return Instruction('kraus', n_qubits, 0, kraus)
def __init__(self, data, input_dims=None, output_dims=None): """Initialize a SuperOp quantum channel operator.""" if issubclass(data.__class__, BaseOperator): # If not a channel we use `to_operator` method to get # the unitary-representation matrix for input if not issubclass(data.__class__, QuantumChannel): data = data.to_operator() input_dim, output_dim = data.dim super_mat = _to_superop(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() elif isinstance(data, (list, np.ndarray)): # We initialize directly from superoperator matrix super_mat = np.array(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.") else: raise QiskitError("Invalid input data format for SuperOp") # 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__('SuperOp', super_mat, input_dims, output_dims)
def compose(self, other, qargs=None, front=False): """Return the composition channel self∘other. Args: other (QuantumChannel): a quantum channel. qargs (list): a list of subsystem positions to compose other on. front (bool): If False compose in standard order other(self(input)) otherwise compose in reverse order self(other(input)) [default: False] Returns: Chi: The composition channel as a Chi object. Raises: QiskitError: if other is not a QuantumChannel subclass, or has incompatible dimensions. """ if qargs is not None: return Chi(SuperOp(self).compose(other, qargs=qargs, front=front)) # Convert other to Choi since we convert via Choi if not isinstance(other, Choi): other = Choi(other) # Check dimensions match up if front and self._input_dim != other._output_dim: raise QiskitError( 'input_dim of self must match output_dim of other') if not front and self._output_dim != other._input_dim: raise QiskitError( 'input_dim of other must match output_dim of self') # Since we cannot directly add two channels in the Chi # representation we convert to the Choi representation return Chi(Choi(self).compose(other, front=front))
def __init__(self, data, input_dim=None, output_dim=None): # Check if input is a quantum channel object # If so we disregard the dimension kwargs if issubclass(data.__class__, QuantumChannel): input_dim, output_dim = data.dims if input_dim != output_dim: raise QiskitError( "Cannot convert to Chi-matrix: input_dim " + "({}) != output_dim ({})".format(input_dim, output_dim)) chi_mat = _to_chi(data.rep, data._data, input_dim, output_dim) else: 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 Choi-matrix input.') if output_dim is None and input_dim is None: output_dim = int(np.sqrt(dim_l)) input_dim = dim_l // output_dim elif input_dim is None: input_dim = dim_l // output_dim elif output_dim is None: output_dim = dim_l // input_dim # Check dimensions if input_dim * output_dim != dim_l: raise QiskitError( "Invalid input and output dimension for Chi-matrix input.") nqubits = int(np.log2(input_dim)) if 2**nqubits != input_dim: raise QiskitError("Input is not an n-qubit Chi matrix.") super().__init__('Chi', chi_mat, input_dim, output_dim)
def add(self, other, inplace=False): """Return the QuantumChannel self + other. Args: other (QuantumChannel): a quantum channel subclass inplace (bool): If True modify the current object inplace [Default: False] Returns: Chi: the linear addition self + other as a Chi object. Raises: QiskitError: if other is not a QuantumChannel subclass, or has incompatible dimensions. """ if not issubclass(other.__class__, QuantumChannel): raise QiskitError('other is not a QuantumChannel subclass') if self.dims != other.dims: raise QiskitError("other QuantumChannel dimensions are not equal") if not isinstance(other, Chi): other = Chi(other) if inplace: self._data += other._data return self input_dim, output_dim = self.dims return Chi(self._data + other.data, input_dim, output_dim)
def _check_qreg(self, register): """Raise exception if r is not in this circuit or not qreg.""" if not isinstance(register, QuantumRegister): raise QiskitError("expected quantum register") if not self.has_register(register): raise QiskitError("register '%s' not in this circuit" % register.name)
def compose(self, other, inplace=False, front=False): """Return the composition channel self∘other. Args: other (QuantumChannel): a quantum channel subclass inplace (bool): If True modify the current object inplace [Default: False] front (bool): If False compose in standard order other(self(input)) otherwise compose in reverse order self(other(input)) [default: False] Returns: UnitaryChannel: The composition channel as a UnitaryChannel object. Raises: QiskitError: if other is not a QuantumChannel subclass, has incompatible dimensions, or is a non-unitary channel. """ if not issubclass(other.__class__, QuantumChannel): raise QiskitError('Other is not a channel rep') # Check dimensions match up if front and self._input_dim != other._output_dim: raise QiskitError( 'input_dim of self must match output_dim of other') if not front and self._output_dim != other._input_dim: raise QiskitError( 'input_dim of other must match output_dim of self') # Convert to UnitaryChannel matrix if not isinstance(other, UnitaryChannel): other = UnitaryChannel(other) if front: # Composition A(B(input)) if self._input_dim != other._output_dim: raise QiskitError( 'input_dim of self must match output_dim of other') input_dim = other._input_dim output_dim = self._output_dim if inplace: np.dot(self._data, other.data, out=self._data) self._input_dim = input_dim self._output_dim = output_dim return self return UnitaryChannel(np.dot(self._data, other.data), input_dim, output_dim) # Composition B(A(input)) if self._output_dim != other._input_dim: raise QiskitError( 'input_dim of other must match output_dim of self') input_dim = self._input_dim output_dim = other._output_dim if inplace: # Numpy out raises error if we try and use out=self._data here self._data = np.dot(other.data, self._data) self._input_dim = input_dim self._output_dim = output_dim return self return UnitaryChannel(np.dot(other.data, self._data), input_dim, output_dim)
def _check_creg(self, register): """Raise exception if r is not in this circuit or not creg.""" if not isinstance(register, ClassicalRegister): raise QiskitError("Expected ClassicalRegister, but %s given" % type(register)) if not self.has_register(register): raise QiskitError("register '%s' not in this circuit" % register.name)
def compose(self, other, inplace=False, front=False): """Return the composition channel self∘other. Args: other (QuantumChannel): a quantum channel subclass inplace (bool): If True modify the current object inplace [Default: False] front (bool): If False compose in standard order other(self(input)) otherwise compose in reverse order self(other(input)) [default: False] Returns: Kraus: The composition channel as a Kraus object. Raises: QiskitError: if other is not a QuantumChannel subclass, or has incompatible dimensions. """ if not issubclass(other.__class__, QuantumChannel): raise QiskitError('other is not a QuantumChannel subclass') # Check dimensions match up if front and self._input_dim != other._output_dim: raise QiskitError( 'input_dim of self must match output_dim of other') if not front and self._output_dim != other._input_dim: raise QiskitError( 'input_dim of other must match output_dim of self') # Convert to Choi matrix if not isinstance(other, Kraus): other = Kraus(other) if front: ka_l, ka_r = self._data kb_l, kb_r = other._data input_dim = other._input_dim output_dim = self._output_dim else: ka_l, ka_r = other._data kb_l, kb_r = self._data input_dim = self._input_dim output_dim = other._output_dim kab_l = [np.dot(a, b) for a in ka_l for b in kb_l] if ka_r is None and kb_r is None: kab_r = None elif ka_r is None: kab_r = [np.dot(a, b) for a in ka_l for b in kb_r] elif kb_r is None: kab_r = [np.dot(a, b) for a in ka_r for b in kb_l] else: kab_r = [np.dot(a, b) for a in ka_r for b in kb_r] data = (kab_l, kab_r) if inplace: self._data = data self._input_dim = input_dim self._output_dim = output_dim return self return Kraus(data, input_dim, output_dim)
def rtol(self, rtol): """Set the relative tolerence parameter for float comparisons.""" max_tol = QuantumChannel.MAX_TOL if rtol < 0: raise QiskitError("Invalid rtol: must be non-negative.") if rtol > max_tol: raise QiskitError( "Invalid rtol: must be less than {}.".format(max_tol)) QuantumChannel.RTOL = rtol
def _check_nqubit_dim(input_dim, output_dim): """Return true if dims correspond to an n-qubit channel.""" if input_dim != output_dim: raise QiskitError( 'Not an n-qubit channel: input_dim' + ' ({}) != output_dim ({})'.format(input_dim, output_dim)) num_qubits = int(np.log2(input_dim)) if 2**num_qubits != input_dim: raise QiskitError('Not an n-qubit channel: input_dim != 2 ** n')
def compose(self, other, inplace=False, front=False): """Return the composition channel self∘other. Args: other (QuantumChannel): a quantum channel subclass inplace (bool): If True modify the current object inplace [Default: False] front (bool): If False compose in standard order other(self(input)) otherwise compose in reverse order self(other(input)) [default: False] Returns: SuperOp: The composition channel as a SuperOp object. Raises: QiskitError: if other is not a QuantumChannel subclass, or has incompatible dimensions. """ if not issubclass(other.__class__, QuantumChannel): raise QiskitError('Other is not a channel rep') # Check dimensions match up if front and self._input_dim != other._output_dim: raise QiskitError( 'input_dim of self must match output_dim of other') if not front and self._output_dim != other._input_dim: raise QiskitError( 'input_dim of other must match output_dim of self') # Convert other to SuperOp if not isinstance(other, SuperOp): other = SuperOp(other) if front: # Composition A(B(input)) input_dim = other._input_dim output_dim = self._output_dim if inplace: if self.dims == other.dims: np.dot(self._data, other.data, out=self._data) else: self._data = np.dot(self._data, other.data) self._input_dim = input_dim self._output_dim = output_dim return self return SuperOp(np.dot(self._data, other.data), input_dim, output_dim) # Composition B(A(input)) input_dim = self._input_dim output_dim = other._output_dim if inplace: if self.dims == other.dims: np.dot(other.data, self._data, out=self._data) else: self._data = np.dot(other.data, self._data) self._input_dim = input_dim self._output_dim = output_dim return self return SuperOp(np.dot(other.data, self._data), input_dim, output_dim)
def __init__(self, data, input_dims=None, output_dims=None): """Initialize a Stinespring quantum channel operator.""" if issubclass(data.__class__, BaseOperator): # If not a channel we use `to_operator` method to get # the unitary-representation matrix for input if not issubclass(data.__class__, QuantumChannel): data = data.to_operator() input_dim, output_dim = data.dim 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() elif 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: raise QiskitError("Invalid input data format for Stinespring") # 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 _rtol(self, rtol): """Set the relative tolerence parameter for float comparisons.""" # NOTE: that this overrides the class value so applies to all # instances of the class. max_tol = self.__class__.MAX_TOL if rtol < 0: raise QiskitError("Invalid rtol: must be non-negative.") if rtol > max_tol: raise QiskitError( "Invalid rtol: must be less than {}.".format(max_tol)) self.__class__.RTOL = rtol
def add_register(self, *regs): """Add registers.""" for register in regs: if register in self.qregs or register in self.cregs: raise QiskitError("register name \"%s\" already exists" % register.name) if isinstance(register, QuantumRegister): self.qregs.append(register) elif isinstance(register, ClassicalRegister): self.cregs.append(register) else: raise QiskitError("expected a register")
def compose(self, other, qubits=None, front=False): """Return the composition channel self∘other. Args: other (QuantumChannel): a quantum channel subclass. qubits (list): a list of subsystem positions to compose other on. front (bool): If False compose in standard order other(self(input)) otherwise compose in reverse order self(other(input)) [default: False] Returns: Kraus: The composition channel as a Kraus object. Raises: QiskitError: if other cannot be converted to a channel, or has incompatible dimensions. """ if qubits is not None: # TODO raise QiskitError("NOT IMPLEMENTED: subsystem composition.") if not isinstance(other, Kraus): other = Kraus(other) # Check dimensions match up if front and self._input_dim != other._output_dim: raise QiskitError( 'input_dim of self must match output_dim of other') if not front and self._output_dim != other._input_dim: raise QiskitError( 'input_dim of other must match output_dim of self') if front: ka_l, ka_r = self._data kb_l, kb_r = other._data input_dim = other._input_dim output_dim = self._output_dim else: ka_l, ka_r = other._data kb_l, kb_r = self._data input_dim = self._input_dim output_dim = other._output_dim kab_l = [np.dot(a, b) for a in ka_l for b in kb_l] if ka_r is None and kb_r is None: kab_r = None elif ka_r is None: kab_r = [np.dot(a, b) for a in ka_l for b in kb_r] elif kb_r is None: kab_r = [np.dot(a, b) for a in ka_r for b in kb_l] else: kab_r = [np.dot(a, b) for a in ka_r for b in kb_r] return Kraus((kab_l, kab_r), input_dim, output_dim)
def _best_subset(backend, n_qubits): """Computes the qubit mapping with the best connectivity. Parameters: backend (BaseBackend): A Qiskit backend instance. n_qubits (int): Number of subset qubits to consider. Returns: ndarray: Array of qubits to use for best connectivity mapping. Raises: QiskitError: Wrong number of qubits given. """ if n_qubits == 1: return np.array([0]) elif n_qubits <= 0: raise QiskitError('Number of qubits <= 0.') device_qubits = backend.configuration().n_qubits if n_qubits > device_qubits: raise QiskitError('Number of qubits greater than device.') cmap = np.asarray(getattr(backend.configuration(), 'coupling_map', None)) data = np.ones_like(cmap[:, 0]) sp_cmap = sp.coo_matrix((data, (cmap[:, 0], cmap[:, 1])), shape=(device_qubits, device_qubits)).tocsr() best = 0 best_map = None # do bfs with each node as starting point for k in range(sp_cmap.shape[0]): bfs = cs.breadth_first_order(sp_cmap, i_start=k, directed=False, return_predecessors=False) connection_count = 0 for i in range(n_qubits): node_idx = bfs[i] for j in range(sp_cmap.indptr[node_idx], sp_cmap.indptr[node_idx + 1]): node = sp_cmap.indices[j] for counter in range(n_qubits): if node == bfs[counter]: connection_count += 1 break if connection_count > best: best = connection_count best_map = bfs[0:n_qubits] return best_map
def compose(self, other, qargs=None, front=False): """Return the composition channel self∘other. Args: other (QuantumChannel): a quantum channel. qargs (list): a list of subsystem positions to compose other on. front (bool): If False compose in standard order other(self(input)) otherwise compose in reverse order self(other(input)) [default: False] Returns: Choi: The composition channel as a Choi object. Raises: QiskitError: if other cannot be converted to a channel or has incompatible dimensions. """ if qargs is not None: return Choi(SuperOp(self).compose(other, qargs=qargs, front=front)) # Convert to Choi matrix if not isinstance(other, Choi): other = Choi(other) # Check dimensions match up if front and self._input_dim != other._output_dim: raise QiskitError( 'input_dim of self must match output_dim of other') if not front and self._output_dim != other._input_dim: raise QiskitError( 'input_dim of other must match output_dim of self') if front: first = np.reshape(other._data, other._bipartite_shape) second = np.reshape(self._data, self._bipartite_shape) input_dim = other._input_dim input_dims = other.input_dims() output_dim = self._output_dim output_dims = self.output_dims() else: first = np.reshape(self._data, self._bipartite_shape) second = np.reshape(other._data, other._bipartite_shape) input_dim = self._input_dim input_dims = self.input_dims() output_dim = other._output_dim output_dims = other.output_dims() # Contract Choi matrices for composition data = np.reshape(np.einsum('iAjB,AkBl->ikjl', first, second), (input_dim * output_dim, input_dim * output_dim)) return Choi(data, input_dims, output_dims)
def _format_state(self, state): """Format input state so it is statevector or density matrix""" state = np.array(state) shape = state.shape ndim = state.ndim if ndim > 2: raise QiskitError('Input state is not a vector or matrix.') # Flatten column-vector to vector if ndim == 2: if shape[1] != 1 and shape[1] != shape[0]: raise QiskitError('Input state is not a vector or matrix.') if shape[1] == 1: # flatten colum-vector to vector state = np.reshape(state, shape[0]) return state
def multiply(self, other): """Return the QuantumChannel self + other. Args: other (complex): a complex number. Returns: Kraus: the scalar multiplication other * self as a Kraus object. Raises: QiskitError: if other is not a valid scalar. """ if not isinstance(other, Number): raise QiskitError("other is not a number") # If the number is complex we need to convert to general # kraus channel so we multiply via Choi representation if isinstance(other, complex) or other < 0: # Convert to Choi-matrix return Kraus(Choi(self).multiply(other)) # If the number is real we can update the Kraus operators # directly val = np.sqrt(other) kraus_r = None kraus_l = [val * k for k in self._data[0]] if self._data[1] is not None: kraus_r = [val * k for k in self._data[1]] return Kraus((kraus_l, kraus_r), self._input_dim, self._output_dim)
def _einsum_matmul(cls, tensor, mat, indices, shift=0, right_mul=False): """Perform a contraction using Numpy.einsum Args: tensor (np.array): a vector or matrix reshaped to a rank-N tensor. mat (np.array): a matrix reshaped to a rank-2M tensor. indices (list): tensor indices to contract with mat. shift (int): shift for indicies of tensor to contract [Default: 0]. right_mul (bool): if True right multiply tensor by mat (else left multiply) [Default: False]. Returns: Numpy.ndarray: the matrix multiplied rank-N tensor. Raises: QiskitError: if mat is not an even rank tensor. """ rank = tensor.ndim rank_mat = mat.ndim if rank_mat % 2 != 0: raise QiskitError( "Contracted matrix must have an even number of indices.") # Get einsum indices for tensor indices_tensor = list(range(rank)) for j, index in enumerate(indices): indices_tensor[index + shift] = rank + j # Get einsum indces for mat mat_contract = list(reversed(range(rank, rank + len(indices)))) mat_free = [index + shift for index in reversed(indices)] if right_mul: indices_mat = mat_contract + mat_free else: indices_mat = mat_free + mat_contract return np.einsum(tensor, indices_tensor, mat, indices_mat)