Skip to content
This repository has been archived by the owner on Dec 7, 2021. It is now read-only.

Added to_gate method on certain operators #1299

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions qiskit/aqua/operators/evolutions/evolved_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import scipy

from qiskit.circuit import ParameterExpression, Instruction
from qiskit.extensions.hamiltonian_gate import HamiltonianGate

from ..operator_base import OperatorBase
from ..primitive_ops.primitive_op import PrimitiveOp
Expand Down Expand Up @@ -161,3 +162,7 @@ def log_i(self, massive: bool = False) -> OperatorBase:
# pylint: disable=arguments-differ
def to_instruction(self, massive: bool = False) -> Instruction:
return self.primitive.to_matrix_op(massive=massive).exp_i() # type: ignore

def to_gate(self, time, label=None) -> HamiltonianGate:
""" Returns a ``Gate`` equivalent to this Operator. """
return HamiltonianGate(self.primitive, time, label=label)
6 changes: 5 additions & 1 deletion qiskit/aqua/operators/primitive_ops/circuit_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import qiskit
from qiskit import QuantumCircuit
from qiskit.circuit.library import IGate
from qiskit.circuit import Instruction, ParameterExpression
from qiskit.circuit import Instruction, ParameterExpression, Gate

from ..operator_base import OperatorBase
from ..list_ops.summed_op import SummedOp
Expand Down Expand Up @@ -206,6 +206,10 @@ def to_circuit_op(self) -> OperatorBase:
def to_instruction(self) -> Instruction:
return self.primitive.to_instruction() # type: ignore

def to_gate(self, label=None) -> Gate:
""" Returns a ``Gate`` equivalent to this Operator. """
return self.primitive.to_gate(label=label) # type: ignore

# Warning - modifying immutable object!!
def reduce(self) -> OperatorBase:
if self.primitive.data is not None: # type: ignore
Expand Down
7 changes: 6 additions & 1 deletion qiskit/aqua/operators/primitive_ops/matrix_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@

from qiskit import QuantumCircuit
from qiskit.quantum_info import Operator
from qiskit.circuit import ParameterExpression, Instruction
from qiskit.circuit import ParameterExpression, Instruction, Gate
from qiskit.extensions.hamiltonian_gate import HamiltonianGate
from qiskit.extensions.unitary import UnitaryGate

from ..operator_base import OperatorBase
from ..primitive_ops.circuit_op import CircuitOp
Expand Down Expand Up @@ -220,5 +221,9 @@ def to_matrix_op(self, massive: bool = False) -> OperatorBase:
def to_instruction(self) -> Instruction:
return (self.coeff * self.primitive).to_instruction() # type: ignore

def to_gate(self, label=None) -> Gate:
""" Returns a ``Gate`` equivalent to this Operator. """
return UnitaryGate(self.primitive, label=label)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this take care of self.coeff and factor it into self.primitive similar to how this is done in to_instruction above.

Can you can check this aspect on the other to_gate implementations. It maybe for some like circuit_op, like in its to_instruction, that its not feasible to include the coeff on the conversion.


def to_legacy_op(self, massive: bool = False) -> MatrixOperator:
return MatrixOperator(self.to_matrix(massive=massive))
6 changes: 5 additions & 1 deletion qiskit/aqua/operators/primitive_ops/pauli_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from scipy.sparse import spmatrix

from qiskit import QuantumCircuit
from qiskit.circuit import ParameterExpression, Instruction
from qiskit.circuit import ParameterExpression, Instruction, Gate
from qiskit.quantum_info import Pauli
from qiskit.circuit.library import RZGate, RYGate, RXGate, XGate, YGate, ZGate, IGate

Expand Down Expand Up @@ -301,6 +301,10 @@ def to_instruction(self) -> Instruction:

return self.to_circuit().to_instruction()

def to_gate(self, label=None) -> Gate:
""" Returns a ``Gate`` equivalent to this Operator. """
return self.to_circuit().to_gate(label=label)

def to_pauli_op(self, massive: bool = False) -> OperatorBase:
return self

Expand Down
7 changes: 7 additions & 0 deletions releasenotes/notes/added-togate-method-deb1a574349fa359.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
features:
- |
Implemented a ``to_gate`` method to ``CircuitOp``, ``PauliOp``,
``EvolvedOp``, and ``MatrixOp``. This method converts operators to
``Gate`` objects as requested in #1091. If the operator is not unitary,
this method throws an error.

44 changes: 43 additions & 1 deletion test/aqua/operators/test_op_construction.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@

from qiskit import QiskitError
from qiskit.aqua import AquaError
from qiskit.circuit import QuantumCircuit, QuantumRegister, Instruction, Parameter, ParameterVector
from qiskit.circuit import (
QuantumCircuit, QuantumRegister, Instruction, Parameter, ParameterVector, Gate
)

from qiskit.extensions.exceptions import ExtensionError
from qiskit.quantum_info import Operator, Pauli, Statevector
Expand Down Expand Up @@ -212,6 +214,29 @@ def test_circuit_op_to_matrix(self):
np.testing.assert_array_almost_equal(
qcop.to_matrix(), scipy.linalg.expm(-0.5j * Z.to_matrix()))

def test_circuit_op_to_gate(self):
""" test CircuitOp.to_gate """
qc = QuantumCircuit(1)
qc.rz(1.0, 0)
qcop = CircuitOp(qc)
with self.subTest('assert to_gate returns Gate'):
self.assertIsInstance(qcop.to_gate(), Gate)

def test_evolved_op_to_gate(self):
""" test EvolvedOp.to_gate """
from qiskit.extensions.hamiltonian_gate import HamiltonianGate

matop = (H ^ 3).to_matrix_op()
evolved_op = EvolvedOp(matop)
with self.subTest('assert to_gate returns Hamiltonian Gate'):
self.assertIsInstance(evolved_op.to_gate(time=1), HamiltonianGate)

matop = (X ^ Y ^ H ^ T).to_matrix_op()
evolved_op = EvolvedOp(matop)
with self.subTest("assert to_gate doesn't work for non Hermitian input"):
with self.assertRaises(ExtensionError):
evolved_op.to_gate(time=1)

def test_matrix_to_instruction(self):
"""Test MatrixOp.to_instruction yields an Instruction object."""
matop = (H ^ 3).to_matrix_op()
Expand All @@ -223,6 +248,23 @@ def test_matrix_to_instruction(self):
with self.assertRaises(ExtensionError):
matop.to_instruction()

def test_matrix_to_gate(self):
"""Test MatrixOp.to_gate yields a Gate object."""
matop = (H ^ 3).to_matrix_op()
with self.subTest('assert to_gate returns Gate'):
self.assertIsInstance(matop.to_gate(), Gate)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there some check we can do on the gate to make sure that the conversion code in to_gate() is working beyond just checking its type. Can we check the value in some way - for instance doing to_matrix on the gate and assuring this is as expected too, or whatever seems reasonable to check that the gate is what we expect beyond just checking type.


matop = ((H ^ 3) + (Z ^ 3)).to_matrix_op()
with self.subTest('non unitary matrix operator throws error'):
with self.assertRaises(ExtensionError):
matop.to_gate()

def test_pauli_op_to_gate(self):
"""Test PauliOp.to_gate yields a Gate object."""
pauli_op = (X ^ Y ^ Z)
with self.subTest('assert to_gate returns Gate'):
self.assertIsInstance(pauli_op.to_gate(), Gate)

def test_adjoint(self):
""" adjoint test """
gnarly_op = 3 * (H ^ I ^ Y).compose(X ^ X ^ Z).tensor(T ^ Z) + \
Expand Down