diff --git a/blueqat/circuit_funcs/__init__.py b/blueqat/circuit_funcs/__init__.py new file mode 100644 index 0000000..c6ac7a7 --- /dev/null +++ b/blueqat/circuit_funcs/__init__.py @@ -0,0 +1,2 @@ +"""This module is EXPERIMENTAL FEATURES. Provided functions may be removed or destructive changed. +This module provides functions that handles Circuits.""" diff --git a/blueqat/circuit_funcs/flatten.py b/blueqat/circuit_funcs/flatten.py new file mode 100644 index 0000000..e08637c --- /dev/null +++ b/blueqat/circuit_funcs/flatten.py @@ -0,0 +1,15 @@ +from .. import Circuit +from .. import gate as g + +def flatten(c: Circuit) -> Circuit: + """expands slice and multiple targets into single target""" + n_qubits = c.n_qubits + ops = [] + for op in c.ops: + if isinstance(op, (g.OneQubitGate, g.Measurement, g.Reset)): + ops += [op.create(t, op.params) for t in op.target_iter(n_qubits)] + elif isinstance(op, g.TwoQubitGate): + ops += [op.create(t, op.params) for t in op.control_target_iter(n_qubits)] + else: + raise ValueError(f"Cannot process operation {op.lowername}.") + return Circuit(n_qubits, ops) diff --git a/blueqat/circuit_funcs/json_serializer.py b/blueqat/circuit_funcs/json_serializer.py new file mode 100644 index 0000000..f43ade8 --- /dev/null +++ b/blueqat/circuit_funcs/json_serializer.py @@ -0,0 +1,83 @@ +"""Defines JSON serializer and deserializer.""" +import typing +from blueqat import Circuit + +from ..gateset import create +from .flatten import flatten + +SCHEMA_NAME = 'blueqat-circuit' +SCHEMA_VERSION = "1" + +if typing.TYPE_CHECKING: + from blueqat.gate import Operation + try: + from typing import Any, Dict, List, TypedDict + except ImportError: + CircuitJsonDict = Dict[str, Any] + OpJsonDict = Dict[str, Any] + else: + class SchemaJsonDict(TypedDict): + """Schema header for detect data type""" + name: str + version: str + + class OpJsonDict(TypedDict): + """Data type of Operation""" + name: str + params: List[float] + targets: List[int] + + class CircuitJsonDict(TypedDict): + """Data type of Circuit""" + schema: SchemaJsonDict + n_qubits: int + ops: List[OpJsonDict] + + +def serialize(c: Circuit) -> 'CircuitJsonDict': + """Serialize Circuit into JSON type dict. + + In this implementation, serialized circuit is flattened. + However, it's not specifications of JSON schema. + """ + def serialize_op(op: 'Operation') -> 'OpJsonDict': + targets = op.targets + if isinstance(targets, slice): + raise TypeError('Not flatten circuit.') + if isinstance(targets, int): + targets = [targets] + if isinstance(targets, tuple): + targets = list(targets) + return { + 'name': str(op.lowername), + 'params': [float(p) for p in op.params], + 'targets': targets + } + + c = flatten(c) + return { + 'schema': { + 'name': SCHEMA_NAME, + 'version': SCHEMA_VERSION + }, + 'n_qubits': c.n_qubits, + 'ops': [serialize_op(op) for op in c.ops] + } + + +def deserialize(data: 'CircuitJsonDict') -> Circuit: + """Deserialize JSON type dict into Circuit""" + def make_op(opdata: 'OpJsonDict') -> 'Operation': + return create(opdata['name'], + tuple(opdata['targets']), + tuple(float(p) for p in opdata['params'])) + schema = data.get('schema', {}) + if schema.get('name', '') != SCHEMA_NAME: + raise ValueError('Invalid schema') + if schema.get('version', '') != SCHEMA_VERSION: + raise ValueError('Unknown schema version') + n_qubits = data['n_qubits'] + ops = data['ops'] + return Circuit(n_qubits, [ + make_op(opdata) for opdata in ops + ]) diff --git a/tests/test_json.py b/tests/test_json.py new file mode 100644 index 0000000..cf1c6e5 --- /dev/null +++ b/tests/test_json.py @@ -0,0 +1,107 @@ +import json +import numpy as np +from blueqat import Circuit +from blueqat.circuit_funcs.json_serializer import serialize, deserialize +from blueqat.circuit_funcs.flatten import flatten + + +def test_json_dump_load(): + """A Circuit and deserialize(serialize())ed circuit returns same result. + (However, it doesn't means they're same Circuit.)""" + c = Circuit().h[:3].x[4].u(1.2, 3.4, 2.3, 1.0)[0].h[:].u(1.2, 3.4, 2.3)[1] + d = serialize(c) + j = json.dumps(d) + d2 = json.loads(j) + c2 = deserialize(d2) + np.allclose(c.run(), c2.run()) + + +def test_serialize_idempotent(): + """Serialized circuit and serialize(deserialize(serialize()))ed circuit are same.""" + c0 = Circuit().h[:3].x[4].cx[1, 3].u(1.2, 3.4, 2.3, 1.0)[0] + c0.h[:].u(1.2, 3.4, 2.3)[1:].m[:] + d1 = serialize(c0) + c1 = deserialize(d1) + d2 = serialize(c1) + c2 = deserialize(d2) + assert d1 == d2 + assert repr(c1) == repr(c2) + + +def test_serialize(): + """Testing serialized result. This JSON may changed in the future.""" + c = Circuit().r(0.2)[0, 2].h[:].m[1] + d = serialize(c) + assert d == { + 'schema': { + 'name': 'blueqat-circuit', + 'version': '1' + }, + 'n_qubits': 3, + 'ops': [ + { + 'name': 'phase', + 'targets': [0], + 'params': [0.2] + }, + { + 'name': 'phase', + 'targets': [2], + 'params': [0.2] + }, + { + 'name': 'h', + 'targets': [0], + 'params': [] + }, + { + 'name': 'h', + 'targets': [1], + 'params': [] + }, + { + 'name': 'h', + 'targets': [2], + 'params': [] + }, + { + 'name': 'measure', + 'targets': [1], + 'params': [] + }, + ] + } + + +def test_deserialize(): + """Testing deserialize result. This JSON may changed in the future.""" + s = """{ + "schema": {"name": "blueqat-circuit", "version": "1"}, + "n_qubits": 3, + "ops": [ + {"name": "phase", "targets": [0], "params": [0.2]}, + {"name": "phase", "targets": [2], "params": [0.2]}, + {"name": "h", "targets": [0], "params": []}, + {"name": "h", "targets": [1], "params": []}, + {"name": "h", "targets": [2], "params": []}, + {"name": "measure", "targets": [1], "params": []} + ]}""" + d = json.loads(s) + c1 = deserialize(d) + c2 = flatten(Circuit().r(0.2)[0, 2].h[:].m[1]) + + +def test_deserialize_unflatten(): + """Testing deserialize unflatten JSON file.""" + s = """{ + "schema": {"name": "blueqat-circuit", "version": "1"}, + "n_qubits": 3, + "ops": [ + {"name": "phase", "targets": [0, 2], "params": [0.2]}, + {"name": "h", "targets": [0, 1, 2], "params": []}, + {"name": "measure", "targets": [1], "params": []} + ]}""" + d = json.loads(s) + c1 = deserialize(d) + c2 = Circuit().r(0.2)[0, 2].h[0, 1, 2].m[1] + assert repr(c1) == repr(c2)