-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #136 from Blueqat/json-format
Json serializer/deserializer
- Loading branch information
Showing
4 changed files
with
207 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
"""This module is EXPERIMENTAL FEATURES. Provided functions may be removed or destructive changed. | ||
This module provides functions that handles Circuits.""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |