예제 #1
0
    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])
예제 #2
0
    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(CircuitOp, new_self)

        if front:
            return other.compose(new_self)
        # pylint: disable=cyclic-import
        from ..operator_globals import Zero
        from ..state_fns import CircuitStateFn
        from .pauli_op import PauliOp
        from .matrix_op import MatrixOp

        if other == Zero ^ new_self.num_qubits:
            return CircuitStateFn(new_self.primitive, coeff=new_self.coeff)

        if isinstance(other, (PauliOp, CircuitOp, MatrixOp)):
            other = other.to_circuit_op()

        if isinstance(other, (CircuitOp, CircuitStateFn)):
            new_qc = other.primitive.compose(new_self.primitive)
            if isinstance(other, CircuitStateFn):
                return CircuitStateFn(new_qc,
                                      is_measurement=other.is_measurement,
                                      coeff=new_self.coeff * other.coeff)
            else:
                return CircuitOp(new_qc, coeff=new_self.coeff * other.coeff)

        return super(CircuitOp, new_self).compose(other)
예제 #3
0
    def tensor(self, other: OperatorBase) -> Union["CircuitOp", TensoredOp]:
        # pylint: disable=cyclic-import
        from .pauli_op import PauliOp
        from .matrix_op import MatrixOp
        if isinstance(other, (PauliOp, CircuitOp, MatrixOp)):
            other = other.to_circuit_op()

        if isinstance(other, CircuitOp):
            new_qc = QuantumCircuit(self.num_qubits + other.num_qubits)
            # NOTE!!! REVERSING QISKIT ENDIANNESS HERE
            new_qc.append(other.to_instruction(),
                          qargs=new_qc.qubits[0:other.primitive.num_qubits])
            new_qc.append(self.to_instruction(),
                          qargs=new_qc.qubits[other.primitive.num_qubits:])
            new_qc = new_qc.decompose()
            return CircuitOp(new_qc, coeff=self.coeff * other.coeff)

        return TensoredOp([self, other])
예제 #4
0
    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)