Skip to content

Commit

Permalink
Use Operator rather than unitary simulator to convert circuit to unit…
Browse files Browse the repository at this point in the history
…ary matrix (qiskit-community#1224)

* Fix factor of 2 error in converting exp_i to rotation gates

* Make CircuitOp use Operator rather than unitary simulator to get matrix

The unitary simulator does not account for the global phase in a
circuit. qiskit.quantum_info.Operator does account for global phase
when converting (when possible) a circuit to a unitary matrix.

Closes qiskit-community#1218

* Remove note on reversing order of qubits

Using Operator(QuantumCircuit) gives the same qubit ordering
that the previous unitary-simulator-based code did. I removed
the comment entirely because the behavior depends only on
Operator(QuantumCircuit), which is not obscure.
  • Loading branch information
jlapeyre committed Sep 15, 2020
1 parent e180bcc commit d31d4f3
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 12 deletions.
12 changes: 3 additions & 9 deletions qiskit/aqua/operators/primitive_ops/circuit_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
import logging
import numpy as np

from qiskit import QuantumCircuit, BasicAer, execute
import qiskit
from qiskit import QuantumCircuit
from qiskit.circuit.library import IGate
from qiskit.circuit import Instruction, ParameterExpression

Expand Down Expand Up @@ -139,14 +140,7 @@ def to_matrix(self, massive: bool = False) -> np.ndarray:
' in this case {0}x{0} elements.'
' Set massive=True if you want to proceed.'.format(2 ** self.num_qubits))

# NOTE: not reversing qubits!! We generally reverse endianness when converting between
# circuit or Pauli representation and matrix representation, but we don't need to here
# because the Unitary simulator already presents the endianness of the circuit unitary in
# forward endianness.
unitary_backend = BasicAer.get_backend('unitary_simulator')
unitary = execute(self.to_circuit(),
unitary_backend,
optimization_level=0).result().get_unitary()
unitary = qiskit.quantum_info.Operator(self.to_circuit()).data
# pylint: disable=cyclic-import
from ..operator_globals import EVAL_SIG_DIGITS
return np.round(unitary * self.coeff, decimals=EVAL_SIG_DIGITS)
Expand Down
6 changes: 3 additions & 3 deletions qiskit/aqua/operators/primitive_ops/pauli_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,13 +220,13 @@ def exp_i(self) -> OperatorBase:
else self.coeff
# Y rotation
if corrected_x[sig_qubit_index] and corrected_z[sig_qubit_index]:
rot_op = PrimitiveOp(RYGate(coeff))
rot_op = PrimitiveOp(RYGate(2 * coeff))
# Z rotation
elif corrected_z[sig_qubit_index]:
rot_op = PrimitiveOp(RZGate(coeff))
rot_op = PrimitiveOp(RZGate(2 * coeff))
# X rotation
elif corrected_x[sig_qubit_index]:
rot_op = PrimitiveOp(RXGate(coeff))
rot_op = PrimitiveOp(RXGate(2 * coeff))

from ..operator_globals import I
left_pad = I.tensorpower(sig_qubit_index)
Expand Down
9 changes: 9 additions & 0 deletions releasenotes/notes/pauli-expi-c0c97395190622c0.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
fixes:
- |
Make PauliOp.exp_i() generate the correct matrix with the following changes.
1) There was previously an error in the phase of a factor of 2.
2) The global phase was ignored when converting the circuit
to a matrix. We now use qiskit.quantum_info.Operator, which is
generally useful for converting a circuit to a unitary matrix,
when possible.
8 changes: 8 additions & 0 deletions test/aqua/operators/test_evolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import numpy as np
import scipy.linalg

import qiskit
from qiskit.circuit import ParameterVector, Parameter

from qiskit.aqua.operators import (X, Y, Z, I, CX, H, ListOp, CircuitOp, Zero, EvolutionFactory,
Expand All @@ -29,6 +30,13 @@
class TestEvolution(QiskitAquaTestCase):
"""Evolution tests."""

def test_exp_i(self):
""" exponential of Pauli test """
op = Z.exp_i()
gate = op.to_circuit().data[0][0]
self.assertIsInstance(gate, qiskit.circuit.library.RZGate)
self.assertEqual(gate.params[0], 2)

def test_pauli_evolution(self):
""" pauli evolution test """
op = (-1.052373245772859 * I ^ I) + \
Expand Down
9 changes: 9 additions & 0 deletions test/aqua/operators/test_op_construction.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

from test.aqua import QiskitAquaTestCase
import itertools
import scipy
from scipy.stats import unitary_group
import numpy as np
from ddt import ddt, data
Expand Down Expand Up @@ -201,6 +202,14 @@ def test_to_matrix(self):
np.testing.assert_array_almost_equal(
op6.to_matrix(), op5.to_matrix() + Operator.from_label('+r').data)

def test_circuit_op_to_matrix(self):
""" test CircuitOp.to_matrix """
qc = QuantumCircuit(1)
qc.rz(1.0, 0)
qcop = CircuitOp(qc)
np.testing.assert_array_almost_equal(
qcop.to_matrix(), scipy.linalg.expm(-0.5j * Z.to_matrix()))

def test_matrix_to_instruction(self):
"""Test MatrixOp.to_instruction yields an Instruction object."""
matop = (H ^ 3).to_matrix_op()
Expand Down

0 comments on commit d31d4f3

Please sign in to comment.