Skip to content
Pasqal Documentation

Serialization

A serialization model class to serialize data from QuantumModels,.

torch.nn.Module and similar structures. The data included in the serialization logic includes: the AbstractBlock and its children classes, QuantumCircuit, Register, and sympy expressions (including Parameter class from qadence.parameters).

A children class must define the value attribute type and how to handle it, since it is the main property for the class to be used by the serialization process. For instance:

@dataclass
class QuantumCircuitSerialization(SerializationModel):
value: QuantumCircuit = dataclass_field(init=False)
def __post_init__(self) -> None:
self.value = (
QuantumCircuit._from_dict(self.d)
if isinstance(self.d, dict)
else self.d
)

Supported Types:

AbstractBlock | QuantumCircuit | QuantumModel | Register | torch.nn.Module Deserializes a dict to one of the supported types.

PARAMETER DESCRIPTION
d

A dict containing a serialized object.

TYPE: dict

as_torch

Whether to transform to torch for the deserialized object.

TYPE: bool DEFAULT: False

Returns: AbstractBlock, QuantumCircuit, QuantumModel, Register, torch.nn.Module.

Examples:

import torch
from qadence import serialize, deserialize, hea, hamiltonian_factory, Z
from qadence import QuantumCircuit, QuantumModel
n_qubits = 2
myblock = hea(n_qubits=n_qubits, depth=1)
block_dict = serialize(myblock)
print(block_dict)
## Lets use myblock in a QuantumCircuit and serialize it.
qc = QuantumCircuit(n_qubits, myblock)
qc_dict = serialize(qc)
qc_deserialized = deserialize(qc_dict)
assert qc == qc_deserialized
## Finally, let's wrap it in a QuantumModel
obs = hamiltonian_factory(n_qubits, detuning = Z)
qm = QuantumModel(qc, obs, backend='pyqtorch', diff_mode='ad')
qm_dict = serialize(qm)
qm_deserialized = deserialize(qm_dict)
# Lets check if the loaded QuantumModel returns the same expectation
assert torch.isclose(qm.expectation({}), qm_deserialized.expectation({}))
{'type': 'ChainBlock', 'qubit_support': (0, 1), 'tag': 'HEA', 'blocks': [{'type': 'ChainBlock', 'qubit_support': (0, 1), 'tag': None, 'blocks': [{'type': 'KronBlock', 'qubit_support': (0, 1), 'tag': None, 'blocks': [{'type': 'RX', 'qubit_support': (0,), 'tag': None, 'parameters': {'_name_dict': {'parameter': ('75447d83-784d-41e9-bf02-4e921aa15a4f', {'name': 'theta_0', 'expression': "Parameter('theta_0')", 'symbols': {'theta_0': {'name': 'theta_0', 'trainable': 'True', 'value': '0.9161447917432486'}}})}}, 'noise': None}, {'type': 'RX', 'qubit_support': (1,), 'tag': None, 'parameters': {'_name_dict': {'parameter': ('cc91b44a-39fa-4001-b2f8-a9369bec14f6', {'name': 'theta_1', 'expression': "Parameter('theta_1')", 'symbols': {'theta_1': {'name': 'theta_1', 'trainable': 'True', 'value': '0.2789282792151917'}}})}}, 'noise': None}]}, {'type': 'KronBlock', 'qubit_support': (0, 1), 'tag': None, 'blocks': [{'type': 'RY', 'qubit_support': (0,), 'tag': None, 'parameters': {'_name_dict': {'parameter': ('46d8a486-c05c-4ece-84e0-9fd2edcd8d5a', {'name': 'theta_2', 'expression': "Parameter('theta_2')", 'symbols': {'theta_2': {'name': 'theta_2', 'trainable': 'True', 'value': '0.7123743659605127'}}})}}, 'noise': None}, {'type': 'RY', 'qubit_support': (1,), 'tag': None, 'parameters': {'_name_dict': {'parameter': ('90226f1c-71fc-4f93-bcc3-c53e48ed5a18', {'name': 'theta_3', 'expression': "Parameter('theta_3')", 'symbols': {'theta_3': {'name': 'theta_3', 'trainable': 'True', 'value': '0.7484748809909304'}}})}}, 'noise': None}]}, {'type': 'KronBlock', 'qubit_support': (0, 1), 'tag': None, 'blocks': [{'type': 'RX', 'qubit_support': (0,), 'tag': None, 'parameters': {'_name_dict': {'parameter': ('91c5d9f8-f806-44b8-95a1-aef8c096489e', {'name': 'theta_4', 'expression': "Parameter('theta_4')", 'symbols': {'theta_4': {'name': 'theta_4', 'trainable': 'True', 'value': '0.17044254285504146'}}})}}, 'noise': None}, {'type': 'RX', 'qubit_support': (1,), 'tag': None, 'parameters': {'_name_dict': {'parameter': ('68731d56-38a4-4e2a-b0b6-f309aa1d51db', {'name': 'theta_5', 'expression': "Parameter('theta_5')", 'symbols': {'theta_5': {'name': 'theta_5', 'trainable': 'True', 'value': '0.8582174907920624'}}})}}, 'noise': None}]}]}, {'type': 'ChainBlock', 'qubit_support': (0, 1), 'tag': None, 'blocks': [{'type': 'KronBlock', 'qubit_support': (0, 1), 'tag': None, 'blocks': [{'type': 'CNOT', 'qubit_support': (0, 1), 'tag': None, 'blocks': [{'type': 'X', 'qubit_support': (1,), 'tag': None, 'noise': None}], 'noise': None}]}]}]}
Source code in qadence/serialization.py
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364def deserialize(d: dict, as_torch: bool = False) -> SUPPORTED_TYPES:
"""
Supported Types:
AbstractBlock | QuantumCircuit | QuantumModel | Register | torch.nn.Module
Deserializes a dict to one of the supported types.
Arguments:
d (dict): A dict containing a serialized object.
as_torch (bool): Whether to transform to torch for the deserialized object.
Returns:
AbstractBlock, QuantumCircuit, QuantumModel, Register, torch.nn.Module.
Examples:
```python exec="on" source="material-block" result="json"
import torch
from qadence import serialize, deserialize, hea, hamiltonian_factory, Z
from qadence import QuantumCircuit, QuantumModel
n_qubits = 2
myblock = hea(n_qubits=n_qubits, depth=1)
block_dict = serialize(myblock)
print(block_dict)
## Lets use myblock in a QuantumCircuit and serialize it.
qc = QuantumCircuit(n_qubits, myblock)
qc_dict = serialize(qc)
qc_deserialized = deserialize(qc_dict)
assert qc == qc_deserialized
## Finally, let's wrap it in a QuantumModel
obs = hamiltonian_factory(n_qubits, detuning = Z)
qm = QuantumModel(qc, obs, backend='pyqtorch', diff_mode='ad')
qm_dict = serialize(qm)
qm_deserialized = deserialize(qm_dict)
# Lets check if the loaded QuantumModel returns the same expectation
assert torch.isclose(qm.expectation({}), qm_deserialized.expectation({}))
```
"""
obj: SerializationModel
if d.get("expression"):
obj = ExpressionSerialization(d)
elif d.get("block") and d.get("register"):
obj = QuantumCircuitSerialization(d)
elif d.get("graph"):
obj = RegisterSerialization(d)
elif d.get("type"):
obj = BlockTypeSerialization(d)
else:
obj = ModelSerialization(d, as_torch=as_torch)
return obj.value

Same as serialize/deserialize but for storing/loading files.

Supported types: AbstractBlock | QuantumCircuit | QuantumModel | Register Loads a .json or .pt file to one of the supported types.

PARAMETER DESCRIPTION
file_path

The name of the file.

TYPE: str

map_location

In case of a .pt file, on which device to load the object (cpu,cuda).

TYPE: str DEFAULT: 'cpu'

Returns: A object of type AbstractBlock, QuantumCircuit, QuantumModel, Register.

Examples:

import torch
from pathlib import Path
import os
from qadence import save, load, hea, hamiltonian_factory, Z
from qadence import QuantumCircuit, QuantumModel
n_qubits = 2
myblock = hea(n_qubits=n_qubits, depth=1)
qc = QuantumCircuit(n_qubits, myblock)
# Lets store the circuit in a json file
save(qc, '.', 'circ')
loaded_qc = load(Path('circ.json'))
qc == loaded_qc
os.remove('circ.json')
## Let's wrap it in a QuantumModel and store that
obs = hamiltonian_factory(n_qubits, detuning = Z)
qm = QuantumModel(qc, obs, backend='pyqtorch', diff_mode='ad')
save(qm, folder= '.',file_name= 'quantum_model')
qm_loaded = load('quantum_model.json')
os.remove('quantum_model.json')
Source code in qadence/serialization.py
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481def load(file_path: str | Path, map_location: str = "cpu") -> SUPPORTED_TYPES:
"""
Same as serialize/deserialize but for storing/loading files.
Supported types: AbstractBlock | QuantumCircuit | QuantumModel | Register
Loads a .json or .pt file to one of the supported types.
Arguments:
file_path (str): The name of the file.
map_location (str): In case of a .pt file, on which device to load the object (cpu,cuda).
Returns:
A object of type AbstractBlock, QuantumCircuit, QuantumModel, Register.
Examples:
```python exec="on" source="material-block" result="json"
import torch
from pathlib import Path
import os
from qadence import save, load, hea, hamiltonian_factory, Z
from qadence import QuantumCircuit, QuantumModel
n_qubits = 2
myblock = hea(n_qubits=n_qubits, depth=1)
qc = QuantumCircuit(n_qubits, myblock)
# Lets store the circuit in a json file
save(qc, '.', 'circ')
loaded_qc = load(Path('circ.json'))
qc == loaded_qc
os.remove('circ.json')
## Let's wrap it in a QuantumModel and store that
obs = hamiltonian_factory(n_qubits, detuning = Z)
qm = QuantumModel(qc, obs, backend='pyqtorch', diff_mode='ad')
save(qm, folder= '.',file_name= 'quantum_model')
qm_loaded = load('quantum_model.json')
os.remove('quantum_model.json')
```
"""
d = {}
if isinstance(file_path, str):
file_path = Path(file_path)
if not os.path.exists(file_path):
logger.error(f"File {file_path} not found.")
raise FileNotFoundError
FORMAT = file_extension(file_path)
_, _, load_fn, _ = FORMAT_DICT[FORMAT] # type: ignore[index]
try:
d = load_fn(file_path, map_location)
logger.debug(f"Successfully loaded {d} from {file_path}.")
except Exception as e:
logger.error(f"Unable to load Object from {file_path} due to {e}")
return deserialize(d)

A parsing expressions function that checks whether a given code is valid on.

the parsing grammar. The grammar is defined to be compatible with sympy expressions, such as Float('-0.33261030434342942', precision=53), while avoiding code injection such as 2*3 or __import__('os').system('ls -la').

PARAMETER DESCRIPTION
code

code to be parsed and checked.

TYPE: str

RETURNS DESCRIPTION
bool

Boolean indicating whether the code matches the defined grammar or not.

Source code in qadence/serialization.py
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106def parse_expr_fn(code: str) -> bool:
"""
A parsing expressions function that checks whether a given code is valid on.
the parsing grammar. The grammar is defined to be compatible with `sympy`
expressions, such as `Float('-0.33261030434342942', precision=53)`, while
avoiding code injection such as `2*3` or `__import__('os').system('ls -la')`.
Args:
code (str): code to be parsed and checked.
Returns:
Boolean indicating whether the code matches the defined grammar or not.
"""
parser = _parsing_serialize_expr
try:
parser.parse(code)
except NoMatch:
return False
else:
return True

save(obj, folder, file_name='', format=SerializationFormat.JSON)

Section titled “ save(obj, folder, file_name='', format=SerializationFormat.JSON) ”

Same as serialize/deserialize but for storing/loading files.

Supported types: AbstractBlock | QuantumCircuit | QuantumModel | Register | torch.nn.Module Saves a qadence object to a json/.pt.

PARAMETER DESCRIPTION obj
Either AbstractBlock, QuantumCircuit, QuantumModel, Register.

TYPE: AbstractBlock | QuantumCircuit | QuantumModel | Register

file_name

The name of the file.

TYPE: str DEFAULT: ''

format

The type of file to save.

TYPE: str DEFAULT: JSON

Returns: None.

Examples:

import torch
from pathlib import Path
import os
from qadence import save, load, hea, hamiltonian_factory, Z
from qadence import QuantumCircuit, QuantumModel
n_qubits = 2
myblock = hea(n_qubits=n_qubits, depth=1)
qc = QuantumCircuit(n_qubits, myblock)
# Lets store the circuit in a json file
save(qc, '.', 'circ')
loaded_qc = load(Path('circ.json'))
qc == loaded_qc
os.remove('circ.json')
## Let's wrap it in a QuantumModel and store that
obs = hamiltonian_factory(n_qubits, detuning = Z)
qm = QuantumModel(qc, obs, backend='pyqtorch', diff_mode='ad')
save(qm, folder= '.',file_name= 'quantum_model')
qm_loaded = load('quantum_model.json')
os.remove('quantum_model.json')
Source code in qadence/serialization.py
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427def save(
obj: SUPPORTED_TYPES,
folder: str | Path,
file_name: str = "",
format: SerializationFormat = SerializationFormat.JSON,
) -> None:
"""
Same as serialize/deserialize but for storing/loading files.
Supported types:
AbstractBlock | QuantumCircuit | QuantumModel | Register | torch.nn.Module
Saves a qadence object to a json/.pt.
Arguments:
obj (AbstractBlock | QuantumCircuit | QuantumModel | Register):
Either AbstractBlock, QuantumCircuit, QuantumModel, Register.
file_name (str): The name of the file.
format (str): The type of file to save.
Returns:
None.
Examples:
```python exec="on" source="material-block" result="json"
import torch
from pathlib import Path
import os
from qadence import save, load, hea, hamiltonian_factory, Z
from qadence import QuantumCircuit, QuantumModel
n_qubits = 2
myblock = hea(n_qubits=n_qubits, depth=1)
qc = QuantumCircuit(n_qubits, myblock)
# Lets store the circuit in a json file
save(qc, '.', 'circ')
loaded_qc = load(Path('circ.json'))
qc == loaded_qc
os.remove('circ.json')
## Let's wrap it in a QuantumModel and store that
obs = hamiltonian_factory(n_qubits, detuning = Z)
qm = QuantumModel(qc, obs, backend='pyqtorch', diff_mode='ad')
save(qm, folder= '.',file_name= 'quantum_model')
qm_loaded = load('quantum_model.json')
os.remove('quantum_model.json')
```
"""
if not isinstance(obj, get_args(SUPPORTED_TYPES)):
logger.error(f"Serialization of object type {type(obj)} not supported.")
folder = Path(folder)
if not folder.is_dir():
logger.error(NotADirectoryError)
if file_name == "":
file_name = type(obj).__name__
try:
suffix, save_fn, _, save_params = FORMAT_DICT[format]
d = serialize(obj, save_params)
file_path = folder / Path(file_name + suffix)
save_fn(d, file_path)
logger.debug(f"Successfully saved {obj} from to {folder}.")
except Exception as e:
logger.error(f"Unable to write {type(obj)} to disk due to {e}")

Supported Types:

AbstractBlock | QuantumCircuit | QuantumModel | torch.nn.Module | Register | Module Serializes a qadence object to a dictionary.

PARAMETER DESCRIPTION
obj

TYPE: AbstractBlock | QuantumCircuit | QuantumModel | Register | Module

Returns: A dict.

Examples:

import torch
from qadence import serialize, deserialize, hea, hamiltonian_factory, Z
from qadence import QuantumCircuit, QuantumModel
n_qubits = 2
myblock = hea(n_qubits=n_qubits, depth=1)
block_dict = serialize(myblock)
print(block_dict)
## Lets use myblock in a QuantumCircuit and serialize it.
qc = QuantumCircuit(n_qubits, myblock)
qc_dict = serialize(qc)
qc_deserialized = deserialize(qc_dict)
assert qc == qc_deserialized
## Finally, let's wrap it in a QuantumModel
obs = hamiltonian_factory(n_qubits, detuning = Z)
qm = QuantumModel(qc, obs, backend='pyqtorch', diff_mode='ad')
qm_dict = serialize(qm)
qm_deserialized = deserialize(qm_dict)
# Lets check if the loaded QuantumModel returns the same expectation
assert torch.isclose(qm.expectation({}), qm_deserialized.expectation({}))
{'type': 'ChainBlock', 'qubit_support': (0, 1), 'tag': 'HEA', 'blocks': [{'type': 'ChainBlock', 'qubit_support': (0, 1), 'tag': None, 'blocks': [{'type': 'KronBlock', 'qubit_support': (0, 1), 'tag': None, 'blocks': [{'type': 'RX', 'qubit_support': (0,), 'tag': None, 'parameters': {'_name_dict': {'parameter': ('2c7da9c9-6f6e-4033-a011-f1e9c19c074e', {'name': 'theta_0', 'expression': "Parameter('theta_0')", 'symbols': {'theta_0': {'name': 'theta_0', 'trainable': 'True', 'value': '0.7161164302971041'}}})}}, 'noise': None}, {'type': 'RX', 'qubit_support': (1,), 'tag': None, 'parameters': {'_name_dict': {'parameter': ('57fb649a-df35-4cd6-a85f-46eaef3566aa', {'name': 'theta_1', 'expression': "Parameter('theta_1')", 'symbols': {'theta_1': {'name': 'theta_1', 'trainable': 'True', 'value': '0.6156695876759051'}}})}}, 'noise': None}]}, {'type': 'KronBlock', 'qubit_support': (0, 1), 'tag': None, 'blocks': [{'type': 'RY', 'qubit_support': (0,), 'tag': None, 'parameters': {'_name_dict': {'parameter': ('fb86ad19-60fc-4098-b0b5-58d4bc9cd2f5', {'name': 'theta_2', 'expression': "Parameter('theta_2')", 'symbols': {'theta_2': {'name': 'theta_2', 'trainable': 'True', 'value': '0.17395980819361812'}}})}}, 'noise': None}, {'type': 'RY', 'qubit_support': (1,), 'tag': None, 'parameters': {'_name_dict': {'parameter': ('4def4f51-8f9b-4456-8f4d-85702db52bff', {'name': 'theta_3', 'expression': "Parameter('theta_3')", 'symbols': {'theta_3': {'name': 'theta_3', 'trainable': 'True', 'value': '0.0006853698663487062'}}})}}, 'noise': None}]}, {'type': 'KronBlock', 'qubit_support': (0, 1), 'tag': None, 'blocks': [{'type': 'RX', 'qubit_support': (0,), 'tag': None, 'parameters': {'_name_dict': {'parameter': ('a1443c11-8644-4ddc-8b82-9012bd26c955', {'name': 'theta_4', 'expression': "Parameter('theta_4')", 'symbols': {'theta_4': {'name': 'theta_4', 'trainable': 'True', 'value': '0.5778343087943436'}}})}}, 'noise': None}, {'type': 'RX', 'qubit_support': (1,), 'tag': None, 'parameters': {'_name_dict': {'parameter': ('f860d663-d0c7-4cd9-aa72-ab85df03ee16', {'name': 'theta_5', 'expression': "Parameter('theta_5')", 'symbols': {'theta_5': {'name': 'theta_5', 'trainable': 'True', 'value': '0.455201855817713'}}})}}, 'noise': None}]}]}, {'type': 'ChainBlock', 'qubit_support': (0, 1), 'tag': None, 'blocks': [{'type': 'KronBlock', 'qubit_support': (0, 1), 'tag': None, 'blocks': [{'type': 'CNOT', 'qubit_support': (0, 1), 'tag': None, 'blocks': [{'type': 'X', 'qubit_support': (1,), 'tag': None, 'noise': None}], 'noise': None}]}]}]}
Source code in qadence/serialization.py
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309def serialize(obj: SUPPORTED_TYPES, save_params: bool = False) -> dict:
"""
Supported Types:
AbstractBlock | QuantumCircuit | QuantumModel | torch.nn.Module | Register | Module
Serializes a qadence object to a dictionary.
Arguments:
obj (AbstractBlock | QuantumCircuit | QuantumModel | Register | torch.nn.Module):
Returns:
A dict.
Examples:
```python exec="on" source="material-block" result="json"
import torch
from qadence import serialize, deserialize, hea, hamiltonian_factory, Z
from qadence import QuantumCircuit, QuantumModel
n_qubits = 2
myblock = hea(n_qubits=n_qubits, depth=1)
block_dict = serialize(myblock)
print(block_dict)
## Lets use myblock in a QuantumCircuit and serialize it.
qc = QuantumCircuit(n_qubits, myblock)
qc_dict = serialize(qc)
qc_deserialized = deserialize(qc_dict)
assert qc == qc_deserialized
## Finally, let's wrap it in a QuantumModel
obs = hamiltonian_factory(n_qubits, detuning = Z)
qm = QuantumModel(qc, obs, backend='pyqtorch', diff_mode='ad')
qm_dict = serialize(qm)
qm_deserialized = deserialize(qm_dict)
# Lets check if the loaded QuantumModel returns the same expectation
assert torch.isclose(qm.expectation({}), qm_deserialized.expectation({}))
```
"""
if not isinstance(obj, get_args(SUPPORTED_TYPES)):
logger.error(TypeError(f"Serialization of object type {type(obj)} not supported."))
d: dict = dict()
try:
if isinstance(obj, core.Expr):
symb_dict = dict()
expr_dict = {"name": str(obj), "expression": srepr(obj)}
symbs: set[Parameter | core.Basic] = obj.free_symbols
if symbs:
symb_dict = {"symbols": {str(s): s._to_dict() for s in symbs}}
d = {**expr_dict, **symb_dict}
else:
if hasattr(obj, "_to_dict"):
model_to_dict: Callable = obj._to_dict
d = (
model_to_dict(save_params)
if isinstance(obj, torch.nn.Module)
else model_to_dict()
)
elif hasattr(obj, "state_dict"):
d = {type(obj).__name__: obj.state_dict()}
else:
raise ValueError(f"Cannot serialize object {obj}.")
except Exception as e:
logger.error(f"Serialization of object {obj} failed due to {e}")
return d