> ## Documentation Index
> Fetch the complete documentation index at: https://docs.haiqu.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# SU(2)-equivariant gates

> SU(2)-equivariant gates, ansatze, verification, spin helpers, and compilation.

### haiqu.sdk.qml.equivariant.su2\_equivariant\_2\_qubit\_gate(theta)

The 2-qubit SU(2)-equivariant gate.

Under total spin, two qubits split into a 1-dimensional spin-0 "singlet"
subspace (the antisymmetric state `(|01> - |10>)/sqrt(2)`) and a
3-dimensional spin-1 "triplet" subspace (the symmetric states). This gate
phases the singlet by `exp(i*theta)` and acts as identity on the
triplet. It is the unique 2-qubit SU(2)-equivariant gate (up to global
phase) and the building block for the n-qubit ansatz.

Hardware form: `cx . crx(theta) . p(theta/2) . cx`.

* **Parameters:**
  **theta** -- Angle. Accepts `float` or a qiskit `Parameter` /
  `ParameterExpression`; the gate is equivariant for any value.
* **Returns:**
  A 2-qubit `QuantumCircuit` implementing the gate.
* **Return type:**
  *QuantumCircuit*

### Example

```python theme={null}
>>> from haiqu.sdk.qml.equivariant import su2_equivariant_2_qubit_gate
>>> gate = su2_equivariant_2_qubit_gate(0.7)
>>> gate.num_qubits
2
```

### haiqu.sdk.qml.equivariant.su2\_equivariant\_3\_qubit\_gate(theta0, theta1, theta2, theta3)

The exact 3-qubit SU(2)-equivariant gate.

The 3-qubit Hilbert space decomposes as spin-3/2 (dim 4, multiplicity 1)
plus two copies of spin-1/2 (dim 2, multiplicity 2). The equivariant gate
is identity on the spin-3/2 sector and applies a generic `U_2 (x) I_2`
that mixes the two spin-1/2 copies while leaving the magnetic label
intact. The four angles parametrize `U_2`.

Constructed as `S_3 . diag(I_4, U_2(theta) (x) I_2) . S_3^dagger` where
`S_3` is the 3-qubit Schur transform. The four angles are the Euler
angles of `U_2` in qiskit's `CUGate(theta, phi, lam, gamma)`
convention (a controlled `U(theta, phi, lam)` with global phase
`gamma`):

* **Parameters:**
  * **theta0** -- Global phase applied to the `U_2` block (`PhaseGate`).
  * **theta1** -- The `lam` Euler angle of `U_2`.
  * **theta2** -- The `theta` (rotation) Euler angle of `U_2`.
  * **theta3** -- The `phi` Euler angle of `U_2`.
* **Returns:**
  A 3-qubit `QuantumCircuit` implementing the gate.
* **Return type:**
  *QuantumCircuit*

### haiqu.sdk.qml.equivariant.su2\_equivariant\_ansatz(num\_qubits, layout='brickwork', num\_layers=1, name='theta')

Build a parametrized SU(2)-equivariant ansatz.

Returns a `QuantumCircuit` with a `ParameterVector` of free angles,
one per 2-qubit `su2` gate. Equivariance is a structural property of
the building block and holds for any parameter binding the optimiser
ever tries: `su2(theta) = exp(i*theta*P_ij)` where
`P_ij = (I - SWAP_ij) / 2` is the pair-singlet projector, and
`P_ij` commutes with the global `S_x`, `S_y`, `S_z`. Products
of equivariant unitaries are equivariant, so the whole parametrized
circuit lies inside the equivariant subgroup of unitaries.

Contrast with Qiskit's `EfficientSU2`: that ansatz uses single-qubit
Pauli rotations, which do NOT commute with the global spin operators,
so it is not equivariant for any non-trivial parameter binding.

* **Parameters:**
  * **num\_qubits** (*int*) -- Number of qubits.
  * **layout** (*str* *|* *List* \*\[\**List* \*\[\**int* *]* *]*) -- `"brickwork"` (nearest-neighbour even-then-odd; the
    default), `"linear"` (chain of adjacent pairs), or a list of
    `[i, j]` qubit-pair lists for a custom layout.
  * **num\_layers** (*int*) -- Repetitions of the chosen layout pattern. Defaults to 1.
  * **name** (*str*) -- `ParameterVector` name (default `"theta"`).
* **Returns:**
  A parametrized `QuantumCircuit` over `num_qubits` qubits. Free
  parameters are accessible via `circuit.parameters`.

- **Raises:**
  **ValueError** -- If `layout` is not a recognised string or a list of
  qubit pairs.

* **Return type:**
  *QuantumCircuit*

### Example

```python theme={null}
>>> from haiqu.sdk.qml.equivariant import su2_equivariant_ansatz
>>> ansatz = su2_equivariant_ansatz(num_qubits=4, num_layers=2)
>>> len(ansatz.parameters)
6
```

### haiqu.sdk.qml.equivariant.brickwork\_pattern(num\_qubits, num\_layers)

Nearest-neighbour brickwork pattern: even bonds, then odd bonds, repeated.

* **Parameters:**
  * **num\_qubits** (*int*) -- Number of qubits.
  * **num\_layers** (*int*) -- Number of brickwork layers.
* **Returns:**
  A list of `[i, j]` qubit pairs in application order.
* **Return type:**
  *List*\[*List*\[int]]

### Example

```python theme={null}
>>> brickwork_pattern(4, 1)
[[0, 1], [2, 3], [1, 2]]
```

### haiqu.sdk.qml.equivariant.is\_su2\_equivariant(U, tol=1e-08)

Check whether a unitary is SU(2)-equivariant.

A gate `U` on n qubits is SU(2)-equivariant iff it commutes with the
three global spin generators `S_x`, `S_y`, `S_z`. Commuting with
every generator is necessary AND sufficient for commuting with the whole
SU(2) group, and avoids forming the total-spin Casimir
`S^2 = S_x^2 + S_y^2 + S_z^2` (each `S_a` is a sparse sum of n Paulis).

Checking only `[U, S^2] = [U, S_z] = 0` (with `S^2` the Casimir above)
is strictly weaker: for example `exp(i*phi*S_z)` commutes with both yet
is NOT equivariant.

* **Parameters:**
  * **U** -- `2^n` by `2^n` unitary as an array-like.
  * **tol** (*float*) -- Threshold on the largest commutator entry.
* **Returns:**
  Tuple `(ok, violation)` where
  `ok = (max_a |[U, S_a]| < tol)` and `violation` is the maximum
  commutator entry over `a` in `{x, y, z}`.

- **Raises:**
  **ValueError** -- If the matrix shape is not `(2^n, 2^n)`.

* **Return type:**
  *Tuple*\[bool, float]

### haiqu.sdk.qml.equivariant.spin\_generators(n)

Dense SU(2) generators `(S_x, S_y, S_z)` for n qubits.

* **Parameters:**
  **n** (*int*) -- Number of qubits.
* **Returns:**
  A tuple `(S_x, S_y, S_z)` of dense `(2**n, 2**n)` complex matrices.
* **Return type:**
  *Tuple*\[*ndarray*, *ndarray*, *ndarray*]

### haiqu.sdk.qml.equivariant.total\_spin\_ops(n)

Return dense matrices `(S^2, S_z)` for n qubits.

* **Parameters:**
  **n** (*int*) -- Number of qubits.
* **Returns:**
  Tuple of dense `(2**n, 2**n)` matrices for the total-spin Casimir
  operator `S^2 = S_x^2 + S_y^2 + S_z^2` and the z-component `S_z`.
* **Return type:**
  *Tuple*\[*ndarray*, *ndarray*]

#### Haiqu.su2\_equivariant\_compilation(target, \*, target\_fidelity=0.99, max\_layers=6, num\_restarts=10, seed=0)

Compress an SU(2)-equivariant gate into a brick of 2-qubit `su2` gates.

Submits a job that fits a shallow brickwork of 2-qubit `su2` gates to
`target` and returns the compressed circuit. The headline use is
compressing the exact 3-qubit equivariant gate into a 2q brick, which
transpiles to substantially fewer two-qubit gates.

The target must be SU(2)-equivariant (commute with the global spin
generators); non-equivariant inputs fail the job. Inherently small-n:
the fit needs the dense `2^n` by `2^n` target unitary, so the
target is capped at 10 qubits. For larger systems, build a
parametrized `su2_equivariant_ansatz()` and optimise
at the state level instead.

* **Parameters:**
  * **target** (*QuantumCircuit* *|* *Gate* *|* *np.ndarray*) -- SU(2)-equivariant
    target. Accepts a `QuantumCircuit`, a `Gate`, or a
    `2^n` by `2^n` `numpy.ndarray`.
  * **target\_fidelity** (*float*) -- Requested process fidelity to the target
    unitary (default `0.99`); a gate (process) fidelity, distinct
    from the state fidelity used elsewhere in the SDK. This is the
    goal the fit aims for, not a guarantee: the returned circuit may
    fall short, so check `job.fidelity` on the result. The fit
    escalates brickwork depth trying to clear this bar.
  * **max\_layers** (*int*) -- Cap on brickwork depth before giving up.
  * **num\_restarts** (*int*) -- Number of optimiser restarts.
  * **seed** (*int*) -- Random seed for restart initialisation.
* **Returns:**
  The compression job. Call
  `job.result()` to retrieve the compressed circuit as a
  `CircuitModel` and read `job.fidelity` for the achieved
  process fidelity.
* **Return type:**
  [Su2EquivariantCompilationJobModel](#haiqu.sdk.schemas.Su2EquivariantCompilationJobModel)

- **Raises:**
  * **ValueError** -- If `target` exceeds the 10-qubit limit.
  * **TypeError** -- If `target` is not a QuantumCircuit, Gate, or ndarray.

### Example

```python theme={null}
>>> from haiqu.sdk.qml import su2_equivariant_3_qubit_gate
>>> target = su2_equivariant_3_qubit_gate(0.8, 1.2, 0.5, 2.1)
>>> job = haiqu.su2_equivariant_compilation(target, target_fidelity=0.99)
>>> circuit = job.result()
>>> job.fidelity >= 0.99
True
```

Transpiling both to a device basis shows the two-qubit-gate drop:

```python theme={null}
>>> dev = haiqu.get_device("fake_marrakesh")
>>> orig = haiqu.transpile(target, device=dev)
>>> comp = haiqu.transpile(circuit, device=dev)
>>> comp.analytics.gates_2q < orig.analytics.gates_2q
True
```

### *class* haiqu.sdk.schemas.Su2EquivariantCompilationJobModel(\*, id, name=None, description=None, user\_id, experiment\_id, status, job\_type, device\_id='Haiqu OS', creation\_date, run\_date=None, finish\_date=None, logs=None, quality=None, info=None, time=None, parameters, circuit\_id=None, compression\_type)

Job returned for SU(2)-equivariant gate compilation.

Mirrors the state-compression surface: `result()` returns the
compressed circuit and `fidelity` is the achieved process fidelity of
the compressed brick to the target unitary. The fit runs server-side;
only the circuit and the fidelity are exposed.

* **Parameters:**
  * **id** (*str*)
  * **name** (*str* *|* *None*)
  * **description** (*str* *|* *None*)
  * **user\_id** (*int*)
  * **experiment\_id** (*str*)
  * **status** (*JobStatus*)
  * **job\_type** (*JobType*)
  * **device\_id** (*str* *|* *None*)
  * **creation\_date** (*datetime*)
  * **run\_date** (*datetime* *|* *None*)
  * **finish\_date** (*datetime* *|* *None*)
  * **logs** (*str* *|* *None*)
  * **quality** (*float* *|* *None*)
  * **info** (*dict* *|* *None*)
  * **time** (*float* *|* *None*)
  * **parameters** (*dict*)
  * **circuit\_id** (*str* *|* *None*)
  * **compression\_type** (*CompressionJobType*)

#### *property* fidelity *: float | None*

Process fidelity of the compressed brick to the target unitary.

#### progress()

Display the progress widget, stream logs from the job.

#### result()

Return job result - the compressed circuit.
Block and wait for the job to complete.

* **Return type:**
  *CircuitModel*

#### retrieve\_status()

Query backend for the job status.

* **Return type:**
  *JobStatus*
