Skip to content

Commit

Permalink
Add serialization support to more gates (quantumlib#6479)
Browse files Browse the repository at this point in the history
This PR adds serializatoin support to
- IdentityGate => fixes quantumlib#4833
- HPowGate => allows sending circuits with cirq.H to backend
- changes the handling of SingleQubitCliffordGate from automatic conversion to PhasedXZGate to have its own proto (originally proposed in quantumlib#6418)
  • Loading branch information
NoureldinYosri authored Mar 8, 2024
1 parent 4513892 commit bc76660
Show file tree
Hide file tree
Showing 13 changed files with 338 additions and 65 deletions.
7 changes: 6 additions & 1 deletion cirq-core/cirq/testing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,12 @@

from cirq.testing.equivalent_repr_eval import assert_equivalent_repr

from cirq.testing.gate_features import SingleQubitGate, TwoQubitGate, ThreeQubitGate
from cirq.testing.gate_features import (
SingleQubitGate,
TwoQubitGate,
ThreeQubitGate,
DoesNotSupportSerializationGate,
)

from cirq.testing.json import assert_json_roundtrip_works

Expand Down
10 changes: 10 additions & 0 deletions cirq-core/cirq/testing/gate_features.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,13 @@ class ThreeQubitGate(raw_types.Gate):

def _num_qubits_(self) -> int:
return 3


class DoesNotSupportSerializationGate(raw_types.Gate):
"""A gate that can't be serialized."""

def __init__(self, n_qubits: int = 1):
self.n_qubits = n_qubits

def _num_qubits_(self) -> int:
return self.n_qubits
23 changes: 23 additions & 0 deletions cirq-google/cirq_google/api/v2/program.proto
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,9 @@ message Operation {
WaitGate waitgate = 16;
InternalGate internalgate = 17;
CouplerPulseGate couplerpulsegate = 18;
IdentityGate identitygate = 19;
HPowGate hpowgate = 20;
SingleQubitCliffordGate singlequbitcliffordgate = 21;
}

// Map from the argument name to the Argument needed to fully specify
Expand Down Expand Up @@ -434,4 +437,24 @@ message CouplerPulseGate{
optional FloatArg coupling_mhz = 4;
optional FloatArg q0_detune_mhz = 5;
optional FloatArg q1_detune_mhz = 6;
}

message CliffordTableau {
optional int32 num_qubits = 1; // Number of qubits the CliffordTableau acts on.
optional int32 initial_state = 2; // The initial state.
repeated bool rs = 3; // A flattened version of the `rs` array.
repeated bool xs = 4; // A flattened version of the `xs` array.
repeated bool zs = 5; // A flattened version of the `zs` array.
}

message SingleQubitCliffordGate {
CliffordTableau tableau = 1;
}

message IdentityGate {
repeated uint32 qid_shape = 1;
}

message HPowGate {
FloatArg exponent = 1;
}
104 changes: 56 additions & 48 deletions cirq-google/cirq_google/api/v2/program_pb2.py

Large diffs are not rendered by default.

108 changes: 105 additions & 3 deletions cirq-google/cirq_google/api/v2/program_pb2.pyi

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion cirq-google/cirq_google/devices/grid_device_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,9 @@ def test_grid_device_validate_operations_negative():
device.validate_operation(cirq.CZ(q00, q10))

with pytest.raises(ValueError, match='gate which is not supported'):
device.validate_operation(cirq.H(device_info.grid_qubits[0]))
device.validate_operation(
cirq.testing.DoesNotSupportSerializationGate()(device_info.grid_qubits[0])
)


@pytest.mark.parametrize(
Expand Down
4 changes: 3 additions & 1 deletion cirq-google/cirq_google/engine/engine_processor_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,9 @@ def test_get_device():
with pytest.raises(ValueError):
device.validate_operation(cirq.X(cirq.GridQubit(1, 2)))
with pytest.raises(ValueError):
device.validate_operation(cirq.H(cirq.GridQubit(0, 0)))
device.validate_operation(
cirq.testing.DoesNotSupportSerializationGate()(cirq.GridQubit(0, 0))
)
with pytest.raises(ValueError):
device.validate_operation(cirq.CZ(cirq.GridQubit(1, 1), cirq.GridQubit(2, 2)))

Expand Down
4 changes: 3 additions & 1 deletion cirq-google/cirq_google/engine/engine_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,8 @@ def test_get_engine_device(get_processor):
with pytest.raises(ValueError):
device.validate_operation(cirq.X(cirq.GridQubit(1, 2)))
with pytest.raises(ValueError):
device.validate_operation(cirq.H(cirq.GridQubit(0, 0)))
device.validate_operation(
cirq.testing.DoesNotSupportSerializationGate()(cirq.GridQubit(0, 0))
)
with pytest.raises(ValueError):
device.validate_operation(cirq.CZ(cirq.GridQubit(1, 1), cirq.GridQubit(2, 2)))
8 changes: 6 additions & 2 deletions cirq-google/cirq_google/engine/virtual_engine_factory_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ def _test_processor(processor: cg.engine.abstract_processor.AbstractProcessor):
circuit = cirq.Circuit(cirq.X(bad_qubit), cirq.measure(bad_qubit))
with pytest.raises(ValueError, match='Qubit not on device'):
_ = processor.run(circuit, repetitions=100)
circuit = cirq.Circuit(cirq.H(good_qubit), cirq.measure(good_qubit))
circuit = cirq.Circuit(
cirq.testing.DoesNotSupportSerializationGate()(good_qubit), cirq.measure(good_qubit)
)
with pytest.raises(ValueError, match='Cannot serialize op'):
_ = processor.run(circuit, repetitions=100)

Expand Down Expand Up @@ -195,7 +197,9 @@ def test_create_default_noisy_quantum_virtual_machine():
with pytest.raises(ValueError, match='Qubit not on device'):
_ = processor.run(circuit, repetitions=100)
good_qubit = cirq.GridQubit(5, 4)
circuit = cirq.Circuit(cirq.H(good_qubit), cirq.measure(good_qubit))
circuit = cirq.Circuit(
cirq.testing.DoesNotSupportSerializationGate()(good_qubit), cirq.measure(good_qubit)
)
with pytest.raises(ValueError, match='.* contains a gate which is not supported.'):
_ = processor.run(circuit, repetitions=100)
device_specification = processor.get_device_specification()
Expand Down
43 changes: 43 additions & 0 deletions cirq-google/cirq_google/serialization/arg_func_langs.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import sympy
from cirq_google.api import v2
from cirq_google.ops import InternalGate
from cirq.qis import CliffordTableau

SUPPORTED_FUNCTIONS_FOR_LANGUAGE: Dict[Optional[str], FrozenSet[str]] = {
'': frozenset(),
Expand Down Expand Up @@ -455,3 +456,45 @@ def internal_gate_from_proto(
num_qubits=int(msg.num_qubits),
**gate_args,
)


def clifford_tableau_arg_to_proto(
value: CliffordTableau, *, out: Optional[v2.program_pb2.CliffordTableau] = None
):
"""Writes an CliffordTableau object into an CliffordTableau proto.
Args:
value: The gate to encode.
arg_function_language: The language to use when encoding functions. If
this is set to None, it will be set to the minimal language
necessary to support the features that were actually used.
out: The proto to write the result into. Defaults to a new instance.
Returns:
The proto that was written into.
"""
msg = v2.program_pb2.CliffordTableau() if out is None else out
msg.num_qubits = value.n
msg.initial_state = value.initial_state
msg.xs.extend(value.xs.flatten())
msg.rs.extend(value.rs.flatten())
msg.zs.extend(value.zs.flatten())
return msg


def clifford_tableau_from_proto(
msg: v2.program_pb2.CliffordTableau, arg_function_language: str
) -> CliffordTableau:
"""Extracts a CliffordTableau object from a CliffordTableau proto.
Args:
msg: The proto containing a serialized value.
arg_function_language: The `arg_function_language` field from
`Program.Language`.
Returns:
The deserialized InternalGate object.
"""
return CliffordTableau(
num_qubits=msg.num_qubits,
initial_state=msg.initial_state,
rs=np.array(msg.rs, dtype=bool) if msg.rs else None,
xs=np.array(msg.xs, dtype=bool).reshape((2 * msg.num_qubits, -1)) if msg.xs else None,
zs=np.array(msg.zs, dtype=bool).reshape((2 * msg.num_qubits, -1)) if msg.zs else None,
)
27 changes: 27 additions & 0 deletions cirq-google/cirq_google/serialization/arg_func_langs_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@
internal_gate_from_proto,
ARG_LIKE,
LANGUAGE_ORDER,
clifford_tableau_arg_to_proto,
clifford_tableau_from_proto,
)
from cirq_google.api import v2
from cirq.qis import CliffordTableau


@pytest.mark.parametrize(
Expand Down Expand Up @@ -249,3 +252,27 @@ def test_invalid_list():

with pytest.raises(ValueError):
_ = arg_to_proto([1.0, ''])


@pytest.mark.parametrize('lang', LANGUAGE_ORDER)
def test_clifford_tableau(lang):
tests = [
CliffordTableau(
1,
0,
rs=np.array([True, False], dtype=bool),
xs=np.array([[True], [False]], dtype=bool),
zs=np.array([[True], [False]], dtype=bool),
),
CliffordTableau(
1,
1,
rs=np.array([True, True], dtype=bool),
xs=np.array([[True], [False]], dtype=bool),
zs=np.array([[False], [False]], dtype=bool),
),
]
for ct in tests:
proto = clifford_tableau_arg_to_proto(ct)
tableau = clifford_tableau_from_proto(proto, lang)
assert tableau == ct
Loading

0 comments on commit bc76660

Please sign in to comment.