herutriana44's picture
First Commit
b7d9967 verified
raw
history blame contribute delete
No virus
10.3 kB
# This code is part of Qiskit.
#
# (C) Copyright IBM 2020, 2023.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
"""CircuitOp Class"""
from typing import Dict, List, Optional, Set, Union, cast
import numpy as np
import qiskit
from qiskit import QuantumCircuit
from qiskit.circuit import Instruction, ParameterExpression
from qiskit.circuit.library import IGate
from qiskit.opflow.list_ops.tensored_op import TensoredOp
from qiskit.opflow.operator_base import OperatorBase
from qiskit.opflow.primitive_ops.primitive_op import PrimitiveOp
from qiskit.quantum_info import Statevector
from qiskit.utils.deprecation import deprecate_func
class CircuitOp(PrimitiveOp):
"""Deprecated: Class for Operators backed by Terra's ``QuantumCircuit`` module."""
primitive: QuantumCircuit
@deprecate_func(
since="0.24.0",
additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.",
)
def __init__(
self,
primitive: Union[Instruction, QuantumCircuit],
coeff: Union[complex, ParameterExpression] = 1.0,
) -> None:
"""
Args:
primitive: The QuantumCircuit which defines the
behavior of the underlying function.
coeff: A coefficient multiplying the primitive
Raises:
TypeError: Unsupported primitive, or primitive has ClassicalRegisters.
"""
if isinstance(primitive, Instruction):
qc = QuantumCircuit(primitive.num_qubits)
qc.append(primitive, qargs=range(primitive.num_qubits))
primitive = qc
if not isinstance(primitive, QuantumCircuit):
raise TypeError(
"CircuitOp can only be instantiated with "
"QuantumCircuit, not {}".format(type(primitive))
)
if len(primitive.clbits) != 0:
raise TypeError("CircuitOp does not support QuantumCircuits with ClassicalRegisters.")
super().__init__(primitive, coeff)
self._coeff = coeff
def primitive_strings(self) -> Set[str]:
return {"QuantumCircuit"}
@property
def num_qubits(self) -> int:
return self.primitive.num_qubits
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, CircuitOp) and self.primitive == other.primitive:
return CircuitOp(self.primitive, coeff=self.coeff + other.coeff)
# Covers all else.
# pylint: disable=cyclic-import
from ..list_ops.summed_op import SummedOp
return SummedOp([self, other])
def adjoint(self) -> "CircuitOp":
return CircuitOp(self.primitive.inverse(), coeff=self.coeff.conjugate())
def equals(self, other: OperatorBase) -> bool:
if not isinstance(other, CircuitOp) or not self.coeff == other.coeff:
return False
return self.primitive == other.primitive
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])
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)
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 __str__(self) -> str:
qc = self.to_circuit()
prim_str = str(qc.draw(output="text"))
if self.coeff == 1.0:
return prim_str
else:
return f"{self.coeff} * {prim_str}"
def assign_parameters(self, param_dict: dict) -> OperatorBase:
param_value = self.coeff
qc = self.primitive
if isinstance(self.coeff, ParameterExpression) or self.primitive.parameters:
unrolled_dict = self._unroll_param_dict(param_dict)
if isinstance(unrolled_dict, list):
from ..list_ops.list_op import ListOp
return ListOp([self.assign_parameters(param_dict) for param_dict in unrolled_dict])
if isinstance(self.coeff, ParameterExpression) and self.coeff.parameters <= set(
unrolled_dict.keys()
):
param_instersection = set(unrolled_dict.keys()) & self.coeff.parameters
binds = {param: unrolled_dict[param] for param in param_instersection}
param_value = float(self.coeff.bind(binds))
# & is set intersection, check if any parameters in unrolled are present in circuit
# This is different from bind_parameters in Terra because they check for set equality
if set(unrolled_dict.keys()) & self.primitive.parameters:
# Only bind the params found in the circuit
param_instersection = set(unrolled_dict.keys()) & self.primitive.parameters
binds = {param: unrolled_dict[param] for param in param_instersection}
qc = self.to_circuit().assign_parameters(binds)
return self.__class__(qc, coeff=param_value)
def eval(
self,
front: Optional[
Union[str, Dict[str, complex], np.ndarray, OperatorBase, Statevector]
] = None,
) -> Union[OperatorBase, complex]:
from ..state_fns import CircuitStateFn
from ..list_ops import ListOp
from .pauli_op import PauliOp
from .matrix_op import MatrixOp
if isinstance(front, ListOp) and front.distributive:
return front.combo_fn(
[self.eval(front.coeff * front_elem) for front_elem in front.oplist]
)
# Composable with circuit
if isinstance(front, (PauliOp, CircuitOp, MatrixOp, CircuitStateFn)):
return self.compose(front)
return self.to_matrix_op().eval(front)
def to_circuit(self) -> QuantumCircuit:
return self.primitive
def to_circuit_op(self) -> "CircuitOp":
return self
def to_instruction(self) -> Instruction:
return self.primitive.to_instruction()
# Warning - modifying immutable object!!
def reduce(self) -> OperatorBase:
if self.primitive.data is not None:
# Need to do this from the end because we're deleting items!
for i in reversed(range(len(self.primitive.data))):
gate = self.primitive.data[i].operation
# Check if Identity or empty instruction (need to check that type is exactly
# Instruction because some gates have lazy gate.definition population)
# pylint: disable=unidiomatic-typecheck
if isinstance(gate, IGate) or (
type(gate) == Instruction and gate.definition.data == []
):
del self.primitive.data[i]
return self
def _expand_dim(self, num_qubits: int) -> "CircuitOp":
return self.permute(list(range(num_qubits, num_qubits + self.num_qubits)))
def permute(self, permutation: List[int]) -> "CircuitOp":
r"""
Permute the qubits of the circuit.
Args:
permutation: A list defining where each qubit should be permuted. The qubit at index
j of the circuit should be permuted to position permutation[j].
Returns:
A new CircuitOp containing the permuted circuit.
"""
new_qc = QuantumCircuit(max(permutation) + 1).compose(self.primitive, qubits=permutation)
return CircuitOp(new_qc, coeff=self.coeff)