Serialization
Serialization
Section titled “Serialization”
SerializationModel(d=dict())
dataclass
Section titled “
SerializationModel(d=dict())
dataclass
”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:
@dataclassclass 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 )
deserialize(d, as_torch=False)
Section titled “
deserialize(d, as_torch=False)
”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:
|
as_torch
|
Whether to transform to torch for the deserialized object.
TYPE:
|
Returns: AbstractBlock, QuantumCircuit, QuantumModel, Register, torch.nn.Module.
Examples:
import torchfrom qadence import serialize, deserialize, hea, hamiltonian_factory, Zfrom qadence import QuantumCircuit, QuantumModel
n_qubits = 2myblock = 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 QuantumModelobs = 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 expectationassert 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
312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364def 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
load(file_path, map_location='cpu')
Section titled “
load(file_path, map_location='cpu')
”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:
|
map_location
|
In case of a .pt file, on which device to load the object (cpu,cuda).
TYPE:
|
Returns: A object of type AbstractBlock, QuantumCircuit, QuantumModel, Register.
Examples:
import torchfrom pathlib import Pathimport os
from qadence import save, load, hea, hamiltonian_factory, Zfrom qadence import QuantumCircuit, QuantumModel
n_qubits = 2myblock = hea(n_qubits=n_qubits, depth=1)qc = QuantumCircuit(n_qubits, myblock)# Lets store the circuit in a json filesave(qc, '.', 'circ')loaded_qc = load(Path('circ.json'))qc == loaded_qcos.remove('circ.json')## Let's wrap it in a QuantumModel and store thatobs = 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
430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481def 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)
parse_expr_fn(code)
Section titled “
parse_expr_fn(code)
”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:
|
| 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 99100101102103104105106def 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.
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 torchfrom pathlib import Pathimport os
from qadence import save, load, hea, hamiltonian_factory, Zfrom qadence import QuantumCircuit, QuantumModel
n_qubits = 2myblock = hea(n_qubits=n_qubits, depth=1)qc = QuantumCircuit(n_qubits, myblock)# Lets store the circuit in a json filesave(qc, '.', 'circ')loaded_qc = load(Path('circ.json'))qc == loaded_qcos.remove('circ.json')## Let's wrap it in a QuantumModel and store thatobs = 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
367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427def 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}")
serialize(obj, save_params=False)
Section titled “
serialize(obj, save_params=False)
”Supported Types:
AbstractBlock | QuantumCircuit | QuantumModel | torch.nn.Module | Register | Module Serializes a qadence object to a dictionary.
| PARAMETER | DESCRIPTION |
|---|---|
obj
|
TYPE:
|
Returns: A dict.
Examples:
import torchfrom qadence import serialize, deserialize, hea, hamiltonian_factory, Zfrom qadence import QuantumCircuit, QuantumModel
n_qubits = 2myblock = 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 QuantumModelobs = 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 expectationassert 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
243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309def 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