Skip to content

Adding the initial implementation for pauliexp gates support. #7374

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
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
34 changes: 34 additions & 0 deletions cirq-ionq/cirq_ionq/serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,19 @@

import dataclasses
import json
import math
from typing import Any, Callable, cast, Collection, Iterator, Sequence, TYPE_CHECKING

import numpy as np

import cirq

from cirq.devices import line_qubit
from cirq.ops.pauli_string_phasor import PauliStringPhasorGate
from cirq_ionq.ionq_exceptions import IonQSerializerMixedGatesetsException
from cirq_ionq.ionq_native_gates import GPI2Gate, GPIGate, MSGate, ZZGate


if TYPE_CHECKING:
import sympy

Expand Down Expand Up @@ -79,6 +83,7 @@ def __init__(self, atol: float = 1e-8):
cirq.HPowGate: self._serialize_h_pow_gate,
cirq.SwapPowGate: self._serialize_swap_gate,
cirq.MeasurementGate: self._serialize_measurement_gate,
cirq.ops.pauli_string_phasor.PauliStringPhasorGate: self._serialize_pauli_string_phasor_gate,
# These gates can't be used with any of the non-measurement gates above
# Rather than validating this here, we rely on the IonQ API to report failure.
GPIGate: self._serialize_gpi_gate,
Expand Down Expand Up @@ -277,6 +282,35 @@ def _serialize_h_pow_gate(self, gate: cirq.HPowGate, targets: Sequence[int]) ->
return {'gate': 'h', 'targets': targets}
return None

def _serialize_pauli_string_phasor_gate(
self, gate: PauliStringPhasorGate, targets: Sequence[int]
) -> dict | None:
paulis = {0: "I", 1: "X", 2: "Y", 3: "Z"}
# Cirq uses big-endian ordering while IonQ API uses little-endian ordering.
big_endian_pauli_string = ''.join(
[paulis[pindex] for pindex in gate.dense_pauli_string.pauli_mask]
)
little_endian_pauli_string = big_endian_pauli_string[::-1]
pauli_string_coefficient = gate.dense_pauli_string.coefficient
if pauli_string_coefficient.imag != 0:
raise ValueError(
'IonQ pauliexp gates does not support complex evolution coefficients. '
f'Found in a PauliStringPhasorGate a complex evolution coefficient {pauli_string_coefficient} for the associated DensePauliString.'
)
coefficients = [pauli_string_coefficient.real]
# I am ignoring here the global phase of i * pi * (gate.exponent_neg + gate.exponent_pos) / 2
time = math.pi * (gate.exponent_neg - gate.exponent_pos) / 2
seralized_gate = {
'gate': 'pauliexp',
'terms': [little_endian_pauli_string],
"coefficients": coefficients,
'targets': targets,
'time': time,
}
# TODO: remove this print statement once the serializer is stable.
print(seralized_gate)
return seralized_gate

# These could potentially be using serialize functions on the gates themselves.
def _serialize_gpi_gate(self, gate: GPIGate, targets: Sequence[int]) -> dict | None:
return {'gate': 'gpi', 'target': targets[0], 'phase': gate.phase}
Expand Down
71 changes: 70 additions & 1 deletion cirq-ionq/cirq_ionq/serializer_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from __future__ import annotations

import json

import math
import numpy as np
import pytest
import sympy
Expand Down Expand Up @@ -527,6 +527,75 @@ def test_serialize_many_circuits_swap_gate():
_ = serializer.serialize_many_circuits([circuit])


def test_serialize_single_circuit_pauli_string_phasor_gate():
q0, q1, q2 = cirq.LineQubit.range(3)
serializer = ionq.Serializer()
pauli_string = cirq.Z(q0) * cirq.I(q1) * cirq.Y(q2)
exponent_neg = 0.25
exponent_pos = -0.5
circuit = cirq.Circuit(
cirq.PauliStringPhasor(pauli_string, exponent_neg=exponent_neg, exponent_pos=exponent_pos)
)
result = serializer.serialize_single_circuit(circuit)

# compare time floating point values with a tolerance
expected_time = math.pi * (exponent_neg - exponent_pos) / 2
assert result.body['circuit'][0]['time'] == pytest.approx(expected_time, abs=1e-10)

result.body['circuit'][0].pop('time')
assert result == ionq.SerializedProgram(
body={
'gateset': 'qis',
'qubits': 3,
'circuit': [
{'gate': 'pauliexp', 'terms': ['YZ'], 'coefficients': [1.0], 'targets': [0, 2]}
],
},
metadata={},
settings={},
)


def test_serialize_many_circuits_pauli_string_phasor_gate():
q0, q1, q2 = cirq.LineQubit.range(3)
serializer = ionq.Serializer()
pauli_string = cirq.Z(q0) * cirq.I(q1) * cirq.Y(q2)
exponent_neg = 0.25
exponent_pos = -0.5
circuit = cirq.Circuit(
cirq.PauliStringPhasor(pauli_string, exponent_neg=exponent_neg, exponent_pos=exponent_pos)
)
result = serializer.serialize_many_circuits([circuit])

# compare time floating point values with a tolerance
expected_time = math.pi * (exponent_neg - exponent_pos) / 2
assert result.body['circuits'][0]['circuit'][0]['time'] == pytest.approx(
expected_time, abs=1e-10
)

result.body['circuits'][0]['circuit'][0].pop('time')
assert result == ionq.SerializedProgram(
body={
'gateset': 'qis',
'qubits': 3,
'circuits': [
{
'circuit': [
{
'gate': 'pauliexp',
'terms': ['YZ'],
'coefficients': [1.0],
'targets': [0, 2],
}
]
}
],
},
metadata={'measurements': '[{}]', 'qubit_numbers': '[3]'},
settings={},
)


def test_serialize_single_circuit_measurement_gate():
q0 = cirq.LineQubit(0)
circuit = cirq.Circuit(cirq.measure(q0, key='tomyheart'))
Expand Down
Loading