def to_matrix(self, massive: bool = False) -> np.ndarray: OperatorBase._check_massive("to_matrix", True, self.num_qubits, massive) # Combination function must be able to handle classical values. # Note: this can end up, when we have list operators containing other list operators, as a # ragged array and numpy 1.19 raises a deprecation warning unless this is explicitly # done as object type now - was implicit before. mat = self.combo_fn( np.asarray([ op.to_matrix(massive=massive) * self.coeff for op in self.oplist ], dtype=object)) # Note: As ComposedOp has a combo function of inner product we can end up here not with # a matrix (array) but a scalar. In which case we make a single element array of it. if isinstance(mat, Number): mat = [mat] return np.asarray(mat, dtype=complex)
def convert(self, operator: OperatorBase) -> OperatorBase: """ Convert the Operator to ``CircuitStateFns``, recursively if ``traverse`` is True. Args: operator: The Operator to convert Returns: The converted Operator. """ if isinstance(operator, DictStateFn) and self._convert_dicts: return CircuitStateFn.from_dict(operator.primitive) if isinstance(operator, VectorStateFn) and self._convert_vectors: return CircuitStateFn.from_vector(operator.to_matrix(massive=True)) elif isinstance(operator, ListOp) and 'Dict' in operator.primitive_strings(): return operator.traverse(self.convert) else: return operator
def compose(self, other: OperatorBase, permutation: Optional[List[int]] = None, front: bool = False) -> OperatorBase: new_self, other = self._expand_shorter_operator_and_permute(other, permutation) new_self = cast(ListOp, new_self) if front: return other.compose(new_self) # Avoid circular dependency # pylint: disable=cyclic-import from .composed_op import ComposedOp return ComposedOp([new_self, other])
def compose(self, other: OperatorBase, permutation: Optional[List[int]] = None, front: bool = False) -> OperatorBase: new_self, other = self._expand_shorter_operator_and_permute( other, permutation) if front: return other.compose(new_self) if isinstance(other, ComposedOp): return ComposedOp([new_self] + other.oplist) return ComposedOp([new_self, other])
def equals(self, other: OperatorBase) -> bool: self_reduced, other_reduced = self.reduce(), other.reduce() if not isinstance(other_reduced, PauliSumOp): return False if isinstance(self_reduced.coeff, ParameterExpression) or isinstance( other_reduced.coeff, ParameterExpression): return (self_reduced.coeff == other_reduced.coeff and self_reduced.primitive == other_reduced.primitive) return (len(self_reduced) == len(other_reduced) and self_reduced.primitive == other_reduced.primitive)
def convert(self, operator: OperatorBase) -> OperatorBase: """Accepts an Operator and returns a new Operator with the Pauli measurements replaced by diagonal Pauli post-rotation based measurements so they can be evaluated by sampling and averaging. Args: operator: The operator to convert. Returns: The converted operator. """ if isinstance(operator, ListOp): return operator.traverse(self.convert).reduce() if isinstance(operator, OperatorStateFn) and operator.is_measurement: # Change to Pauli representation if necessary if ( isinstance(operator.primitive, (ListOp, PrimitiveOp)) and not isinstance(operator.primitive, PauliSumOp) and {"Pauli", "SparsePauliOp"} < operator.primitive_strings() ): logger.warning( "Measured Observable 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 Observable with massive=True explicitly if they so choose. pauli_obsv = operator.primitive.to_pauli_op(massive=False) operator = StateFn(pauli_obsv, is_measurement=True, coeff=operator.coeff) if self._grouper and isinstance(operator.primitive, (ListOp, PauliSumOp)): grouped = self._grouper.convert(operator.primitive) operator = StateFn(grouped, is_measurement=True, coeff=operator.coeff) # Convert the measurement into diagonal basis (PauliBasisChange chooses # this basis by default). cob = PauliBasisChange(replacement_fn=PauliBasisChange.measurement_replacement_fn) return cob.convert(operator).reduce() return operator
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) new_self.from_operator = self.from_operator 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, from_operator=self.from_operator, ) if isinstance(other, CircuitStateFn) and self.is_measurement: # pylint: disable=cyclic-import from ..operator_globals import Zero return self.compose(CircuitOp(other.primitive)).compose( (Zero ^ self.num_qubits) * other.coeff) return ComposedOp([new_self, other])
def _check_is_diagonal(operator: OperatorBase) -> bool: """Check whether ``operator`` is diagonal. Args: operator: The operator to check for diagonality. Returns: True, if the operator is diagonal, False otherwise. Raises: OpflowError: If the operator is not diagonal. """ if isinstance(operator, PauliOp): # every X component must be False if not np.any(operator.primitive.x): return True return False if isinstance(operator, SummedOp): # cover the case of sums of diagonal paulis, but don't raise since there might be summands # canceling the non-diagonal parts # ignoring mypy since we know that all operators are PauliOps if all( isinstance(op, PauliOp) and not np.any(op.primitive.x) for op in operator.oplist): return True if isinstance(operator, ListOp): return all(operator.traverse(_check_is_diagonal)) # cannot efficiently check if a operator is diagonal, converting to matrix matrix = operator.to_matrix() if np.all(matrix == np.diag(np.diagonal(matrix))): return True return False
def compose(self, other: OperatorBase, permutation: Optional[List[int]] = None, front: bool = False) -> OperatorBase: r""" Composition (Linear algebra-style: A@B(x) = A(B(x))) is not well defined for states in the binary function model, but is well defined for measurements. Args: other: The Operator to compose with self. permutation: ``List[int]`` which defines permutation on other operator. front: If front==True, return ``other.compose(self)``. Returns: An Operator equivalent to the function composition of self and other. Raises: ValueError: If self is not a measurement, it cannot be composed from the right. """ # TODO maybe allow outers later to produce density operators or projectors, but not yet. if not self.is_measurement and not front: raise ValueError( "Composition with a Statefunction in the first operand is not defined." ) new_self, other = self._expand_shorter_operator_and_permute( other, permutation) if front: return other.compose(self) # TODO maybe include some reduction here in the subclasses - vector and Op, op and Op, etc. from ..primitive_ops.circuit_op import CircuitOp if self.primitive == { "0" * self.num_qubits: 1.0 } and isinstance(other, CircuitOp): # Returning CircuitStateFn return StateFn(other.primitive, is_measurement=self.is_measurement, coeff=self.coeff * other.coeff) from ..list_ops.composed_op import ComposedOp if isinstance(other, ComposedOp): return ComposedOp([new_self] + other.oplist, coeff=new_self.coeff * other.coeff) return ComposedOp([new_self, other])
def compose( self, other: OperatorBase, permutation: Optional[List[int]] = None, front: bool = False ) -> OperatorBase: new_self, other = self._expand_shorter_operator_and_permute(other, permutation) new_self = cast(MatrixOp, new_self) if front: return other.compose(new_self) if isinstance(other, MatrixOp): return MatrixOp( new_self.primitive.compose(other.primitive, front=True), coeff=new_self.coeff * other.coeff, ) return super(MatrixOp, new_self).compose(other)
def convert(self, operator: OperatorBase) -> OperatorBase: """Accept an Operator and return a new Operator with the Pauli measurements replaced by AerSnapshot-based expectation circuits. Args: operator: The operator to convert. Returns: The converted operator. """ if isinstance(operator, OperatorStateFn) and operator.is_measurement: return self._replace_pauli_sums(operator.primitive) * operator.coeff elif isinstance(operator, ListOp): return operator.traverse(self.convert) else: return operator
def equals(self, other: OperatorBase) -> bool: """Check if other is equal to self. Note: This is not a mathematical check for equality. If ``self`` and ``other`` implement the same operation but differ in the representation (e.g. different type of summands) ``equals`` will evaluate to ``False``. Args: other: The other operator to check for equality. Returns: True, if other and self are equal, otherwise False. Examples: >>> from qiskit.opflow import X, Z >>> 2 * X == X + X True >>> X + Z == Z + X True """ self_reduced, other_reduced = self.reduce(), other.reduce() if not isinstance(other_reduced, type(self_reduced)): return False # check if reduced op is still a SummedOp if not isinstance(self_reduced, SummedOp): return self_reduced == other_reduced self_reduced = cast(SummedOp, self_reduced) other_reduced = cast(SummedOp, other_reduced) if len(self_reduced.oplist) != len(other_reduced.oplist): return False # absorb coeffs into the operators if self_reduced.coeff != 1: self_reduced = SummedOp( [op * self_reduced.coeff for op in self_reduced.oplist]) if other_reduced.coeff != 1: other_reduced = SummedOp( [op * other_reduced.coeff for op in other_reduced.oplist]) # compare independent of order return all(any(i == j for j in other_reduced) for i in self_reduced)
def equals(self, other: OperatorBase) -> bool: self_reduced, other_reduced = self.reduce(), other.reduce() if isinstance(other_reduced, PauliOp): other_reduced = PauliSumOp( SparsePauliOp(other_reduced.primitive, coeffs=[other_reduced.coeff])) if not isinstance(other_reduced, PauliSumOp): return False if isinstance(self_reduced.coeff, ParameterExpression) or isinstance( other_reduced.coeff, ParameterExpression): return self_reduced.coeff == other_reduced.coeff and self_reduced.primitive.equiv( other_reduced.primitive) return len(self_reduced) == len( other_reduced) and self_reduced.primitive.equiv( other_reduced.primitive)
def compose( self, other: OperatorBase, permutation: Optional[List[int]] = None, front: bool = False, ) -> OperatorBase: new_self, other = self._expand_shorter_operator_and_permute( other, permutation) new_self = cast(PauliSumOp, new_self) if front: return other.compose(new_self) # If self is identity, just return other. if not np.any( np.logical_or(new_self.primitive.paulis.x, new_self.primitive.paulis.z)): return other * new_self.coeff * sum(new_self.primitive.coeffs) # Both PauliSumOps if isinstance(other, PauliSumOp): return PauliSumOp( new_self.primitive.dot(other.primitive), coeff=new_self.coeff * other.coeff, ) if isinstance(other, PauliOp): other_primitive = SparsePauliOp(other.primitive) return PauliSumOp( new_self.primitive.dot(other_primitive), coeff=new_self.coeff * other.coeff, ) # pylint: disable=cyclic-import from ..state_fns.circuit_state_fn import CircuitStateFn from .circuit_op import CircuitOp if isinstance(other, (CircuitOp, CircuitStateFn)): pauli_op = cast(Union[PauliOp, SummedOp], new_self.to_pauli_op()) return pauli_op.to_circuit_op().compose(other) return super(PauliSumOp, new_self).compose(other)
def convert(self, operator: OperatorBase) -> OperatorBase: """ Converts the Operator to tapered one by Z2 symmetries. Args: operator: the operator Returns: A new operator whose qubit number is reduced by 2. """ if not isinstance(operator, PauliSumOp): return operator operator = cast(PauliSumOp, operator) if operator.is_zero(): logger.info("Operator is empty, can not do two qubit reduction. " "Return the empty operator back.") return PauliSumOp.from_list([("I" * (operator.num_qubits - 2), 0)]) num_qubits = operator.num_qubits last_idx = num_qubits - 1 mid_idx = num_qubits // 2 - 1 sq_list = [mid_idx, last_idx] # build symmetries, sq_paulis: symmetries, sq_paulis = [], [] for idx in sq_list: pauli_str = ["I"] * num_qubits pauli_str[idx] = "Z" z_sym = Pauli("".join(pauli_str)[::-1]) symmetries.append(z_sym) pauli_str[idx] = "X" sq_pauli = Pauli("".join(pauli_str)[::-1]) sq_paulis.append(sq_pauli) z2_symmetries = Z2Symmetries(symmetries, sq_paulis, sq_list, self._tapering_values) return z2_symmetries.taper(operator)
def convert(self, operator: OperatorBase) -> OperatorBase: """Accept an Operator and return a new Operator with the Pauli measurements replaced by AerSnapshot-based expectation circuits. Args: operator: The operator to convert. If it contains non-hermitian terms, the operator is decomposed into hermitian and anti-hermitian parts. Returns: The converted operator. """ if isinstance(operator, OperatorStateFn) and operator.is_measurement: if isinstance(operator.primitive, ListOp): is_herm = all( (op.is_hermitian() for op in operator.primitive.oplist)) else: is_herm = operator.primitive.is_hermitian() if not is_herm: pauli_sum_re = (self._replace_pauli_sums( 1 / 2 * (operator.primitive + operator.primitive.adjoint()).reduce()) * operator.coeff) pauli_sum_im = (self._replace_pauli_sums( 1 / 2j * (operator.primitive - operator.primitive.adjoint()).reduce()) * operator.coeff) pauli_sum = (pauli_sum_re + 1j * pauli_sum_im).reduce() else: pauli_sum = self._replace_pauli_sums( operator.primitive) * operator.coeff return pauli_sum elif isinstance(operator, ListOp): return operator.traverse(self.convert) else: return operator
def compose(self, other: OperatorBase, permutation: Optional[List[int]] = None, front: bool = False) -> OperatorBase: new_self, other = self._expand_shorter_operator_and_permute( other, permutation) new_self = cast(PauliOp, new_self) if front: return other.compose(new_self) # If self is identity, just return other. if not any(new_self.primitive.x + new_self.primitive.z): return other * new_self.coeff # Both Paulis if isinstance(other, PauliOp): product = new_self.primitive.dot(other.primitive) return PrimitiveOp(product, coeff=new_self.coeff * other.coeff) # pylint: disable=cyclic-import from .pauli_sum_op import PauliSumOp if isinstance(other, PauliSumOp): return PauliSumOp( SparsePauliOp(new_self.primitive).dot(other.primitive), coeff=new_self.coeff * other.coeff, ) # pylint: disable=cyclic-import from .circuit_op import CircuitOp from ..state_fns.circuit_state_fn import CircuitStateFn if isinstance(other, (CircuitOp, CircuitStateFn)): return new_self.to_circuit_op().compose(other) return super(PauliOp, new_self).compose(other)
def to_density_matrix(self, massive: bool = False) -> np.ndarray: OperatorBase._check_massive('to_density_matrix', True, self.num_qubits, massive) states = int(2 ** self.num_qubits) return self.to_matrix(massive=massive) * np.eye(states) * self.coeff
def to_matrix(self, massive: bool = False) -> np.ndarray: OperatorBase._check_massive("to_matrix", True, self.num_qubits, massive) return self.primitive.to_matrix() * self.coeff
def to_matrix(self, massive: bool = False) -> np.ndarray: OperatorBase._check_massive("to_matrix", True, self.num_qubits, massive) unitary = qiskit.quantum_info.Operator(self.to_circuit()).data return unitary * self.coeff
def to_matrix(self, massive: bool = False) -> np.ndarray: OperatorBase._check_massive("to_matrix", False, self.num_qubits, massive) vec = self.primitive.data * self.coeff return vec if not self.is_measurement else vec.reshape(1, -1)
def build(operator: OperatorBase, backend: Optional[Union[Backend, BaseBackend, QuantumInstance]] = None, include_custom: bool = True) -> ExpectationBase: """ A factory method for convenient automatic selection of an Expectation based on the Operator to be converted and backend used to sample the expectation value. Args: operator: The Operator whose expectation value will be taken. backend: The backend which will be used to sample the expectation value. include_custom: Whether the factory will include the (Aer) specific custom expectations if their behavior against the backend might not be as expected. For instance when using Aer qasm_simulator with paulis the Aer snapshot can be used but the outcome lacks shot noise and hence does not intuitively behave overall as people might expect when choosing a qasm_simulator. It is however fast as long as the more state vector like behavior is acceptable. Returns: The expectation algorithm which best fits the Operator and backend. Raises: ValueError: If operator is not of a composition for which we know the best Expectation method. """ backend_to_check = backend.backend if isinstance(backend, QuantumInstance) else backend # pylint: disable=cyclic-import primitives = operator.primitive_strings() if primitives in ({'Pauli'}, {'SparsePauliOp'}): if backend_to_check is None: # If user has Aer but didn't specify a backend, use the Aer fast expectation if has_aer(): from qiskit import Aer backend_to_check = Aer.get_backend('qasm_simulator') # If user doesn't have Aer, use statevector_simulator # for < 16 qubits, and qasm with warning for more. else: if operator.num_qubits <= 16: backend_to_check = BasicAer.get_backend('statevector_simulator') else: logger.warning( '%d qubits is a very large expectation value. ' 'Consider installing Aer to use ' 'Aer\'s fast expectation, which will perform better here. We\'ll use ' 'the BasicAer qasm backend for this expectation to avoid having to ' 'construct the %dx%d operator matrix.', operator.num_qubits, 2 ** operator.num_qubits, 2 ** operator.num_qubits) backend_to_check = BasicAer.get_backend('qasm_simulator') # If the user specified Aer qasm backend and is using a # Pauli operator, use the Aer fast expectation if we are including such # custom behaviors. if is_aer_qasm(backend_to_check) and include_custom: return AerPauliExpectation() # If the user specified a statevector backend (either Aer or BasicAer), # use a converter to produce a # Matrix operator and compute using matmul elif is_statevector_backend(backend_to_check): if operator.num_qubits >= 16: logger.warning( 'Note: Using a statevector_simulator with %d qubits can be very expensive. ' 'Consider using the Aer qasm_simulator instead to take advantage of Aer\'s ' 'built-in fast Pauli Expectation', operator.num_qubits) return MatrixExpectation() # All other backends, including IBMQ, BasicAer QASM, go here. else: return PauliExpectation() elif primitives == {'Matrix'}: return MatrixExpectation() else: raise ValueError('Expectations of Mixed Operators not yet supported.')
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
def to_matrix(self, massive: bool = False) -> np.ndarray: OperatorBase._check_massive("to_matrix", True, self.num_qubits, massive) if isinstance(self.coeff, ParameterExpression): return (self.primitive.to_matrix(sparse=True)).toarray() * self.coeff return (self.primitive.to_matrix(sparse=True) * self.coeff).toarray()
def convert( self, operator: OperatorBase, params: Optional[Dict[Parameter, Union[float, List[float], List[List[float]]]]] = None, ) -> OperatorBase: r""" Converts the Operator to one in which the CircuitStateFns are replaced by DictStateFns or VectorStateFns. Extracts the CircuitStateFns out of the Operator, caches them, calls ``sample_circuits`` below to get their converted replacements, and replaces the CircuitStateFns in operator with the replacement StateFns. Args: operator: The Operator to convert params: A dictionary mapping parameters to either single binding values or lists of binding values. Returns: The converted Operator with CircuitStateFns replaced by DictStateFns or VectorStateFns. Raises: OpflowError: if extracted circuits are empty. """ # check if the operator should be cached op_id = operator.instance_id # op_id = id(operator) if op_id not in self._cached_ops.keys(): # delete cache if we only want to cache one operator if self._caching == "last": self.clear_cache() # convert to circuit and reduce operator_dicts_replaced = operator.to_circuit_op() self._reduced_op_cache = operator_dicts_replaced.reduce() # extract circuits self._circuit_ops_cache = {} self._extract_circuitstatefns(self._reduced_op_cache) if not self._circuit_ops_cache: raise OpflowError( "Circuits are empty. " "Check that the operator is an instance of CircuitStateFn or its ListOp." ) self._transpiled_circ_cache = None self._transpile_before_bind = True else: # load the cached circuits self._reduced_op_cache = self._cached_ops[op_id].reduced_op_cache self._circuit_ops_cache = self._cached_ops[op_id].circuit_ops_cache self._transpiled_circ_cache = self._cached_ops[op_id].transpiled_circ_cache self._transpile_before_bind = self._cached_ops[op_id].transpile_before_bind self._transpiled_circ_templates = self._cached_ops[op_id].transpiled_circ_templates return_as_list = False if params is not None and len(params.keys()) > 0: p_0 = list(params.values())[0] if isinstance(p_0, (list, np.ndarray)): num_parameterizations = len(p_0) param_bindings = [ {param: value_list[i] for param, value_list in params.items()} # type: ignore for i in range(num_parameterizations) ] return_as_list = True else: num_parameterizations = 1 param_bindings = [params] else: param_bindings = None num_parameterizations = 1 # Don't pass circuits if we have in the cache, the sampling function knows to use the cache circs = list(self._circuit_ops_cache.values()) if not self._transpiled_circ_cache else None p_b = cast(List[Dict[Parameter, float]], param_bindings) sampled_statefn_dicts = self.sample_circuits(circuit_sfns=circs, param_bindings=p_b) def replace_circuits_with_dicts(operator, param_index=0): if isinstance(operator, CircuitStateFn): return sampled_statefn_dicts[id(operator)][param_index] elif isinstance(operator, ListOp): return operator.traverse( partial(replace_circuits_with_dicts, param_index=param_index) ) else: return operator # store the operator we constructed, if it isn't stored already if op_id not in self._cached_ops.keys(): op_cache = OperatorCache() op_cache.reduced_op_cache = self._reduced_op_cache op_cache.circuit_ops_cache = self._circuit_ops_cache op_cache.transpiled_circ_cache = self._transpiled_circ_cache op_cache.transpile_before_bind = self._transpile_before_bind op_cache.transpiled_circ_templates = self._transpiled_circ_templates self._cached_ops[op_id] = op_cache if return_as_list: return ListOp( [ replace_circuits_with_dicts(self._reduced_op_cache, param_index=i) for i in range(num_parameterizations) ] ) else: return replace_circuits_with_dicts(self._reduced_op_cache, param_index=0)
def to_density_matrix(self, massive: bool = False) -> np.ndarray: OperatorBase._check_massive('to_density_matrix', True, self.num_qubits, massive) return self.primitive.to_operator().data * self.coeff