---
title: QkObs (latest version)
description: API reference for QkObs in the latest version of qiskit-c
source: https://quantum.cloud.ibm.com/docs/en/api/qiskit-c/qk-obs
---

# QkObs

```c
typedef struct QkObs QkObs
```

An observable over Pauli bases that stores its data in a qubit-sparse format.

## Mathematics

This observable represents a sum over strings of the Pauli operators and Pauli-eigenstate projectors, with each term weighted by some complex number. That is, the full observable is

$$
\text{\texttt{QkObs}} = \sum_i c_i \bigotimes_n A^{(n)}_i


$$

for complex numbers $c_i$ and single-qubit operators acting on qubit $n$ from a restricted alphabet $A^{(n)}_i$. The sum over $i$ is the sum of the individual terms, and the tensor product produces the operator strings. The alphabet of allowed single-qubit operators that the $A^{(n)}_i$ are drawn from is the Pauli operators and the Pauli-eigenstate projection operators. Explicitly, these are:

| Operator                                                                 | `QkBitTerm`       | Numeric value |
| ------------------------------------------------------------------------ | ----------------- | ------------- |
| $I$ (identity)                                                           | Not stored.       | Not stored.   |
| $X$ (Pauli X)                                                            | `QkBitTerm_X`     | `0b0010` (2)  |
| $Y$ (Pauli Y)                                                            | `QkBitTerm_Y`     | `0b0011` (3)  |
| $Z$ (Pauli Z)                                                            | `QkBitTerm_Z`     | `0b0001` (1)  |
| $\lvert+\rangle\langle+\rvert$ (projector to positive eigenstate of X)   | `QkBitTerm_Plus`  | `0b1010` (10) |
| $\lvert-\rangle\langle-\rvert$ (projector to negative eigenstate of X)   | `QkBitTerm_Minus` | `0b0110` (6)  |
| $\lvert r\rangle\langle r\rvert$ (projector to positive eigenstate of Y) | `QkBitTerm_Right` | `0b1011` (11) |
| $\lvert l\rangle\langle l\rvert$ (projector to negative eigenstate of Y) | `QkBitTerm_Left`  | `0b0111` (7)  |
| $\lvert0\rangle\langle0\rvert$ (projector to positive eigenstate of Z)   | `QkBitTerm_Zero`  | `0b1001` (9)  |
| $\lvert1\rangle\langle1\rvert$ (projector to negative eigenstate of Z)   | `QkBitTerm_One`   | `0b0101` (5)  |

Due to allowing both the Paulis and their projectors, the allowed alphabet forms an overcomplete basis of the operator space. This means that there is not a unique summation to represent a given observable. As a consequence, comparison requires additional care and using `qk_obs_canonicalize` on two mathematically equivalent observables might not result in the same representation.

`QkObs` uses its particular overcomplete basis with the aim of making “efficiency of measurement” equivalent to “efficiency of representation”. For example, the observable ${\lvert0\rangle\langle0\rvert}^{\otimes n}$ can be efficiently measured on hardware with simple $Z$ measurements, but can only be represented in terms of Paulis as ${(I + Z)}^{\otimes n}/2^n$, which requires $2^n$ stored terms. `QkObs` requires only a single term to store this. The downside to this is that it is impractical to take an arbitrary matrix and find the *best* `QkObs` representation. You typically will want to construct a `QkObs` directly, rather than trying to decompose into one.

## Representation

The internal representation of a `QkObs` stores only the non-identity qubit operators. This makes it significantly more efficient to represent observables such as $\sum_{n\in \text{qubits}} Z^{(n)}$; `QkObs` requires an amount of memory linear in the total number of qubits. The terms are stored compressed, similar in spirit to the compressed sparse row format of sparse matrices. In this analogy, the terms of the sum are the “rows”, and the qubit terms are the “columns”, where an absent entry represents the identity rather than a zero. More explicitly, the representation is made up of four contiguous arrays:

| Attribute accessible by | Length | Description                                                                                                                                                                                                                                                                                                                                                                                |
| ----------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `qk_obs_coeffs`         | $t$    | The complex scalar multiplier for each term.                                                                                                                                                                                                                                                                                                                                               |
| `qk_obs_bit_terms`      | $s$    | Each of the non-identity single-qubit terms for all of the operators, in order. These correspond to the non-identity $A^{(n)}_i$ in the sum description, where the entries are stored in order of increasing $i$ first, and in order of increasing $n$ within each term.                                                                                                                   |
| `qk_obs_indices`        | $s$    | The corresponding qubit ($n$) for each of the bit terms. `QkObs` requires that this list is term-wise sorted, and algorithms can rely on this invariant being upheld.                                                                                                                                                                                                                      |
| `qk_obs_boundaries`     | $t+1$  | The indices that partition the bit terms and indices into complete terms. For term number $i$, its complex coefficient is stored at index `i`, and its non-identity single-qubit operators and their corresponding qubits are in the range `[boundaries[i], boundaries[i+1])` in the bit terms and indices, respectively. The boundaries always have an explicit 0 as their first element. |

The length parameter $t$ is the number of terms in the sum and can be queried using `qk_obs_num_terms`. The parameter $s$ is the total number of non-identity single-qubit terms and can be queried using `qk_obs_len`.

As illustrative examples:

- in the case of a zero operator, the boundaries are length 1 (a single 0) and all other vectors are empty.
- in the case of a fully simplified identity operator, the boundaries are `{0, 0}`, the coefficients have a single entry, and both the bit terms and indices are empty.
- for the operator $Z_2 Z_0 - X_3 Y_1$, the boundaries are `{0, 2, 4}`, the coeffs are `{1.0, -1.0}`, the bit terms are `{QkBitTerm_Z, QkBitTerm_Z, QkBitTerm_Y, QkBitTerm_X}` and the indices are `{0, 2, 1, 3}`. The operator might act on more than four qubits, depending on the the number of qubits (see `qk_obs_num_qubits`). Note that the single-bit terms and indices are sorted into termwise sorted order.

These cases are not special, they’re fully consistent with the rules and should not need special handling.

### Canonical ordering

For any given mathematical observable, there are several ways of representing it with `QkObs`. For example, the same set of single-bit terms and their corresponding indices might appear multiple times in the observable. Mathematically, this is equivalent to having only a single term with all the coefficients summed. Similarly, the terms of the sum in a `QkObs` can be in any order while representing the same observable, since addition is commutative (although while floating-point addition is not associative, `QkObs` makes no guarantees about the summation order).

These two categories of representation degeneracy can cause the operator equality, `qk_obs_equal`, to claim that two observables are not equal, despite representing the same object. In these cases, it can be convenient to define some *canonical form*, which allows observables to be compared structurally. You can put a `QkObs` in canonical form by using the `qk_obs_canonicalize` function. The precise ordering of terms in canonical ordering is not specified, and may change between versions of Qiskit. Within the same version of Qiskit, however, you can compare two observables structurally by comparing their simplified forms.

> **Note**
>
> If you wish to account for floating-point tolerance in the comparison, it is safest to use a recipe such as:
>
> ```c
> bool equivalent(QkObs *left, QkObs *right, double tol) {
>   // compare a canonicalized version of left - right to the zero observable
>   QkObs *neg_right = qk_obs_multiply(right, &(QkComplex64){-1, 0});
>   QkObs *diff = qk_obs_add(left, neg_right);
>   QkObs *canonical = qk_obs_canonicalize(diff, tol);
>
>   QkObs *zero = qk_obs_zero(qk_obs_num_qubits(left));
>   bool equiv = qk_obs_equal(diff, zero);
>   // free all temporary variables
>   qk_obs_free(neg_right);
>   qk_obs_free(diff);
>   qk_obs_free(canonical);
>   qk_obs_free(zero);
>   return equiv;
> }
> ```

> **Note**
>
> The canonical form produced by `qk_obs_canonicalize` alone will not universally detect all observables that are equivalent due to the over-complete basis alphabet.

### Indexing

Individual observable sum terms in `QkObs` can be accessed via `qk_obs_term` and return objects of type `QkObsTerm`. These terms then contain fields with the coefficient of the term, its bit terms, indices and the number of qubits it is defined on. Together with the information of the number of terms, you can iterate over all observable terms as

```c
size_t num_terms = qk_obs_num_terms(obs);  // obs is QkObs*
for (size_t i = 0; i < num_terms; i++) {
    QkObsTerm term;  // allocate term on stack
    int exit = qk_obs_term(obs, i, &term);  // get the term (exit > 0 upon index errors)
    // do something with the term...
}
```

> **Warning**
>
> Populating a `QkObsTerm` via `qk_obs_term` will reference data of the original `QkObs`. Modifying the bit terms or indices will change the observable and can leave it in an incoherent state.

## Construction

`QkObs` can be constructed by initializing an empty observable (with `qk_obs_zero`) and iteratively adding terms (with `qk_obs_add_term`). Alternatively, an observable can be constructed from “raw” data (with `qk_obs_new`) if all internal data is specified. This requires care to ensure the data is coherent and results in a valid observable.

| Function          | Summary                                                            |
| ----------------- | ------------------------------------------------------------------ |
| `qk_obs_zero`     | Construct an empty observable on a given number of qubits.         |
| `qk_obs_identity` | Construct the identity observable on a given number of qubits.     |
| `qk_obs_new`      | Construct an observable from [the raw data arrays](#qkobs-arrays). |

## Mathematical manipulation

`QkObs` supports fundamental arithmetic operations in between observables or with scalars. You can:

- add two observables using `qk_obs_add` and `qk_obs_add_inplace`
- multiply by a complex number with `qk_obs_multiply` and `qk_obs_multiply_inplace`
- compose (multiply) two observables via `qk_obs_compose` and `qk_obs_compose_map`
- compute `left + scalar * right` for two observables and a complex scalar with `qk_obs_scaled_add` and `qk_obs_scaled_add_inplace`

## Functions

### qk\_obs\_zero

`QkObs *qk_obs_zero(uint32_t num_qubits)`

Construct the zero observable (without any terms).

#### Example

```c
QkObs *zero = qk_obs_zero(100);
```

**Parameters**

- **num\_qubits** – The number of qubits the observable is defined on.

**Returns**

A pointer to the created observable.

### qk\_obs\_identity

`QkObs *qk_obs_identity(uint32_t num_qubits)`

Construct the identity observable.

#### Example

```c
QkObs *identity = qk_obs_identity(100);
```

**Parameters**

- **num\_qubits** – The number of qubits the observable is defined on.

**Returns**

A pointer to the created observable.

### qk\_obs\_new

`QkObs *qk_obs_new(uint32_t num_qubits, uint64_t num_terms, uint64_t num_bits, QkComplex64 *coeffs, QkBitTerm *bit_terms, uint32_t *indices, size_t *boundaries)`

Construct a new observable from raw data.

Any of the pointer arguments may be `NULL` if and only if their corresponding length is zero.

#### Example

```c
// define the raw data for the 100-qubit observable |01><01|_{0, 1} - |+-><+-|_{98, 99}
uint32_t num_qubits = 100;
uint64_t num_terms = 2;  // we have 2 terms: |01><01|, -1 * |+-><+-|
uint64_t num_bits = 4; // we have 4 non-identity bits: 0, 1, +, -
QkComplex64 coeffs[] = {{1, 0}, {-1, 0}};
QkBitTerm bits[4] = {QkBitTerm_Zero, QkBitTerm_One, QkBitTerm_Plus, QkBitTerm_Minus};

uint32_t indices[4] = {0, 1, 98, 99};  // <-- e.g. {1, 0, 99, 98} would be invalid
size_t boundaries[3] = {0, 2, 4};
QkObs *obs = qk_obs_new(
    num_qubits, num_terms, num_bits, coeffs, bits, indices, boundaries
);
qk_obs_free(obs);
```

#### Safety

Behavior is undefined if any of the following conditions are violated:

- `coeffs` is a pointer to a `QkComplex64` array of length `num_terms`
- `bit_terms` is a pointer to an array of valid `QkBitTerm` elements of length `num_bits`
- `indices` is a pointer to a `uint32_t` array of length `num_bits`, which is term-wise sorted in strict ascending order, and every element is smaller than `num_qubits`
- `boundaries` is a pointer to a `size_t` array of length `num_terms + 1`, which is sorted in ascending order, the first element is 0 and the last element is smaller than `num_terms`

**Parameters**

- **num\_qubits** – The number of qubits the observable is defined on.
- **num\_terms** – The number of terms.
- **num\_bits** – The total number of non-identity bit terms.
- **coeffs** – A pointer to the first element of the coefficients array, which has length `num_terms`.
- **bit\_terms** – A pointer to the first element of the bit terms array, which has length `num_bits`.
- **indices** – A pointer to the first element of the indices array, which has length `num_bits`. Note that, per term, these *must* be sorted incrementally.
- **boundaries** – A pointer to the first element of the boundaries array, which has length `num_terms + 1`.

**Returns**

If the input data was coherent and the construction successful, the result is a pointer to the observable. Otherwise a null pointer is returned.

### qk\_obs\_free

`void qk_obs_free(QkObs *obs)`

Free the observable.

#### Example

```c
QkObs *obs = qk_obs_zero(100);
qk_obs_free(obs);
```

#### Safety

Behavior is undefined if `obs` is not either null or a valid pointer to a `QkObs`.

**Parameters**

- **obs** – A pointer to the observable to free.

### qk\_obs\_add\_term

`QkExitCode qk_obs_add_term(QkObs *obs, const QkObsTerm *cterm)`

Add a term to the observable.

#### Example

```c
uint32_t num_qubits = 100;
QkObs *obs = qk_obs_zero(num_qubits);

QkComplex64 coeff = {1, 0};
QkBitTerm bit_terms[3] = {QkBitTerm_X, QkBitTerm_Y, QkBitTerm_Z};
uint32_t indices[3] = {0, 1, 2};
QkObsTerm term = {coeff, 3, bit_terms, indices, num_qubits};

QkExitCode exit_code = qk_obs_add_term(obs, &term);
```

#### Safety

Behavior is undefined if any of the following is violated:

- `obs` is a valid, non-null pointer to a `QkObs`
- `cterm` is a valid, non-null pointer to a `QkObsTerm`

**Parameters**

- **obs** – A pointer to the observable.
- **cterm** – A pointer to the term to add.

**Returns**

An exit code. This is `>0` if the term is incoherent or adding the term fails.

### qk\_obs\_term

`QkExitCode qk_obs_term(QkObs *obs, uint64_t index, QkObsTerm *out)`

Get an observable term by reference.

A `QkObsTerm` contains pointers to the indices and bit terms in the term, which can be used to modify the internal data of the observable. This can leave the observable in an incoherent state and should be avoided, unless great care is taken. It is generally safer to construct a new observable instead of attempting in-place modifications.

#### Example

```c
QkObs *obs = qk_obs_identity(100);
QkObsTerm term;
QkExitCode exit_code = qk_obs_term(obs, 0, &term);
// out-of-bounds indices return an error code
// QkExitCode error = qk_obs_term(obs, 12, &term);
```

#### Safety

Behavior is undefined if any of the following is violated

- `obs` is a valid, non-null pointer to a `QkObs`
- `out` is a valid, non-null pointer to a `QkObsTerm`

**Parameters**

- **obs** – A pointer to the observable.
- **index** – The index of the term to get.
- **out** – A pointer to a `QkObsTerm` used to return the observable term.

**Returns**

An exit code.

### qk\_obs\_num\_terms

`size_t qk_obs_num_terms(const QkObs *obs)`

Get the number of terms in the observable.

#### Example

```c
QkObs *obs = qk_obs_identity(100);
size_t num_terms = qk_obs_num_terms(obs);  // num_terms==1
```

#### Safety

Behavior is undefined `obs` is not a valid, non-null pointer to a `QkObs`.

**Parameters**

- **obs** – A pointer to the observable.

**Returns**

The number of terms in the observable.

### qk\_obs\_num\_qubits

`uint32_t qk_obs_num_qubits(const QkObs *obs)`

Get the number of qubits the observable is defined on.

#### Example

```c
QkObs *obs = qk_obs_identity(100);
uint32_t num_qubits = qk_obs_num_qubits(obs);  // num_qubits==100
```

#### Safety

Behavior is undefined `obs` is not a valid, non-null pointer to a `QkObs`.

**Parameters**

- **obs** – A pointer to the observable.

**Returns**

The number of qubits the observable is defined on.

### qk\_obs\_len

`size_t qk_obs_len(const QkObs *obs)`

Get the number of bit terms/indices in the observable.

#### Example

```c
QkObs *obs = qk_obs_identity(100);
size_t len = qk_obs_len(obs);  // len==0, as there are no non-trivial bit terms
```

#### Safety

Behavior is undefined `obs` is not a valid, non-null pointer to a `QkObs`.

**Parameters**

- **obs** – A pointer to the observable.

**Returns**

The number of terms in the observable.

### qk\_obs\_coeffs

`QkComplex64 *qk_obs_coeffs(QkObs *obs)`

Get a pointer to the coefficients.

This can be used to read and modify the observable’s coefficients. The resulting pointer is valid to read for `qk_obs_num_terms(obs)` elements of `QkComplex64`.

#### Example

```c
QkObs *obs = qk_obs_identity(100);
size_t num_terms = qk_obs_num_terms(obs);
QkComplex64 *coeffs = qk_obs_coeffs(obs);

for (size_t i = 0; i < num_terms; i++) {
    printf("%f + i%f\n", coeffs[i].re, coeffs[i].im);
}
```

#### Safety

Behavior is undefined `obs` is not a valid, non-null pointer to a `QkObs`.

**Parameters**

- **obs** – A pointer to the observable.

**Returns**

A pointer to the coefficients.

### qk\_obs\_indices

`uint32_t *qk_obs_indices(QkObs *obs)`

Get a pointer to the indices.

This can be used to read and modify the observable’s indices. The resulting pointer is valid to read for `qk_obs_len(obs)` elements of size `uint32_t`.

#### Example

```c
uint32_t num_qubits = 100;
QkObs *obs = qk_obs_zero(num_qubits);

QkComplex64 coeff = {1, 0};
QkBitTerm bit_terms[3] = {QkBitTerm_X, QkBitTerm_Y, QkBitTerm_Z};
uint32_t term_indices[3] = {0, 1, 2};
QkObsTerm term = {coeff, 3, bit_terms, term_indices, num_qubits};
qk_obs_add_term(obs, &term);

size_t len = qk_obs_len(obs);
uint32_t *indices = qk_obs_indices(obs);

for (size_t i = 0; i < len; i++) {
    printf("index %i: %i\n", i, indices[i]);
}

qk_obs_free(obs);
```

#### Safety

Behavior is undefined `obs` is not a valid, non-null pointer to a `QkObs`.

**Parameters**

- **obs** – A pointer to the observable.

**Returns**

A pointer to the indices.

### qk\_obs\_boundaries

`size_t *qk_obs_boundaries(QkObs *obs)`

Get a pointer to the term boundaries.

This can be used to read and modify the observable’s term boundaries. The resulting pointer is valid to read for `qk_obs_num_terms(obs) + 1` elements of size `size_t`.

#### Example

```c
uint32_t num_qubits = 100;
QkObs *obs = qk_obs_zero(num_qubits);

QkComplex64 coeff = {1, 0};
QkBitTerm bit_terms[3] = {QkBitTerm_X, QkBitTerm_Y, QkBitTerm_Z};
uint32_t indices[3] = {0, 1, 2};
QkObsTerm term = {coeff, 3, bit_terms, indices, num_qubits};
qk_obs_add_term(obs, &term);

size_t num_terms = qk_obs_num_terms(obs);
size_t *boundaries = qk_obs_boundaries(obs);

for (size_t i = 0; i < num_terms + 1; i++) {
    printf("boundary %i: %i\n", i, boundaries[i]);
}
```

#### Safety

Behavior is undefined `obs` is not a valid, non-null pointer to a `QkObs`.

**Parameters**

- **obs** – A pointer to the observable.

**Returns**

A pointer to the boundaries.

### qk\_obs\_bit\_terms

`QkBitTerm *qk_obs_bit_terms(QkObs *obs)`

Get a pointer to the bit terms.

This can be used to read and modify the observable’s bit terms. The resulting pointer is valid to read for `qk_obs_len(obs)` elements of size `uint8_t`.

#### Example

```c
uint32_t num_qubits = 100;
QkObs *obs = qk_obs_zero(num_qubits);

QkComplex64 coeff = {1, 0};
QkBitTerm bit_terms[3] = {QkBitTerm_X, QkBitTerm_Y, QkBitTerm_Z};
uint32_t indices[3] = {0, 1, 2};
QkObsTerm term = {coeff, 3, bit_terms, indices, num_qubits};
qk_obs_add_term(obs, &term);

size_t len = qk_obs_len(obs);
QkBitTerm *bits = qk_obs_bit_terms(obs);

for (size_t i = 0; i < len; i++) {
    printf("bit term %i: %i\n", i, bits[i]);
}

qk_obs_free(obs);
```

#### Safety

Behavior is undefined `obs` is not a valid, non-null pointer to a `QkObs`, or if invalid values are written into the resulting `QkBitTerm` pointer.

**Parameters**

- **obs** – A pointer to the observable.

**Returns**

A pointer to the bit terms.

### qk\_obs\_multiply

`QkObs *qk_obs_multiply(const QkObs *obs, const QkComplex64 *coeff)`

Multiply the observable by a complex coefficient.

#### Example

```c
QkObs *obs = qk_obs_identity(100);
QkComplex64 coeff = {2, 0};
QkObs *result = qk_obs_multiply(obs, &coeff);
```

#### Safety

Behavior is undefined if any of the following is violated

- `obs` is a valid, non-null pointer to a `QkObs`
- `coeff` is a valid, non-null pointer to a `QkComplex64`

**Parameters**

- **obs** – A pointer to the observable.
- **coeff** – The coefficient to multiply the observable with.

**Returns**

A pointer to the result `obs * coeff`.

### qk\_obs\_multiply\_inplace

`void qk_obs_multiply_inplace(QkObs *obs, const QkComplex64 *coeff)`

Multiply the observable in-place by a complex coefficient.

#### Example

```c
QkObs *obs = qk_obs_identity(100);
QkComplex64 coeff = {2, 0};
qk_obs_multiply_inplace(obs, &coeff);
```

#### Safety

Behavior is undefined if any of the following is violated

- `obs` is a valid, non-null pointer to a `QkObs`
- `coeff` is a valid, non-null pointer to a `QkComplex64`

**Parameters**

- **obs** – A pointer to the observable.
- **coeff** – The coefficient to multiply the observable with.

### qk\_obs\_add

`QkObs *qk_obs_add(const QkObs *left, const QkObs *right)`

Add two observables.

#### Example

```c
QkObs *left = qk_obs_identity(100);
QkObs *right = qk_obs_zero(100);
QkObs *result = qk_obs_add(left, right);
```

#### Safety

Behavior is undefined if `left` or `right` are not valid, non-null pointers to `QkObs`\ s.

**Parameters**

- **left** – A pointer to the left observable.
- **right** – A pointer to the right observable.

**Returns**

A pointer to the result `left + right`.

### qk\_obs\_add\_inplace

`void qk_obs_add_inplace(QkObs *left, const QkObs *right)`

Add an observable to an existing one.

#### Example

```c
QkObs *left = qk_obs_identity(100);
QkObs *right = qk_obs_zero(100);
qk_obs_add_inplace(left, right);
```

#### Safety

Behavior is undefined if `left` or `right` are not valid, non-null pointers to `QkObs`\ s.

**Parameters**

- **left** – A pointer to the left observable.
- **right** – A pointer to the right observable.

### qk\_obs\_scaled\_add

`QkObs *qk_obs_scaled_add(const QkObs *left, const QkObs *right, const QkComplex64 *factor)`

Add two observables while scaling the coefficients of the right one.

#### Example

```c
QkObs *left = qk_obs_zero(100);
QkObs *right = qk_obs_identity(100);
QkComplex64 factor = {2, 0};
QkObs *result = qk_obs_scaled_add(left, right, &factor);
```

#### Safety

Behavior is undefined if `left` or `right` are not valid, non-null pointers to `QkObs`\ s.

**Parameters**

- **left** – A pointer to the left observable.
- **right** – A pointer to the right observable.
- **factor** – The factor to multiply the coefficients with.

**Returns**

An owned pointer to the result `left + factor * right`.

### qk\_obs\_scaled\_add\_inplace

`void qk_obs_scaled_add_inplace(QkObs *left, const QkObs *right, const QkComplex64 *factor)`

Add a scaled observable to an existing one.

#### Example

```c
QkObs *left = qk_obs_zero(100);
QkObs *right = qk_obs_identity(100);
QkComplex64 factor = {2, 0};
qk_obs_scaled_add_inplace(left, right, &factor);
```

#### Safety

Behavior is undefined if `left` or `right` are not valid, non-null pointers to `QkObs`\ s.

**Parameters**

- **left** – A pointer to the left observable.
- **right** – A pointer to the right observable.
- **factor** – The factor to multiply the coefficients with.

### qk\_obs\_compose

`QkObs *qk_obs_compose(const QkObs *first, const QkObs *second)`

Compose (multiply) two observables.

#### Example

```c
QkObs *first = qk_obs_zero(100);
QkObs *second = qk_obs_identity(100);
QkObs *result = qk_obs_compose(first, second);
```

#### Safety

Behavior is undefined if `first` or `second` are not valid, non-null pointers to `QkObs`\ s.

**Parameters**

- **first** – One observable.
- **second** – The other observable.

**Returns**

`first.compose(second)` which equals the observable `result = second @ first`, in terms of the matrix multiplication `@`.

### qk\_obs\_compose\_map

`QkObs *qk_obs_compose_map(const QkObs *first, const QkObs *second, const uint32_t *qargs)`

Compose (multiply) two observables according to a custom qubit order.

Notably, this allows composing two observables of different size.

#### Example

```c
QkObs *first = qk_obs_zero(100);
QkObs *second = qk_obs_identity(100);
QkObs *result = qk_obs_compose(first, second);
```

#### Safety

To call this function safely

- `first` and `second` must be valid, non-null pointers to `QkObs`\ s
- `qargs` must point to an array of `uint32_t`, readable for `qk_obs_num_qubits(second)` elements (meaning the number of qubits in `second`)

**Parameters**

- **first** – One observable.
- **second** – The other observable. The number of qubits must match the length of `qargs`.
- **qargs** – The qubit arguments specified which indices in `first` to associate with the ones in `second`.

**Returns**

`first.compose(second)` which equals the observable `result = second @ first`, in terms of the matrix multiplication `@`.

### qk\_obs\_apply\_layout

`QkExitCode qk_obs_apply_layout(QkObs *obs, const uint32_t *layout, uint32_t num_qubits)`

Apply a new qubit layout to the observable.

The layout is set by an array `layout` of new indices, specifying that qubit at current index `i` is relabelled to index `layout[i]`. The number of qubits the observable acts on can be extended by setting a larger `num_qubits` than the current observable has.

#### Example

This interface allows to relabel and extend the qubit indices:

```c
QkObs *obs = qk_obs_zero(4);

// add a term to the observable
QkBitTerm bit_terms[3] = {QkBitTerm_X, QkBitTerm_Y, QkBitTerm_Z};
uint32_t qubits[3] = {1, 2, 3};
complex double coeff = 1;
QkObsTerm term = {coeff, 3, bit_terms, qubits, 4};
qk_obs_add_term(obs, &term);

uint32_t layout[3] = {0, 10, 9};  // qubit mapping is: 0->0, 1->10, 2->9
uint32_t num_output_qubits = 11;
QkExitCode exit = qk_obs_apply_layout(obs, layout, num_output_qubits);
```

In a compiler workflow, this function can conveniently be used to apply a `QkTranspileLayout*` obtained from a transpiler pass, called `transpile_layout` in the following example:

```c
// get the number of output qubits
uint32_t num_output_qubits = qk_transpile_layout_num_output_qubits(transpile_layout);

// get the layout including the ancillas (hence the ``false`` in the function call)
uint32_t *layout = malloc(sizeof(uint32_t) * num_output_qubits);
qk_transpile_layout_final_layout(transpile_layout, false, layout);

// apply the layout
QkExitCode exit = qk_obs_apply_layout(obs, layout, num_output_qubits);

// free the layout array
free(layout);
```

#### Safety

Behavior is undefined if `obs` is not a valid, non-null pointer to `QkObs` or if `layout` is not a valid, non-null pointer to a sequence of `qk_obs_num_qubits(obs)` consecutive elements of `uint32_t`.

**Parameters**

- **obs** – A pointer to the observable, this observable will be modified in place upon success. Check the exit code to ensure the layout was correctly applied.
- **layout** – A pointer to the layout. The pointer must point to an array to `qk_obs_num_qubits(obs)` elements of type `uint32_t`. Each element must have values in `[0, num_qubits)`.
- **num\_qubits** – The number of output qubits.

**Returns**

An exit code.

- `QkExitCode_Success` upon success
- `QkExitCode_DuplicteIndexError` if duplicate qubit indices were found
- `QkExitCode_MismatchedQubits` if `num_qubits` is smaller than the number of qubits in the observable
- `QkExitCode_IndexError` for any other index errors, such as invalid values in `layout`.

### qk\_obs\_canonicalize

`QkObs *qk_obs_canonicalize(const QkObs *obs, double tol)`

Calculate the canonical representation of the observable.

#### Example

```c
QkObs *iden = qk_obs_identity(100);
QkObs *two = qk_obs_add(iden, iden);

double tol = 1e-6;
QkObs *canonical = qk_obs_canonicalize(two, tol);
```

#### Safety

Behavior is undefined `obs` is not a valid, non-null pointer to a `QkObs`.

**Parameters**

- **obs** – A pointer to the observable.
- **tol** – The tolerance below which coefficients are considered to be zero.

**Returns**

The canonical representation of the observable.

### qk\_obs\_copy

`QkObs *qk_obs_copy(const QkObs *obs)`

Copy the observable.

#### Example

```c
QkObs *original = qk_obs_identity(100);
QkObs *copied = qk_obs_copy(original);
```

#### Safety

Behavior is undefined `obs` is not a valid, non-null pointer to a `QkObs`.

**Parameters**

- **obs** – A pointer to the observable.

**Returns**

A pointer to a copy of the observable.

### qk\_obs\_equal

`bool qk_obs_equal(const QkObs *obs, const QkObs *other)`

Compare two observables for equality.

Note that this does not compare mathematical equality, but data equality. This means that two observables might represent the same observable but not compare as equal.

#### Example

```c
QkObs *observable = qk_obs_identity(100);
QkObs *other = qk_obs_identity(100);
bool are_equal = qk_obs_equal(observable, other);
```

#### Safety

Behavior is undefined if `obs` or `other` are not valid, non-null pointers to `QkObs`\ s.

**Parameters**

- **obs** – A pointer to one observable.
- **other** – A pointer to another observable.

**Returns**

`true` if the observables are equal, `false` otherwise.

### qk\_obs\_str

`char *qk_obs_str(const QkObs *obs)`

Return a string representation of a `QkObs`.

#### Example

```c
QkObs *obs = qk_obs_identity(100);
char *string = qk_obs_str(obs);
qk_str_free(string);
qk_obs_free(obs);
```

#### Safety

Behavior is undefined `obs` is not a valid, non-null pointer to a `QkObs`.

The string must not be freed with the normal C free, you must use `qk_str_free` to free the memory consumed by the String. Not calling `qk_str_free` will lead to a memory leak.

Do not change the length of the string after it’s returned (by writing a nul byte somewhere inside the string or removing the final one), although values can be mutated.

**Parameters**

- **obs** – A pointer to the `QkObs` to get the string for.

**Returns**

A pointer to a nul-terminated char array of the string representation for `obs`

### qk\_str\_free

`void qk_str_free(char *string)`

Free a string representation.

#### Safety

Behavior is undefined if `str` is not a pointer returned by `qk_obs_str` or `qk_obsterm_str`.

**Parameters**

- **string** – A pointer to the returned string representation from `qk_obs_str` or `qk_obsterm_str`.

### qk\_obs\_to\_python

`PyObject *qk_obs_to_python(QkObs *obs)`

Pass ownership of a `QkObs` object to Python.

It is not safe to use the `QkObs` pointer after calling this function. In particular, you should not attempt to clear or free it. The caller must own the `QkObs`, not hold a borrowed reference (for example, a `QkObs *` retrieved from `qk_obs_borrow_from_python` is not owned).

#### Safety

The caller must be attached to a Python interpreter. Behavior is undefined if `obs` is not a valid non-null pointer to an initialized and owned `QkObs`.

**Parameters**

- **obs** – The owned object.

**Returns**

An owned Python reference to the object.

### qk\_obs\_borrow\_from\_python

`QkObs *qk_obs_borrow_from_python(PyObject *ob)`

Retrieve a `QkObs` pointer from a Python object.

This borrows a Python reference and extracts the `QkObs` pointer for it, if it is of the correct type. The returned pointer is borrowed from the `ob` pointer. If the `PyObject` is not the correct type, the return value is `NULL` and the exception state of the Python interpreter is set.

You must be attached to a Python interpreter to call this function.

You can also use `qk_obs_convert_from_python`, which is logically the exact same as this function, but can be directly used as a “converter” function for the `PyArg_Parse*` family of Python converter functions.

#### Safety

The caller must be attached to a Python interpreter. Behavior is undefined if `ob` is not a valid non-null pointer to a Python object.

**Parameters**

- **ob** – A borrowed Python object.

**Returns**

A pointer to the native object, or `NULL` if the Python object is the wrong type.

### qk\_obs\_convert\_from\_python

`int qk_obs_convert_from_python(PyObject *object, void *address)`

Retrieve a `QkObs` pointer from a Python object.

This borrows a Python reference and extracts the `QkObs` pointer for it into `address`, if it is of the correct type. The returned pointer is borrowed from the `object` pointer. If the `PyObject` is not the correct type, the return value is 1, the exception state of the Python interpreter is set, and `address` is unchanged.

You must be attached to a Python interpreter to call this function.

You can also use `qk_obs_borrow_from_python`, which is logically the exact same as this, but with a more natural signature for direct usage.

#### Safety

The caller must be attached to a Python interpreter. Behavior is undefined if `object` is not a valid non-null pointer to a Python object, or if `address` is not a pointer to writeable data of the correct type.

**Parameters**

- **object** – A borrowed Python object.
- **address** – The location to write the output to.

**Returns**

1 on success, 0 on failure.
