def tensor(self, other: OperatorBase) -> Union["CircuitStateFn", TensoredOp]: r""" Return tensor product between self and other, overloaded by ``^``. Note: You must be conscious of Qiskit's big-endian bit printing convention. Meaning, Plus.tensor(Zero) produces a \|+⟩ on qubit 0 and a \|0⟩ on qubit 1, or \|+⟩⨂\|0⟩, but would produce a QuantumCircuit like: \|0⟩-- \|+⟩-- Because Terra prints circuits and results with qubit 0 at the end of the string or circuit. Args: other: The ``OperatorBase`` to tensor product with self. Returns: An ``OperatorBase`` equivalent to the tensor product of self and other. """ if isinstance(other, CircuitStateFn ) and other.is_measurement == self.is_measurement: # Avoid reimplementing tensor, just use CircuitOp's c_op_self = CircuitOp(self.primitive, self.coeff) c_op_other = CircuitOp(other.primitive, other.coeff) c_op = c_op_self.tensor(c_op_other) if isinstance(c_op, CircuitOp): return CircuitStateFn( primitive=c_op.primitive, # pylint: disable=no-member coeff=c_op.coeff, is_measurement=self.is_measurement) return TensoredOp([self, other])
def compose(self, other: OperatorBase, permutation: Optional[List[int]] = None, front: bool = False) -> OperatorBase: if not self.is_measurement and not front: raise ValueError( 'Composition with a Statefunctions in the first operand is not defined.' ) new_self, other = self._expand_shorter_operator_and_permute( other, permutation) if front: return other.compose(new_self) if isinstance(other, (PauliOp, CircuitOp, MatrixOp)): op_circuit_self = CircuitOp(self.primitive) # Avoid reimplementing compose logic composed_op_circs = cast( CircuitOp, op_circuit_self.compose(other.to_circuit_op())) # Returning CircuitStateFn return CircuitStateFn(composed_op_circs.primitive, is_measurement=self.is_measurement, coeff=self.coeff * other.coeff) if isinstance(other, CircuitStateFn) and self.is_measurement: # pylint: disable=cyclic-import from ..operator_globals import Zero return self.compose(CircuitOp( other.primitive, other.coeff)).compose(Zero ^ self.num_qubits) return ComposedOp([new_self, other])
def permute(self, permutation: Optional[List[int]] = None) -> OperatorBase: """Creates a new MatrixOp that acts on the permuted qubits. Args: permutation: A list defining where each qubit should be permuted. The qubit at index j should be permuted to position permutation[j]. Returns: A new MatrixOp representing the permuted operator. Raises: OpflowError: if indices do not define a new index for each qubit. """ new_self = self new_matrix_size = max(permutation) + 1 if self.num_qubits != len(permutation): raise OpflowError( "New index must be defined for each qubit of the operator.") if self.num_qubits < new_matrix_size: # pad the operator with identities new_self = self._expand_dim(new_matrix_size - self.num_qubits) qc = QuantumCircuit(new_matrix_size) # extend the indices to match the size of the new matrix permutation = (list( filter(lambda x: x not in permutation, range(new_matrix_size))) + permutation) # decompose permutation into sequence of transpositions transpositions = arithmetic.transpositions(permutation) for trans in transpositions: qc.swap(trans[0], trans[1]) matrix = CircuitOp(qc).to_matrix() return MatrixOp(matrix.transpose()) @ new_self @ MatrixOp(matrix)
def _recursive_convert(self, operator: OperatorBase) -> OperatorBase: if isinstance(operator, EvolvedOp): if isinstance(operator.primitive, (PauliOp, PauliSumOp)): pauli = operator.primitive.primitive time = operator.coeff * operator.primitive.coeff evo = PauliEvolutionGate( pauli, time=time, synthesis=self._get_evolution_synthesis()) return CircuitOp(evo.definition) # operator = EvolvedOp(operator.primitive.to_pauli_op(), coeff=operator.coeff) if not {"Pauli"} == operator.primitive_strings(): logger.warning( "Evolved Hamiltonian is not composed of only Paulis, converting to " "Pauli representation, which can be expensive.") # Setting massive=False because this conversion is implicit. User can perform this # action on the Hamiltonian with massive=True explicitly if they so choose. # TODO explore performance to see whether we should avoid doing this repeatedly pauli_ham = operator.primitive.to_pauli_op(massive=False) operator = EvolvedOp(pauli_ham, coeff=operator.coeff) if isinstance(operator.primitive, SummedOp): # TODO uncomment when we implement Abelian grouped evolution. # if operator.primitive.abelian: # return self.evolution_for_abelian_paulisum(operator.primitive) # else: # Collect terms that are not the identity. oplist = [ x for x in operator.primitive if not isinstance(x, PauliOp) or sum(x.primitive.x + x.primitive.z) != 0 ] # Collect the coefficients of any identity terms, # which become global phases when exponentiated. identity_phases = [ x.coeff for x in operator.primitive if isinstance(x, PauliOp) and sum(x.primitive.x + x.primitive.z) == 0 ] # Construct sum without the identity operators. new_primitive = SummedOp(oplist, coeff=operator.primitive.coeff) trotterized = self.trotter.convert(new_primitive) circuit_no_identities = self._recursive_convert(trotterized) # Set the global phase of the QuantumCircuit to account for removed identity terms. global_phase = -sum(identity_phases) * operator.primitive.coeff circuit_no_identities.primitive.global_phase = global_phase return circuit_no_identities # Covers ListOp, ComposedOp, TensoredOp elif isinstance(operator.primitive, ListOp): converted_ops = [ self._recursive_convert(op) for op in operator.primitive.oplist ] return operator.primitive.__class__(converted_ops, coeff=operator.coeff) elif isinstance(operator, ListOp): return operator.traverse(self.convert).reduce() return operator
def exp_i(self) -> OperatorBase: """Return a ``CircuitOp`` equivalent to e^-iH for this operator H""" return CircuitOp(HamiltonianGate(self.primitive, time=self.coeff))
def make_immutable(obj): """Delete the __setattr__ property to make the object mostly immutable.""" # TODO figure out how to get correct error message # def throw_immutability_exception(self, *args): # raise OpflowError('Operator convenience globals are immutable.') obj.__setattr__ = None return obj # 1-Qubit Paulis X = make_immutable(PauliOp(Pauli("X"))) Y = make_immutable(PauliOp(Pauli("Y"))) Z = make_immutable(PauliOp(Pauli("Z"))) I = make_immutable(PauliOp(Pauli("I"))) # Clifford+T, and some other common non-parameterized gates CX = make_immutable(CircuitOp(CXGate())) S = make_immutable(CircuitOp(SGate())) H = make_immutable(CircuitOp(HGate())) T = make_immutable(CircuitOp(TGate())) Swap = make_immutable(CircuitOp(SwapGate())) CZ = make_immutable(CircuitOp(CZGate())) # 1-Qubit Paulis Zero = make_immutable(DictStateFn("0")) One = make_immutable(DictStateFn("1")) Plus = make_immutable(H.compose(Zero)) Minus = make_immutable(H.compose(X).compose(Zero))