def add(self, other: OperatorBase) -> Union["EvolvedOp", SummedOp]: if not self.num_qubits == other.num_qubits: raise ValueError( "Sum over operators with different numbers of qubits, {} and {}, is not well " "defined".format(self.num_qubits, other.num_qubits)) if isinstance(other, EvolvedOp) and self.primitive == other.primitive: return EvolvedOp(self.primitive, coeff=self.coeff + other.coeff) if isinstance(other, SummedOp): op_list = [cast(OperatorBase, self)] + other.oplist return SummedOp(op_list) return SummedOp([self, other])
def from_dict(density_dict: dict) -> 'CircuitStateFn': """ Construct the CircuitStateFn from a dict mapping strings to probability densities. Args: density_dict: The dict representing the desired state. Returns: The CircuitStateFn created from the dict. """ # If the dict is sparse (elements <= qubits), don't go # building a statevector to pass to Qiskit's # initializer, just create a sum. if len(density_dict) <= len(list(density_dict.keys())[0]): statefn_circuits = [] for bstr, prob in density_dict.items(): qc = QuantumCircuit(len(bstr)) # NOTE: Reversing endianness!! for (index, bit) in enumerate(reversed(bstr)): if bit == '1': qc.x(index) sf_circuit = CircuitStateFn(qc, coeff=prob) statefn_circuits += [sf_circuit] if len(statefn_circuits) == 1: return statefn_circuits[0] else: return cast( CircuitStateFn, SummedOp(cast(List[OperatorBase], statefn_circuits))) else: sf_dict = StateFn(density_dict) return CircuitStateFn.from_vector(sf_dict.to_matrix())
def add(self, other: OperatorBase) -> OperatorBase: if not self.num_qubits == other.num_qubits: raise ValueError( "Sum over operators with different numbers of qubits, {} and {}, is not well " "defined".format(self.num_qubits, other.num_qubits) ) if isinstance(other, PauliOp) and self.primitive == other.primitive: return PauliOp(self.primitive, coeff=self.coeff + other.coeff) # pylint: disable=cyclic-import from .pauli_sum_op import PauliSumOp if ( isinstance(other, PauliOp) and isinstance(self.coeff, (int, float, complex)) and isinstance(other.coeff, (int, float, complex)) ): return PauliSumOp( SparsePauliOp(self.primitive, coeffs=[self.coeff]) + SparsePauliOp(other.primitive, coeffs=[other.coeff]) ) if isinstance(other, PauliSumOp) and isinstance(self.coeff, (int, float, complex)): return PauliSumOp(SparsePauliOp(self.primitive, coeffs=[self.coeff])) + other return SummedOp([self, other])
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 add(self, other: OperatorBase) -> Union["OperatorStateFn", SummedOp]: if not self.num_qubits == other.num_qubits: raise ValueError( "Sum over statefns with different numbers of qubits, {} and {}, is not well " "defined".format(self.num_qubits, other.num_qubits)) # Right now doesn't make sense to add a StateFn to a Measurement if isinstance(other, OperatorStateFn ) and self.is_measurement == other.is_measurement: if isinstance(other.primitive, OperatorBase) and self.primitive == other.primitive: return OperatorStateFn( self.primitive, coeff=self.coeff + other.coeff, is_measurement=self.is_measurement, ) # Covers Statevector and custom. elif isinstance(other, OperatorStateFn): # Also assumes scalar multiplication is available return OperatorStateFn( (self.coeff * self.primitive).add(other.primitive * other.coeff), is_measurement=self._is_measurement, ) return SummedOp([self, other])
def add(self, other: OperatorBase) -> OperatorBase: if not self.num_qubits == other.num_qubits: raise ValueError('Sum over operators with different numbers of qubits, ' '{} and {}, is not well ' 'defined'.format(self.num_qubits, other.num_qubits)) if isinstance(other, CircuitStateFn) and self.primitive == other.primitive: return CircuitStateFn(self.primitive, coeff=self.coeff + other.coeff) # Covers all else. return SummedOp([self, other])
def add(self, other: OperatorBase) -> OperatorBase: if not self.num_qubits == other.num_qubits: raise ValueError( 'Sum over statefns with different numbers of qubits, {} and {}, is not well ' 'defined'.format(self.num_qubits, other.num_qubits)) # Right now doesn't make sense to add a StateFn to a Measurement if isinstance(other, VectorStateFn) and self.is_measurement == other.is_measurement: # Covers Statevector and custom. return VectorStateFn((self.coeff * self.primitive) + (other.primitive * other.coeff), is_measurement=self._is_measurement) return SummedOp([self, other])
def group_subops(cls, list_op: Union[ListOp, PauliSumOp]) -> ListOp: """Given a ListOp, attempt to group into Abelian ListOps of the same type. Args: list_op: The Operator to group into Abelian groups Returns: The grouped Operator. Raises: OpflowError: If any of list_op's sub-ops is not ``PauliOp``. """ if isinstance(list_op, ListOp): for op in list_op.oplist: if not isinstance(op, PauliOp): raise OpflowError( "Cannot determine Abelian groups if any Operator in list_op is not " f"`PauliOp`. E.g., {op} ({type(op)})") edges = cls._anti_commutation_graph(list_op) nodes = range(len(list_op)) graph = rx.PyGraph() graph.add_nodes_from(nodes) graph.add_edges_from_no_data(edges) # Keys in coloring_dict are nodes, values are colors coloring_dict = rx.graph_greedy_color(graph) groups = defaultdict(list) for idx, color in coloring_dict.items(): groups[color].append(idx) if isinstance(list_op, PauliSumOp): primitive = list_op.primitive return SummedOp( [ PauliSumOp(primitive[group], grouping_type="TPB") for group in groups.values() ], coeff=list_op.coeff, ) group_ops: List[ListOp] = [ list_op.__class__([list_op[idx] for idx in group], abelian=True) for group in groups.values() ] if len(group_ops) == 1: return group_ops[0].mul(list_op.coeff) return list_op.__class__(group_ops, coeff=list_op.coeff)
def add(self, other: OperatorBase) -> OperatorBase: if not self.num_qubits == other.num_qubits: raise ValueError( f"Sum of operators with different numbers of qubits, {self.num_qubits} and " f"{other.num_qubits}, is not well defined" ) if isinstance(other, PauliSumOp): return PauliSumOp(self.coeff * self.primitive + other.coeff * other.primitive, coeff=1) if isinstance(other, PauliOp): return PauliSumOp( self.coeff * self.primitive + other.coeff * SparsePauliOp(other.primitive) ) return SummedOp([self, other])
def add(self, other: OperatorBase) -> Union["MatrixOp", SummedOp]: if not self.num_qubits == other.num_qubits: raise ValueError( "Sum over operators with different numbers of qubits, {} and {}, is not well " "defined".format(self.num_qubits, other.num_qubits)) if isinstance(other, MatrixOp) and self.primitive == other.primitive: return MatrixOp(self.primitive, coeff=self.coeff + other.coeff) # Terra's Operator cannot handle ParameterExpressions if (isinstance(other, MatrixOp) and not isinstance(self.coeff, ParameterExpression) and not isinstance(other.coeff, ParameterExpression)): return MatrixOp((self.coeff * self.primitive) + (other.coeff * other.primitive)) # Covers Paulis, Circuits, and all else. return SummedOp([self, other])
def to_pauli_op(self, massive: bool = False) -> Union[PauliOp, SummedOp]: def to_native(x): return x.item() if isinstance(x, np.generic) else x if len(self.primitive) == 1: return PauliOp( Pauli( (self.primitive.paulis.z[0], self.primitive.paulis.x[0])), to_native(np.real_if_close(self.primitive.coeffs[0])) * self.coeff, ) coeffs = np.real_if_close(self.primitive.coeffs) return SummedOp( [ PauliOp(pauli, to_native(coeff)) for pauli, coeff in zip(self.primitive.paulis, coeffs) ], coeff=self.coeff, )
def to_pauli_op(self, massive: bool = False) -> Union[PauliOp, SummedOp]: def to_native(x): return x.item() if isinstance(x, np.generic) else x if len(self.primitive) == 1: return PauliOp( Pauli((self.primitive.table.Z[0], self.primitive.table.X[0])), to_native(np.real_if_close(self.primitive.coeffs[0])) * self.coeff, ) tables = self.primitive.table coeffs = np.real_if_close(self.primitive.coeffs) return SummedOp( [ PauliOp( Pauli((t.Z[0], t.X[0])), to_native(c), ) for t, c in zip(tables, coeffs) ], coeff=self.coeff, )
def convert(self, operator: OperatorBase) -> OperatorBase: r""" Given a ``PauliOp``, or an Operator containing ``PauliOps`` if ``_traverse`` is True, converts each Pauli into the basis specified by self._destination and a basis-change-circuit, calls ``replacement_fn`` with these two Operators, and replaces the ``PauliOps`` with the output of ``replacement_fn``. For example, for the built-in ``operator_replacement_fn`` below, each PauliOp p will be replaced by the composition of the basis-change Clifford ``CircuitOp`` c with the destination PauliOp d and c†, such that p = c·d·c†, up to global phase. Args: operator: The Operator to convert. Returns: The converted Operator. """ if (isinstance(operator, OperatorStateFn) and isinstance(operator.primitive, PauliSumOp) and operator.primitive.grouping_type == "TPB"): primitive = operator.primitive.primitive.copy() origin_x = reduce(np.logical_or, primitive.table.X) origin_z = reduce(np.logical_or, primitive.table.Z) origin_pauli = Pauli((origin_z, origin_x)) cob_instr_op, _ = self.get_cob_circuit(origin_pauli) primitive.table.Z = np.logical_or(primitive.table.X, primitive.table.Z) primitive.table.X = False dest_pauli_sum_op = PauliSumOp(primitive, coeff=operator.coeff, grouping_type="TPB") return self._replacement_fn(cob_instr_op, dest_pauli_sum_op) if (isinstance(operator, OperatorStateFn) and isinstance(operator.primitive, SummedOp) and all( isinstance(op, PauliSumOp) and op.grouping_type == "TPB" for op in operator.primitive.oplist)): sf_list: List[OperatorBase] = [ StateFn(op, is_measurement=operator.is_measurement) for op in operator.primitive.oplist ] listop_of_statefns = SummedOp(oplist=sf_list, coeff=operator.coeff) return listop_of_statefns.traverse(self.convert) if isinstance(operator, OperatorStateFn) and isinstance( operator.primitive, PauliSumOp): operator = OperatorStateFn( operator.primitive.to_pauli_op(), coeff=operator.coeff, is_measurement=operator.is_measurement, ) if isinstance(operator, PauliSumOp): operator = operator.to_pauli_op() if isinstance(operator, (Pauli, PauliOp)): cob_instr_op, dest_pauli_op = self.get_cob_circuit(operator) return self._replacement_fn(cob_instr_op, dest_pauli_op) if isinstance(operator, StateFn) and "Pauli" in operator.primitive_strings(): # If the StateFn/Meas only contains a Pauli, use it directly. if isinstance(operator.primitive, PauliOp): cob_instr_op, dest_pauli_op = self.get_cob_circuit( operator.primitive) return self._replacement_fn(cob_instr_op, dest_pauli_op * operator.coeff) # TODO make a canonical "distribute" or graph swap as method in ListOp? elif operator.primitive.distributive: if operator.primitive.abelian: origin_pauli = self.get_tpb_pauli(operator.primitive) cob_instr_op, _ = self.get_cob_circuit(origin_pauli) diag_ops: List[OperatorBase] = [ self.get_diagonal_pauli_op(op) for op in operator.primitive.oplist ] dest_pauli_op = operator.primitive.__class__( diag_ops, coeff=operator.coeff, abelian=True) return self._replacement_fn(cob_instr_op, dest_pauli_op) else: sf_list = [ StateFn(op, is_measurement=operator.is_measurement) for op in operator.primitive.oplist ] listop_of_statefns = operator.primitive.__class__( oplist=sf_list, coeff=operator.coeff) return listop_of_statefns.traverse(self.convert) elif (isinstance(operator, ListOp) and self._traverse and "Pauli" in operator.primitive_strings()): # If ListOp is abelian we can find a single post-rotation circuit # for the whole set. For now, # assume operator can only be abelian if all elements are # Paulis (enforced in AbelianGrouper). if operator.abelian: origin_pauli = self.get_tpb_pauli(operator) cob_instr_op, _ = self.get_cob_circuit(origin_pauli) oplist = cast(List[PauliOp], operator.oplist) diag_ops = [self.get_diagonal_pauli_op(op) for op in oplist] dest_list_op = operator.__class__(diag_ops, coeff=operator.coeff, abelian=True) return self._replacement_fn(cob_instr_op, dest_list_op) else: return operator.traverse(self.convert) return operator