## `eigenkit.core.wire`

A stable JSON form for circuits and observables.

The functions of this module convert a `CircuitSpec` and its observables to plain JSON types (`dict`, `list`, `str`, `int`, `float`, `bool` and `None`), and back. `.eigenkit` bundles use the same encoding, so a change to it would break the saved models.

Example

```python
import json

from eigenkit.core import wire
from eigenkit.core.blocks import AngleEncoding, BasicVariational, assemble
from eigenkit.core.observables import all_z

spec = assemble([AngleEncoding(in_features=2), BasicVariational(depth=1)])
payload = json.dumps(
    {
        "circuit": wire.circuit_spec_to_json(spec),
        "observables": wire.observables_to_json(all_z(2)),
    }
)

data = json.loads(payload)
assert wire.circuit_spec_from_json(data["circuit"]) == spec
assert wire.observables_from_json(data["observables"]) == all_z(2)
```

### `circuit_spec_from_json(data)`

Decode a circuit encoded by `circuit_spec_to_json`.

Parameters:

| Name   | Type   | Description                                                 | Default    |
| ------ | ------ | ----------------------------------------------------------- | ---------- |
| `data` | `dict` | The encoded circuit, for example as returned by json.loads. | *required* |

Returns:

| Type          | Description                                     |
| ------------- | ----------------------------------------------- |
| `CircuitSpec` | The circuit, equal to the one that was encoded. |

### `circuit_spec_to_json(spec)`

Encode a circuit as a dictionary that can be saved as JSON.

Parameters:

| Name   | Type          | Description            | Default    |
| ------ | ------------- | ---------------------- | ---------- |
| `spec` | `CircuitSpec` | The assembled circuit. | *required* |

Returns:

| Type   | Description                                                                                        |
| ------ | -------------------------------------------------------------------------------------------------- |
| `dict` | A dictionary with the keys n_qubits, n_weights, n_inputs and gates, made of plain JSON types only. |

### `observables_from_json(data)`

Decode observables encoded by `observables_to_json`.

Parameters:

| Name   | Type   | Description | Default                                                         |
| ------ | ------ | ----------- | --------------------------------------------------------------- |
| `data` | \`list | dict\`      | The encoded observables, for example as returned by json.loads. |

Returns:

| Type         | Description        |
| ------------ | ------------------ |
| \`Observable | list[Observable]\` |

### `observables_to_json(observables)`

Encode one observable, or a list of them, as data that can be saved as JSON.

Each observable becomes `{"terms": [{"coeff": c, "paulis": [[op, q], ...]}]}`. A coefficient that is a Python `int` or `float` is written unchanged. A NumPy integer becomes an `int`, and any other real number (a NumPy float or a `Fraction`, for example) becomes a `float`, so the output is plain JSON for any encoder.

Parameters:

| Name          | Type         | Description            | Default                                                 |
| ------------- | ------------ | ---------------------- | ------------------------------------------------------- |
| `observables` | \`Observable | Sequence[Observable]\` | A single observable, or a list or tuple of observables. |

Returns:

| Type   | Description |
| ------ | ----------- |
| \`list | dict\`      |

Raises:

| Type        | Description                                         |
| ----------- | --------------------------------------------------- |
| `TypeError` | If a coefficient is a bool or is not a real number. |
