## `eigenkit.core.observables`

Functions and classes that make observables easy to write.

There are three ways to choose what a circuit measures:

```python
# Pauli algebra
observable = Z(0) * X(1) + 0.5 * Y(2)

# class methods, or a string
observable = Observable.from_string("Z0 X1 + 0.5 Y2")
observable = Observable.z(0)

# no observable at all: by default, Circuit measures Z on every qubit
```

`PauliExpr` is a simple builder. It collects terms, each made of a coefficient and a tensor product of Pauli operators, and it converts them to an `Observable` when needed. A product of two operators on the same qubit is simplified when the two operators are equal (`P * P = I`). A product of different Pauli operators on the same qubit (`X * Y = iZ` and so on) needs complex arithmetic and is not supported.

### `PauliExpr`

A sum of terms, each a coefficient times a product of Pauli operators.

In each term, the Pauli operators act on distinct qubits, so the product is a tensor product.

#### `constant`

The coefficient of the identity term, or `0.0` when there is none.

#### `__init__(terms)`

Build an expression from raw terms.

Parameters:

| Name    | Type              | Description                                                                                                                                                                                | Default    |
| ------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------- |
| `terms` | `Iterable[_Term]` | Internal records of the terms. Prefer the X, Y and Z functions and ordinary arithmetic, such as 0.5 * Z(0) + X(1). They build the same expression without creating these records directly. | *required* |

#### `single(operator, qubit, coeff=1.0)`

Return `coeff` times a single Pauli `operator` on `qubit`.

Parameters:

| Name       | Type    | Description                          | Default    |
| ---------- | ------- | ------------------------------------ | ---------- |
| `operator` | `str`   | The Pauli operator: "X", "Y" or "Z". | *required* |
| `qubit`    | `int`   | The index of the qubit.              | *required* |
| `coeff`    | `float` | The coefficient. Default: 1.0.       | `1.0`      |

Returns:

| Type          | Description                              |
| ------------- | ---------------------------------------- |
| `'PauliExpr'` | The expression, which has a single term. |

#### `to_observable()`

Convert the expression to an `Observable`.

The terms with the same Pauli string are combined, and the terms whose coefficient is `0` (at most `1e-15` in magnitude) are dropped.

An `Observable` holds only Pauli terms, so a non-zero identity term raises an error. Such a term comes from a scalar offset, or from `P * P = I`. Dropping it without an error would shift every expectation value. To measure such an expression, measure `expr - expr.constant` and add `expr.constant` to the result. The chemistry entry points take this constant as `offset`.

Returns:

| Type         | Description     |
| ------------ | --------------- |
| `Observable` | The observable. |

Raises:

| Type         | Description                                                                                      |
| ------------ | ------------------------------------------------------------------------------------------------ |
| `ValueError` | If the expression has a constant term that is not 0, or if no term that can be measured is left. |

### `X(qubit)`

Return Pauli `X` on `qubit`, as an expression that can be added and scaled.

Parameters:

| Name    | Type  | Description             | Default    |
| ------- | ----- | ----------------------- | ---------- |
| `qubit` | `int` | The index of the qubit. | *required* |

Returns:

| Type        | Description     |
| ----------- | --------------- |
| `PauliExpr` | The expression. |

### `Y(qubit)`

Return Pauli `Y` on `qubit`, as an expression that can be added and scaled.

Parameters:

| Name    | Type  | Description             | Default    |
| ------- | ----- | ----------------------- | ---------- |
| `qubit` | `int` | The index of the qubit. | *required* |

Returns:

| Type        | Description     |
| ----------- | --------------- |
| `PauliExpr` | The expression. |

### `Z(qubit)`

Return Pauli `Z` on `qubit`, as an expression that can be added and scaled.

Parameters:

| Name    | Type  | Description             | Default    |
| ------- | ----- | ----------------------- | ---------- |
| `qubit` | `int` | The index of the qubit. | *required* |

Returns:

| Type        | Description     |
| ----------- | --------------- |
| `PauliExpr` | The expression. |

### `all_z(n_qubits)`

Return one `Z` observable for each qubit.

This is useful as a default measurement.

Parameters:

| Name       | Type  | Description                          | Default    |
| ---------- | ----- | ------------------------------------ | ---------- |
| `n_qubits` | `int` | The number of qubits of the circuit. | *required* |

Returns:

| Type               | Description                    |
| ------------------ | ------------------------------ |
| `list[Observable]` | One Observable for each qubit. |

### `from_string(text)`

Parse an observable from a string.

This function is a shortcut for `Observable.from_string`.

Parameters:

| Name   | Type  | Description          | Default    |
| ------ | ----- | -------------------- | ---------- |
| `text` | `str` | The string to parse. | *required* |

Returns:

| Type         | Description            |
| ------------ | ---------------------- |
| `Observable` | The parsed observable. |
