> ## 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.

# Problem Formulations

> Different problem formulations for optimization problems.

<a id="module-haiqu.sdk.optimization.qubo" />

### *class* haiqu.sdk.optimization.qubo.QUBO

A class representing a Quadratic Unconstrained Binary Optimization (QUBO) problem.

This class provides a unified interface for QUBO problems, supporting construction
from multiple formats and conversion to Qiskit-compatible representations.

A QUBO problem can be initialized in multiple ways:

1. From a Docplex model / CPLEX file
2. From an Ising-like Hamiltonian (SparsePauliOp)
3. From a Qiskit QuadraticProgram.

#### cost(bitstring: str) → float

Compute the QUBO objective for a given bitstring.

Uses the formula: $f(x) = c + a^T x + \sum_{i<j} Q_{ij} x_i x_j$
where $x_i \in \{0, 1\}$ are binary variables.

Note on Qiskit's Quadratic Coefficient Storage:
: Qiskit's QuadraticExpression stores coefficients in **upper-triangle format only**:

<br />

$$
f(x) = c + \sum_i a_i x_i + \sum_i \sum_{j>i} Q_{ij} x_i x_j
$$

<br />

* Only $Q_{ij}$ for $i < j$ are stored (via `to_dict()`)
* Each stored $Q_{ij}$ is the **full coefficient** (not doubled)
* Example: If `to_dict()` returns `{(0,1): 0.5}`, the contribution is `0.5 * x_0 * x_1`

<br />

**Important:** When you pass a symmetric matrix to `minimize(quadratic=[[...]])`,
Qiskit sums symmetric entries. For instance, `[[0,1],[1,0]]` stores Q\[0,1]=2.

* **Parameters:**
  **bitstring** -- Bitstring in Qiskit convention (little-endian, rightmost bit = qubit 0).
* **Returns:**
  The objective value for the given bitstring.
* **Return type:**
  float

#### NOTE

Bitstrings use Qiskit convention: rightmost bit = qubit 0.
Example: "101" means x0=1, x1=0, x2=1 (for var\_names=\['x0','x1','x2']).

#### *classmethod* from\_docplex(docplex\_model) → [QUBO](#haiqu.sdk.optimization.qubo.QUBO)

Create from a DOcplex model (docplex.mp.model.Model).

#### *classmethod* from\_file(path: str) → [QUBO](#haiqu.sdk.optimization.qubo.QUBO)

Load a problem from a CPLEX/LP file and convert it to QUBO form.

#### *classmethod* from\_hamiltonian(H: SparsePauliOp, offset: float = 0.0) → [QUBO](#haiqu.sdk.optimization.qubo.QUBO)

Create QUBO from an Ising Hamiltonian represented as a Pauli operator.

This method converts an Ising model Hamiltonian (with spin variables sᵢ ∈ {-1, +1})
to QUBO formulation (with binary variables xᵢ ∈ {0, 1}) using the mapping sᵢ = 1 - 2·xᵢ.

**Input Format:**
: The Hamiltonian is given as a SparsePauliOp containing Pauli Z operators:

<br />

* Single Z terms (e.g., 'Z', 'IZI') represent local fields hᵢ
* Products of Zs (e.g., 'ZZ', 'IZZI') represent couplings Jᵢⱼ
* Pauli X or Y operators are not supported (pure Ising model)

**Conversion Formula:**
: Ising Hamiltonian: H = Σ hᵢ·sᵢ + Σ Jᵢⱼ·sᵢ·sⱼ + offset  (sᵢ ∈ {-1, +1})

<br />

The conversion uses the mapping: sᵢ = 1 - 2·xᵢ where xᵢ ∈ {0, 1}

<br />

This transforms to QUBO: f(x) = c + Σ aᵢ·xᵢ + Σ Qᵢⱼ·xᵢ·xⱼ

<br />

**Detailed Conversion for Each Term Type:**

<br />

1. **Local field term** hᵢ·sᵢ (where sᵢ ∈ {-1, +1}):

<br />

Substitute sᵢ = 1 - 2·xᵢ:
: = hᵢ·(1 - 2·xᵢ)
\= hᵢ - 2·hᵢ·xᵢ

<br />

Contributes:
: - Constant: +hᵢ

* Linear xᵢ: -2·hᵢ

2. **Coupling term** Jᵢⱼ·sᵢ·sⱼ:

<br />

Substitute sᵢ = 1-2·xᵢ and sⱼ = 1-2·xⱼ:
: = Jᵢⱼ·(1 - 2·xᵢ)·(1 - 2·xⱼ)
\= Jᵢⱼ·\[1 - 2·xᵢ - 2·xⱼ + 4·xᵢ·xⱼ]

<br />

Contributes:
: - Constant: +Jᵢⱼ

* Linear xᵢ: -2·Jᵢⱼ
* Linear xⱼ: -2·Jᵢⱼ
* Quadratic xᵢ·xⱼ: +4·Jᵢⱼ

<br />

**Important:** When multiple Pauli terms are present, their contributions are **summed**.
For example, if both hᵢ·Zᵢ and Jᵢⱼ·Zᵢ·Zⱼ affect variable xᵢ, the linear coefficients
add: aᵢ = -2·hᵢ + (-2·Jᵢⱼ) = -2·(hᵢ + Jᵢⱼ).

<br />

**Note on Normalization:** Qiskit's `from_ising()` stores linear terms as diagonal
entries in the quadratic matrix (e.g., -2·hᵢ becomes Q\[i,i] = -2·hᵢ). These diagonal
terms are automatically normalized to linear coefficients by `from_quadratic_program()`,
since for binary variables xᵢ² = xᵢ.

**Implementation:**
: Uses Qiskit's `from_ising()` function to perform the conversion, which handles
the Pauli operator algebra and coefficient transformations automatically. The
resulting QuadraticProgram is then normalized via `from_quadratic_program()`.

* **Parameters:**
  * **H** -- Ising Hamiltonian as a SparsePauliOp (must contain only Z operators)
  * **offset** -- Additional constant offset to add to the Hamiltonian. Defaults to 0.0.
* **Returns:**
  QUBO instance representing the same optimization problem
* **Raises:**
  * **TypeError** -- If H is not a SparsePauliOp
  * **QiskitOptimizationError** -- If H contains Pauli X or Y operators
  * **QiskitOptimizationError** -- If any Pauli term acts on more than 2 qubits (only pairwise interactions supported)

### Example

```python theme={null}
>>> from qiskit.quantum_info import SparsePauliOp
>>>
>>> # Create simple Ising Hamiltonian: H = 1.0·Z₀·Z₁ (coupling only)
>>> # This represents interaction between spins: J₀₁·s₀·s₁ with J₀₁ = 1.0
>>> H = SparsePauliOp.from_list([('ZZ', 1.0)])
>>> qubo = QUBO.from_hamiltonian(H, offset=0.0)
>>>
>>> # The conversion applies: J₀₁·s₀·s₁ = J₀₁·(1-2x₀)·(1-2x₁)
>>> # Expanding: J₀₁ - 2·J₀₁·x₀ - 2·J₀₁·x₁ + 4·J₀₁·x₀·x₁
>>> # With J₀₁ = 1.0:
>>> #   Constant: +1.0
>>> #   Linear x₀: -2.0 (normalized from diagonal Q[0,0])
>>> #   Linear x₁: -2.0 (normalized from diagonal Q[1,1])
>>> #   Quadratic x₀·x₁: +4.0
>>>
>>> print(qubo._qp.objective.constant)  # 1.0
>>> print(qubo._qp.objective.linear.to_dict())  # {0: -2.0, 1: -2.0}
>>> print(qubo._qp.objective.quadratic.to_dict())  # {(0,1): 4.0}
```

#### *classmethod* from\_lp\_string(lp\_content: str) → [QUBO](#haiqu.sdk.optimization.qubo.QUBO)

Deserialize QUBO from LP file format string.

* **Parameters:**
  **lp\_content** -- LP file content as string
* **Returns:**
  QUBO instance

#### *classmethod* from\_quadratic\_program(qp: QuadraticProgram) → [QUBO](#haiqu.sdk.optimization.qubo.QUBO)

Create a QUBO from a Qiskit QuadraticProgram.

Note on Qiskit's Quadratic Coefficient Storage:
: Qiskit's QuadraticExpression internally stores quadratic coefficients in
**upper-triangle format only** (i.e., only $Q_{ij}$ for $i < j$).

<br />

The objective function form is:

<br />

$$
f(x) = c + \sum_i a_i x_i + \sum_i \sum_{j>i} Q_{ij} x_i x_j
$$

<br />

**Important Details:**

<br />

* When you call `qp.objective.quadratic.to_dict()`, it returns only the upper triangle
* Each stored $Q_{ij}$ represents the **full coefficient** of $x_i x_j$ (not doubled)
* When setting coefficients via `qp.objective.quadratic[(i,j)]`, indices are normalized
  to `(min(i,j), max(i,j))`, so setting Q\[1,0] overwrites Q\[0,1]
* When passing a full symmetric matrix to `minimize(quadratic=[[...]])`, Qiskit **sums**
  the symmetric entries (e.g., Q\[0,1]=1 and Q\[1,0]=1 → stored as Q\[0,1]=2)

<br />

All QUBO methods in this class properly handle Qiskit's upper-triangle representation.

#### to\_file(path: str) → str

Export the QUBO as a CPLEX LP file.

#### to\_hamiltonian() → Tuple\[SparsePauliOp, float]

Convert QUBO to an Ising Hamiltonian represented as Pauli operators.

This method performs the **inverse transformation** of `from_hamiltonian()`,
converting from QUBO formulation (binary variables xᵢ ∈ {0, 1}) back to Ising
model (spin variables sᵢ ∈ {-1, +1}) using the inverse mapping xᵢ = (1 - sᵢ)/2.

**Output Format:**
: Returns a tuple `(H, offset)` where:

<br />

* `H`: SparsePauliOp containing the Ising Hamiltonian as Pauli Z operators
* `offset`: Float constant offset term

**Conversion Formula (QUBO → Ising):**
: QUBO objective: f(x) = c + Σ aᵢ·xᵢ + Σ Qᵢⱼ·xᵢ·xⱼ  (xᵢ ∈ {0, 1})

<br />

Using inverse substitution xᵢ = (1 - sᵢ)/2, this becomes:

<br />

Ising Hamiltonian expectation: ⟨H⟩ = Σ hᵢ·sᵢ + Σ Jᵢⱼ·sᵢ·sⱼ + offset  (sᵢ ∈ {-1, +1})

<br />

Where the coefficients are derived by reversing the Ising→QUBO transformation.

**Implementation:**
: Uses Qiskit's `QuadraticProgram.to_ising()` method to perform the conversion,
which handles the variable substitution and Pauli operator construction automatically.

* **Returns:**
  A tuple containing:
  : - SparsePauliOp: The Ising Hamiltonian with Pauli Z operators
  * float: The constant offset term
* **Return type:**
  Tuple\[SparsePauliOp, float]

### Example

```python theme={null}
>>> # Create QUBO with linear and quadratic terms
>>> from qiskit_optimization import QuadraticProgram
>>> qp = QuadraticProgram()
>>> qp.binary_var('x0')
>>> qp.binary_var('x1')
>>> qp.minimize(linear={'x0': 0.5}, quadratic={('x0', 'x1'): 1.0})
>>> qubo = QUBO.from_quadratic_program(qp)
>>> H, offset = qubo.to_hamiltonian()
>>> # H is a SparsePauliOp like: 0.5*Z_0 + 1.0*Z_0*Z_1
>>> # offset is the constant term
>>> # Can now use with Qiskit quantum algorithms:
>>> from qiskit_algorithms import QAOA
>>> qaoa = QAOA(sampler=sampler, optimizer=optimizer)
>>> result = qaoa.compute_minimum_eigenvalue(H)
```

#### SEE ALSO

* `from_hamiltonian()`: Inverse operation (Ising → QUBO)

#### to\_lp\_string() → str

Serialize QUBO to LP file format string.

* **Returns:**
  LP file content as string
* **Return type:**
  str
