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 eval( self, front: Optional[ Union[str, Dict[str, complex], np.ndarray, OperatorBase, Statevector] ] = None, ) -> Union[OperatorBase, complex]: if front is None: sparse_vector_state_fn = self.to_spmatrix_op().eval() return sparse_vector_state_fn if not self.is_measurement and isinstance(front, OperatorBase): raise ValueError( 'Cannot compute overlap with StateFn or Operator if not Measurement. Try taking ' 'sf.adjoint() first to convert to measurement.') if isinstance(front, ListOp) and front.distributive: return front.combo_fn([self.eval(front.coeff * front_elem) for front_elem in front.oplist]) # For now, always do this. If it's not performant, we can be more granular. if not isinstance(front, OperatorBase): front = StateFn(front) # pylint: disable=cyclic-import from ..operator_globals import EVAL_SIG_DIGITS # If the primitive is a lookup of bitstrings, # we define all missing strings to have a function value of # zero. if isinstance(front, DictStateFn): return np.round( cast(float, sum([v * front.primitive.get(b, 0) for (b, v) in self.primitive.items()]) * self.coeff * front.coeff), decimals=EVAL_SIG_DIGITS) # All remaining possibilities only apply when self.is_measurement is True if isinstance(front, VectorStateFn): # TODO does it need to be this way for measurement? # return sum([v * front.primitive.data[int(b, 2)] * # np.conj(front.primitive.data[int(b, 2)]) return np.round( cast(float, sum([v * front.primitive.data[int(b, 2)] for (b, v) in self.primitive.items()]) * self.coeff), decimals=EVAL_SIG_DIGITS) from .circuit_state_fn import CircuitStateFn if isinstance(front, CircuitStateFn): # Don't reimplement logic from CircuitStateFn self_adjoint = cast(DictStateFn, self.adjoint()) return np.conj(front.adjoint().eval(self_adjoint.primitive)) * self.coeff from .operator_state_fn import OperatorStateFn if isinstance(front, OperatorStateFn): return cast(Union[OperatorBase, float, complex], front.adjoint().eval(self.adjoint())) # All other OperatorBases go here self_adjoint = cast(DictStateFn, self.adjoint()) adjointed_eval = cast(OperatorBase, front.adjoint().eval(self_adjoint.primitive)) return adjointed_eval.adjoint() * self.coeff
def eval( self, front: Optional[Union[str, dict, np.ndarray, OperatorBase, Statevector]] = None ) -> Union[OperatorBase, complex]: if front is None: matrix = cast(MatrixOp, self.primitive.to_matrix_op()).primitive.data # pylint: disable=cyclic-import from .vector_state_fn import VectorStateFn return VectorStateFn(matrix[0, :]) if not self.is_measurement and isinstance(front, OperatorBase): raise ValueError( "Cannot compute overlap with StateFn or Operator if not Measurement. Try taking " "sf.adjoint() first to convert to measurement.") if not isinstance(front, OperatorBase): front = StateFn(front) if isinstance(self.primitive, ListOp) and self.primitive.distributive: evals = [ OperatorStateFn(op, is_measurement=self.is_measurement).eval(front) for op in self.primitive.oplist ] result = self.primitive.combo_fn(evals) if isinstance(result, list): multiplied = self.primitive.coeff * self.coeff * np.array( result) return multiplied.tolist() return result * self.coeff * self.primitive.coeff # pylint: disable=cyclic-import from .vector_state_fn import VectorStateFn if isinstance(self.primitive, PauliSumOp) and isinstance( front, VectorStateFn): return ( front.primitive.expectation_value(self.primitive.primitive) * self.coeff * front.coeff) # Need an ListOp-specific carve-out here to make sure measurement over a ListOp doesn't # produce two-dimensional ListOp from composing from both sides of primitive. # Can't use isinstance because this would include subclasses. # pylint: disable=unidiomatic-typecheck if isinstance(front, ListOp) and type(front) == ListOp: return front.combo_fn([ self.eval(front.coeff * front_elem) for front_elem in front.oplist ]) # If we evaluate against a circuit, evaluate it to a vector so we # make sure to only do the expensive circuit simulation once if isinstance(front, CircuitStateFn): front = front.eval() return front.adjoint().eval( cast(OperatorBase, self.primitive.eval(front))) * self.coeff
def tensor(self, other: OperatorBase) -> OperatorBase: if isinstance(other, VectorStateFn): return StateFn( self.primitive.tensor(other.primitive), coeff=self.coeff * other.coeff, is_measurement=self.is_measurement, ) return TensoredOp([self, other])
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 eval( self, front: Optional[ Union[str, Dict[str, complex], np.ndarray, Statevector, OperatorBase] ] = None, ) -> Union[OperatorBase, complex]: if front is None: # this object is already a VectorStateFn return self if not self.is_measurement and isinstance(front, OperatorBase): raise ValueError( "Cannot compute overlap with StateFn or Operator if not Measurement. Try taking " "sf.adjoint() first to convert to measurement." ) if isinstance(front, ListOp) and front.distributive: return front.combo_fn( [self.eval(front.coeff * front_elem) for front_elem in front.oplist] ) if not isinstance(front, OperatorBase): front = StateFn(front) # pylint: disable=cyclic-import from ..operator_globals import EVAL_SIG_DIGITS from .operator_state_fn import OperatorStateFn from .circuit_state_fn import CircuitStateFn from .dict_state_fn import DictStateFn if isinstance(front, DictStateFn): return np.round( sum( [ v * self.primitive.data[int(b, 2)] * front.coeff for (b, v) in front.primitive.items() ] ) * self.coeff, decimals=EVAL_SIG_DIGITS, ) if isinstance(front, VectorStateFn): # Need to extract the element or np.array([1]) is returned. return np.round( np.dot(self.to_matrix(), front.to_matrix())[0], decimals=EVAL_SIG_DIGITS ) if isinstance(front, CircuitStateFn): # Don't reimplement logic from CircuitStateFn return np.conj(front.adjoint().eval(self.adjoint().primitive)) * self.coeff if isinstance(front, OperatorStateFn): return front.adjoint().eval(self.primitive) * self.coeff return front.adjoint().eval(self.adjoint().primitive).adjoint() * self.coeff # type: ignore
def tensor(self, other: OperatorBase) -> OperatorBase: # Both dicts if isinstance(other, DictStateFn): new_dict = {k1 + k2: v1 * v2 for ((k1, v1,), (k2, v2)) in itertools.product(self.primitive.items(), other.primitive.items())} return StateFn(new_dict, coeff=self.coeff * other.coeff, is_measurement=self.is_measurement) # pylint: disable=cyclic-import from ..list_ops.tensored_op import TensoredOp return TensoredOp([self, other])
def statefn_replacement_fn( cob_instr_op: PrimitiveOp, dest_pauli_op: Union[PauliOp, PauliSumOp, ListOp]) -> OperatorBase: r""" A built-in convenience replacement function which produces state functions isomorphic to an ``OperatorStateFn`` state function holding the origin ``PauliOp``. Args: cob_instr_op: The basis-change ``CircuitOp``. dest_pauli_op: The destination Pauli type operator. Returns: The ``~CircuitOp @ StateFn`` composition equivalent to a state function defined by the original ``PauliOp``. """ return ComposedOp([cob_instr_op.adjoint(), StateFn(dest_pauli_op)])
def measurement_replacement_fn( cob_instr_op: PrimitiveOp, dest_pauli_op: Union[PauliOp, PauliSumOp, ListOp] ) -> OperatorBase: r""" A built-in convenience replacement function which produces measurements isomorphic to an ``OperatorStateFn`` measurement holding the origin ``PauliOp``. Args: cob_instr_op: The basis-change ``CircuitOp``. dest_pauli_op: The destination Pauli type operator. Returns: The ``~StateFn @ CircuitOp`` composition equivalent to a measurement by the original ``PauliOp``. """ return ComposedOp([StateFn(dest_pauli_op, is_measurement=True), cob_instr_op])
def sample_circuits( self, circuit_sfns: Optional[List[CircuitStateFn]] = None, param_bindings: Optional[List[Dict[Parameter, float]]] = None, ) -> Dict[int, List[StateFn]]: r""" Samples the CircuitStateFns and returns a dict associating their ``id()`` values to their replacement DictStateFn or VectorStateFn. If param_bindings is provided, the CircuitStateFns are broken into their parameterizations, and a list of StateFns is returned in the dict for each circuit ``id()``. Note that param_bindings is provided here in a different format than in ``convert``, and lists of parameters within the dict is not supported, and only binding dicts which are valid to be passed into Terra can be included in this list. Args: circuit_sfns: The list of CircuitStateFns to sample. param_bindings: The parameterizations to bind to each CircuitStateFn. Returns: The dictionary mapping ids of the CircuitStateFns to their replacement StateFns. Raises: OpflowError: if extracted circuits are empty. """ if not circuit_sfns and not self._transpiled_circ_cache: raise OpflowError("CircuitStateFn is empty and there is no cache.") if circuit_sfns: self._transpiled_circ_templates = None if self._statevector or circuit_sfns[0].from_operator: circuits = [op_c.to_circuit(meas=False) for op_c in circuit_sfns] else: circuits = [op_c.to_circuit(meas=True) for op_c in circuit_sfns] try: self._transpiled_circ_cache = self.quantum_instance.transpile( circuits, pass_manager=self.quantum_instance.unbound_pass_manager ) except QiskitError: logger.debug( r"CircuitSampler failed to transpile circuits with unbound " r"parameters. Attempting to transpile only when circuits are bound " r"now, but this can hurt performance due to repeated transpilation." ) self._transpile_before_bind = False self._transpiled_circ_cache = circuits else: circuit_sfns = list(self._circuit_ops_cache.values()) if param_bindings is not None: if self._param_qobj: start_time = time() ready_circs = self._prepare_parameterized_run_config(param_bindings) end_time = time() logger.debug("Parameter conversion %.5f (ms)", (end_time - start_time) * 1000) else: start_time = time() ready_circs = [ circ.assign_parameters(_filter_params(circ, binding)) for circ in self._transpiled_circ_cache for binding in param_bindings ] end_time = time() logger.debug("Parameter binding %.5f (ms)", (end_time - start_time) * 1000) else: ready_circs = self._transpiled_circ_cache # run transpiler passes on bound circuits if self._transpile_before_bind and self.quantum_instance.bound_pass_manager is not None: ready_circs = self.quantum_instance.transpile( ready_circs, pass_manager=self.quantum_instance.bound_pass_manager ) results = self.quantum_instance.execute( ready_circs, had_transpiled=self._transpile_before_bind ) if param_bindings is not None and self._param_qobj: self._clean_parameterized_run_config() # Wipe parameterizations, if any # self.quantum_instance._run_config.parameterizations = None sampled_statefn_dicts = {} for i, op_c in enumerate(circuit_sfns): # Taking square root because we're replacing a statevector # representation of probabilities. reps = len(param_bindings) if param_bindings is not None else 1 c_statefns = [] for j in range(reps): circ_index = (i * reps) + j circ_results = results.data(circ_index) if "expval_measurement" in circ_results: avg = circ_results["expval_measurement"] # Will be replaced with just avg when eval is called later num_qubits = circuit_sfns[0].num_qubits result_sfn = DictStateFn( "0" * num_qubits, coeff=avg * op_c.coeff, is_measurement=op_c.is_measurement, from_operator=op_c.from_operator, ) elif self._statevector: result_sfn = StateFn( op_c.coeff * results.get_statevector(circ_index), is_measurement=op_c.is_measurement, ) else: shots = self.quantum_instance._run_config.shots result_sfn = DictStateFn( { b: (v / shots) ** 0.5 * op_c.coeff for (b, v) in results.get_counts(circ_index).items() }, is_measurement=op_c.is_measurement, from_operator=op_c.from_operator, ) if self._attach_results: result_sfn.execution_results = circ_results c_statefns.append(result_sfn) sampled_statefn_dicts[id(op_c)] = c_statefns return sampled_statefn_dicts
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