# `qlat_utils.json` — JSON Serialization for NumPy and Numeric Types Source: `qlat-utils/qlat_utils/json.py` > **Note:** Update this document when updating the source file. ## Outline 1. [Overview](#overview) 2. [Supported Types](#supported-types) 3. [API](#api) 4. [Encoding Format](#encoding-format) 5. [Examples](#examples) --- ## Overview The `qlat_utils.json` module provides `json_dumps` and `json_loads` functions that extend Python's standard `json` module to handle NumPy and extended numeric types commonly used in lattice QCD computations. Standard `json.dumps` raises `TypeError` on `numpy.float32`, `numpy.ndarray`, `complex`, and similar types. This module encodes them transparently and decodes them back to their original types, ensuring faithful round-tripping. ```python import qlat_utils as q import numpy as np s = q.json_dumps({"value": np.float32(0.5)}) obj = q.json_loads(s) # recovers np.float32(0.5) ``` --- ## Supported Types | Python / NumPy Type | Round-Trips As | |---|---| | `complex` | `complex` | | `numpy.complex64` | `numpy.complex64` | | `numpy.complex128` | `numpy.complex128` | | `numpy.float32` | `numpy.float32` | | `numpy.int32` | `numpy.int32` | | `numpy.int64` | `numpy.int64` | | `numpy.ndarray` | `numpy.ndarray` (via `tolist()`) | | `range` | `range` | All standard JSON types (`int`, `float`, `str`, `list`, `dict`, `None`, `bool`) are handled by the standard encoder and pass through unchanged. `numpy.float64` is intentionally **not** included. On 64-bit platforms, `np.float64` uses the same underlying C type as Python's built-in ``float``, and is typically a subclass of ``float``. The standard JSON encoder therefore handles it natively (outputting a plain JSON number), and the decoder reconstructs it as a Python ``float``. No precision is lost — both represent IEEE 754 double-precision values with identical bit patterns. --- ## API ### `json_dumps(obj, *, indent=2) -> str` Serialize `obj` to a JSON string. Extended types are encoded with an `__extended_json_type__` marker (see [Encoding Format](#encoding-format)). | Parameter | Type | Default | Description | |---|---|---|---| | `obj` | any | — | Object to serialize | | `indent` | `int \| str \| None` | `2` | JSON indentation; `None` for compact output | ```python s = q.json_dumps({"mass": np.float32(0.5), "corr": np.array([1, 2, 3])}) compact = q.json_dumps({"mass": np.float32(0.5)}, indent=None) ``` ### `json_loads(s) -> any` Deserialize a JSON string back into Python/NumPy objects. Extended types (encoded with `__extended_json_type__`) are restored to their original types. | Parameter | Type | Description | |---|---|---| | `s` | `str` | JSON string to deserialize | ```python obj = q.json_loads(s) ``` --- ## Encoding Format Each extended type is stored as a JSON object with an `__extended_json_type__` key identifying the original type. The decoder uses this key to restore the correct Python/NumPy type automatically. | Type | Encoded Representation | |---|---| | `complex` | `{"real": r, "imag": i, "__extended_json_type__": "complex"}` | | `complex64` | `{"real": r, "imag": i, "__extended_json_type__": "complex64"}` | | `complex128` | `{"real": r, "imag": i, "__extended_json_type__": "complex128"}` | | `float32` | `{"value": v, "__extended_json_type__": "float32"}` | | `int32` | `{"value": v, "__extended_json_type__": "int32"}` | | `int64` | `{"value": v, "__extended_json_type__": "int64"}` | | `ndarray` | `{"value": [...], "__extended_json_type__": "ndarray"}` | | `range` | `{"start": s, "stop": e, "step": t, "__extended_json_type__": "range"}` | Plain Python types (`int`, `float`, `str`, `list`, `dict`, `None`, `bool`) are encoded by the standard JSON encoder and contain no `__extended_json_type__` marker. --- ## Examples ### Basic Round-Trip ```python import qlat_utils as q import numpy as np data = { "mass": np.float32(0.5), "energy": complex(1.2, -0.3), "propagator": np.array([[1+0j, 2+0j], [3+0j, 4+0j]]), "sweeps": np.int64(1000), "step_range": range(0, 100, 5), } s = q.json_dumps(data) print(s) restored = q.json_loads(s) print(type(restored["mass"])) # print(type(restored["energy"])) # print(type(restored["propagator"])) # print(type(restored["step_range"])) # ``` ### Saving and Loading Parameters ```python import qlat_utils as q import numpy as np params = { "lattice_size": [16, 16, 16, 32], "beta": np.float32(6.0), "mass": np.float32(0.05), "n_therm": np.int32(100), "n_traj": np.int64(2000), } # Write to file with open("params.json", "w") as f: f.write(q.json_dumps(params)) # Read back with open("params.json") as f: params = q.json_loads(f.read()) ``` ### Compact (No Indentation) ```python s = q.json_dumps({"mass": np.float32(0.5)}, indent=None) ``` ### Nested Structures ```python data = { "config_0": { "plaquette": np.float64(0.58), "correlator": np.array([1.0, 0.8, 0.6]), }, "config_1": { "plaquette": np.float64(0.59), "correlator": np.array([1.0, 0.79, 0.58]), }, } s = q.json_dumps(data) restored = q.json_loads(s) # numpy array preserved through nesting; np.float64 round-trips as Python float ```