Skip to content
Open
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
3 changes: 2 additions & 1 deletion qiskit_ionq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
TrappedIonOptimizerPluginCommuteGpi2ThroughMs,
)
from .version import __version__
from .ionq_gates import GPIGate, GPI2Gate, MSGate, ZZGate
from .ionq_gates import GPIGate, GPI2Gate, VirtualZGate, MSGate, ZZGate
from .constants import ErrorMitigation
from .ionq_equivalence_library import add_equivalences

Expand All @@ -50,6 +50,7 @@
"GPIGate",
"GPI2Gate",
"MSGate",
"VirtualZGate",
"ZZGate",
"ErrorMitigation",
"add_equivalences",
Expand Down
1 change: 1 addition & 0 deletions qiskit_ionq/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@
ionq_native_basis_gates = [
"gpi",
"gpi2",
"gz", # VirtualZ gate (RZ)
"ms", # Pairwise MS gate
"zz", # ZZ gate
]
Expand Down
12 changes: 11 additions & 1 deletion qiskit_ionq/ionq_equivalence_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
RZZGate,
UGate,
)
from .ionq_gates import GPIGate, GPI2Gate, MSGate, ZZGate
from .ionq_gates import GPIGate, GPI2Gate, VirtualZGate, MSGate, ZZGate


# 1q gates
Expand Down Expand Up @@ -81,6 +81,15 @@ def gpi2_gate_equivalence() -> None:
SessionEquivalenceLibrary.add_equivalence(GPI2Gate(phi), circ)


def virtualz_gate_equivalence() -> None:
"""VirtualZ(θ) -> RZ(θ) (for Aer/QIS simulation)."""
q = QuantumRegister(1, "q")
theta = Parameter("theta_param")
circ = QuantumCircuit(q)
circ.append(RZGate(theta), [0])
SessionEquivalenceLibrary.add_equivalence(VirtualZGate(theta), circ)


# 2q native gates -> standard rotations (helps simulation & pattern matching)


Expand Down Expand Up @@ -126,6 +135,7 @@ def add_equivalences() -> None:
u_gate_equivalence()
gpi_gate_equivalence()
gpi2_gate_equivalence()
virtualz_gate_equivalence()
# 2q
zz_gate_equivalence()
# CX (both backends)
Expand Down
37 changes: 37 additions & 0 deletions qiskit_ionq/ionq_gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,43 @@ def __array__(self, dtype=None, copy=None):
return arr


class VirtualZGate(Gate):
r"""Single-qubit VirtualZ (GZ) gate.
**Circuit symbol:**
.. parsed-literal::
┌───────┐
q_0: ┤ GZ(θ) ├
└───────┘
**Matrix Representation:**

.. math::

GZ(\theta) =
\begin{pmatrix}
e^{-i\theta/2} & 0 \\
0 & e^{i\theta/2}
\end{pmatrix}

Equivalent to RZ(θ).
"""

def __init__(self, theta: ParameterValueType, label: Optional[str] = None):
"""Create new VirtualZ gate."""
super().__init__("gz", 1, [theta], label=label)

def __array__(self, dtype=None, copy=None):
"""Return a numpy array for the VirtualZ gate."""
theta = float(self.params[0])
arr = np.array(
[[np.exp(-1j * theta / 2), 0], [0, np.exp(1j * theta / 2)]]
)
if dtype is not None:
arr = arr.astype(dtype, copy=False)
if copy is True:
return arr.copy()
return arr


class MSGate(Gate):
r"""Entangling 2-Qubit MS gate.
**Circuit symbol:**
Expand Down
5 changes: 4 additions & 1 deletion test/helpers/test_qiskit_to_ionq.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
compress_to_metadata_string,
get_user_agent,
)
from qiskit_ionq.ionq_gates import GPIGate, GPI2Gate, MSGate, ZZGate
from qiskit_ionq.ionq_gates import GPIGate, GPI2Gate, VirtualZGate, MSGate, ZZGate
from qiskit_ionq.constants import ErrorMitigation


Expand Down Expand Up @@ -320,6 +320,7 @@ def test_native_circuit_transpile(simulator_backend):
circ.append(GPIGate(0.1), [0])
circ.append(GPI2Gate(0.2), [1])
circ.append(MSGate(0.2, 0.3, 0.25), [1, 2])
circ.append(VirtualZGate(0.3), [0])
circ.append(ZZGate(0.4), [0, 2])

with pytest.raises(TranspilerError) as exc_info:
Expand All @@ -338,6 +339,7 @@ def test_full_native_circuit(simulator_backend):
qc.append(GPIGate(0.1), [0])
qc.append(GPI2Gate(0.2), [1])
qc.append(MSGate(0.2, 0.3, 0.25), [1, 2])
qc.append(VirtualZGate(0.3), [0])
qc.append(ZZGate(0.4), [0, 2])
ionq_json = qiskit_to_ionq(
qc,
Expand Down Expand Up @@ -378,6 +380,7 @@ def test_full_native_circuit(simulator_backend):
{"gate": "gpi", "target": 0, "phase": 0.1},
{"gate": "gpi2", "target": 1, "phase": 0.2},
{"gate": "ms", "targets": [1, 2], "phases": [0.2, 0.3], "angle": 0.25},
{"gate": "gz", "targets": [0], "phase": 0.3},
{"gate": "zz", "angle": 0.4, "targets": [0, 2]},
],
},
Expand Down
20 changes: 18 additions & 2 deletions test/ionq_gates/test_gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@

import pytest

from qiskit.circuit.library import XGate, YGate, RXGate, RYGate, HGate
from qiskit_ionq import GPIGate, GPI2Gate, MSGate, ZZGate
from qiskit.circuit.library import XGate, YGate, RXGate, RYGate, RZGate, HGate
from qiskit_ionq import GPIGate, GPI2Gate, VirtualZGate, MSGate, ZZGate


@pytest.mark.parametrize("gate,phase", [(XGate(), 0), (YGate(), 0.25)])
Expand Down Expand Up @@ -97,6 +97,22 @@ def test_ms_inverse(params):
np.testing.assert_array_almost_equal(mat.dot(mat.conj().T), np.identity(4))


@pytest.mark.parametrize("theta", [0, 0.1, 0.4, np.pi / 2, np.pi, 2 * np.pi])
def test_virtualz_equivalence(theta):
"""Tests that VirtualZGate matches RZGate at various angles."""
gz_gate = VirtualZGate(theta)
rz_gate = RZGate(theta)
np.testing.assert_array_almost_equal(gz_gate.to_matrix(), rz_gate.to_matrix())


@pytest.mark.parametrize("theta", [0, 0.1, 0.4, np.pi / 2, np.pi, 2 * np.pi])
def test_gz_inverse(theta):
"""Tests that the VirtualZ gate is unitary."""
gate = VirtualZGate(theta)
mat = np.array(gate)
np.testing.assert_array_almost_equal(mat.dot(mat.conj().T), np.identity(2))


@pytest.mark.parametrize(
"angle",
[0, 0.1, 0.4, np.pi / 2, np.pi, 2 * np.pi],
Expand Down
Loading