{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# QLOQ (Qubit Logic on Qudits)\n",
"\n",
"Welcome to this tutorial, where we explore **QLOQ** (Qubit Logic on Qudits) for linear optical quantum computing. We will break down the core concepts behind encoding multiple qubits into a single photon (qudit), demonstrate how **intra-group** gates (like CNOT and single-qubit rotations) are implemented **without** the usual success-probability issues, and show how **Ralph CZ** gates can link multiple qudit blocks.\n",
"\n",
"## References\n",
"\n",
"[1] L. Lysaght, T. Goubault, P. Sinnott, S. Mansfield, P-E. Emeriau, \"Quantum circuit compression using qubit logic on qudits,\" https://arxiv.org/abs/2411.03878v1 (2024).\n",
"\n",
"## I. Introduction to QLOQ\n",
"\n",
"**QLOQ** (Qubit Logic on Qudits) is a specialized architecture in linear optics that encodes multiple qubits **within a single photon**. Traditionally, a 2-qubit operation in linear optics involves two separate photons interfering at a beam splitter with a probabilistic success rate. However, **QLOQ** circumvents that for intra-group gates by confining both qubits to one photon’s modes.\n",
"\n",
"- **Inter-group** entangling gates (between different photons) still rely on a *Ralph CZ* gate, which is post-selected (probabilistic). Inter-group entangling gates are accomplished via an unbalanced Ralph CZ to accomplish a multi-controlled Z operation. A balanced Ralph CZ has the same success probability(1/9) for each input state. An **unbalanced** version has different success probability for each input. It was chosen because it performs empirically better than the standard CCCZ and requires less post-selected modes.\n",
"- **Intra-group** gates (like CNOT, CZ, single-qubit rotations) become deterministic mode permutations and transformations."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## II. Qudits & CNOT in a Single Photon\n",
"\n",
"### Qudits\n",
"A **qubit** is a 2-level system $|0\\rangle$ or $|1\\rangle$. A **qudit** extends this to $d$ levels. For a **2-qubit** block, $d = 4$. We treat each logical basis state as a unique optical mode:\n",
"\n",
"$|00\\rangle \\rightarrow$ mode 0 \n",
"$|01\\rangle \\rightarrow$ mode 1 \n",
"$|10\\rangle \\rightarrow$ mode 2 \n",
"$|11\\rangle \\rightarrow$ mode 3 \n",
"\n",
"A single photon occupying exactly one of these four modes represents any superposition of the 2-qubit space.\n",
"\n",
"### CNOT Within a Qudit\n",
"In standard linear optics, a **CNOT** between two separate photons is probabilistic. In QLOQ, if both qubits are in the *same* photon, a CNOT is merely a **mode permutation**:\n",
"\n",
"- $|10\\rangle \\rightarrow |11\\rangle$\n",
"- $|00\\rangle$ and $|01\\rangle$ remain the same\n",
"\n",
"The complete mapping is:\n",
"\n",
"Mode Index | Binary State | CNOT Output\n",
"-----------|--------------------|-------------\n",
"0 | $\\lvert 00\\rangle$ | $\\lvert 00\\rangle$\n",
"1 | $\\lvert 01\\rangle$ | $\\lvert 01\\rangle$\n",
"2 | $\\lvert 10\\rangle$ | $\\lvert 11\\rangle$\n",
"3 | $\\lvert 11\\rangle$ | $\\lvert 10\\rangle$\n",
"\n",
"This operation is **deterministic** because it's implemented entirely within the single photon's modes, bypassing the usual success probability constraints.\n",
"\n",
"> **CZ** is similarly done by a mode permutation + Hadamards on the target qubit."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## III. Applying Rotations in a Qudit Group\n",
"When **multiple qubits** are encoded into a **single photon** (a qudit), each logical qubit corresponds to a specific **pairing of modes**. For a **2-qubit** group (4 modes total):\n",
"\n",
"- **Modes**: \n",
" $0 \\rightarrow |00\\rangle$ \n",
" $1 \\rightarrow |01\\rangle$ \n",
" $2 \\rightarrow |10\\rangle$ \n",
" $3 \\rightarrow |11\\rangle$\n",
"\n",
"- **Second qubit** flips between $|0\\rangle$ and $|1\\rangle$ in the *rightmost bit*, so to rotate it, we **pair**:\n",
" - $(0,1) \\rightarrow |00\\rangle, |01\\rangle$\n",
" - $(2,3) \\rightarrow |10\\rangle, |11\\rangle$\n",
"\n",
"- **First qubit** flips in the *leftmost bit*, so to rotate it, we **pair**:\n",
" - $(0,2) \\rightarrow |00\\rangle, |10\\rangle$\n",
" - $(1,3) \\rightarrow |01\\rangle, |11\\rangle$\n",
"\n",
"- So in practice we would apply a 2 mode parametrized beamsplitter for the specific rotation we want to achieve ($R_x$, $R_y$, $R_z$ etc) for each combination corresponding to the qubit we wish to act on.\n",
"\n",
"- Thankfully all this logic and all relevant swaps have been pre-coded into the ansatz builder so the user need not worry too much about them.\n",
"\n",
"> **Key Insight**: **Intra-group operations** are layerwise, meaning you stack them: first apply a rotation on the second qubit via parametrized beamsplitters on the correct pairs of modes, then do some internal mode swap, then apply a rotation on the first qubit by doing similarly, etc."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## IV. Building QLOQ Experiments with Perceval\n",
"\n",
"Perceval provides a `QLOQ ansatz` that helps you define:\n",
"\n",
"1. **Group Sizes**: e.g., `[Encoding.QUDIT2, Encoding.QUDIT2]`\n",
" - can do Encoding.DUAL_RAIL, Encoding.QUDIT3 etc\n",
"2. **Layers**: e.g., `[\"Y\"]` for Ry rotations\n",
"3. **Phases**: the numerical angles for each rotation (or `None` for symbolic)\n",
"4. **Entangling Gate** (`ctype`): either `\"cx\"` or `\"cz\"` inside each group\n",
"\n",
"Below is a minimal code snippet showing how to construct and display a QLOQ experiment."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"from perceval import LogicalState, pdisplay, catalog, Encoding, SimulatedComputer\n",
"import perceval as pcvl\n",
"import numpy as np\n",
"from scipy.optimize import minimize\n",
"from typing import List, Dict, Tuple, Optional\n",
"import matplotlib.pyplot as plt"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Number of required phases: 16\n"
]
},
{
"data": {
"image/svg+xml": [
"\n",
""
],
"text/plain": [
""
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Example: Building a QLOQ experiment in Perceval\n",
"ansatz = catalog[\"qloq ansatz\"]\n",
"\n",
"# Define groups of qubits (each group is a qudit).\n",
"group_sizes = [Encoding.QUDIT2, Encoding.QUDIT2]\n",
"\n",
"# Choose the single-qubit rotation layers we want (Y, X, or Z)\n",
"layers = [\"Y\"] # e.g., apply RY rotations in each group\n",
"#generally RY rotations are sufficient\n",
"\n",
"# (Optional) Provide numeric phases or use None for symbolic placeholders\n",
"nb_phases = ansatz.get_parameter_nb(group_sizes, len(layers))\n",
"print(\"Number of required phases:\", nb_phases)\n",
"\n",
"phases = None # Use symbolic parameters for visualization\n",
"\n",
"# You can also build the QLOQ experiment with 'ctype=\"cx\"'\n",
"experiment = ansatz.build_experiment(\n",
" group_sizes=group_sizes,\n",
" layers=layers,\n",
" phases=phases,\n",
" ctype=\"cz\"\n",
")\n",
"\n",
"pdisplay(experiment, recursive=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The ansatz in the qubit picture will then have this form, with layers of 2 qubit blocks linked by a multi-controlled Z gate which takes the form of an unbalanced CCCZ here using the ralph CZ."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## V. Summary\n",
"\n",
"**QLOQ** offers a unique way to encode **multiple qubits** in **one photon** (creating qudits). The major advantage is that **CNOTs and single-qubit rotations** within that qudit can be executed deterministically without the usual success probability of two-photon gates. When linking multiple qudit blocks, **Ralph CZ** gates are used, which are **probabilistic** and post-selected.\n",
"\n",
"### Key Points\n",
"- **Qudits**: 2 qubits = 4 modes in a single photon, 3 qubits = 8 modes, etc.\n",
"- **Intra-group** gates (e.g., CNOT, Ry, Rz, Rx → Mode permutations and layers of beamsplitters.\n",
"- **Inter-group** gates → Ralph CZ (post-selected) forming an unbalanced multi-controlled Z.\n",
"- **Layerwise approach**: We apply rotations/cnot gates in a sequence of “layers,” possibly swapping modes to target the correct qubit.\n",
"\n",
"In upcoming sections, we will:\n",
"- **Integrate** a classical optimizer (e.g., COBYLA) to do VQE-like or QAOA-like tasks on these QLOQ circuits.\n",
"- Explore **QUBO** matrices and measure the resulting cost function from the photonic simulator."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## VI. Encoding Functions Explained\n",
"\n",
"These two functions, `to_fock_state` and `fock_to_qubit_state`, convert between:\n",
"\n",
"1. A **bitstring representation** of qubits (e.g., `\"0101\"`)\n",
"2. A **Fock-state representation** (a list of occupation numbers, eventually wrapped in a `pcvl.BasicState`)\n",
"\n",
"### Key Idea\n",
"- **Bitstring**: Represents qubits in the usual binary sense, e.g. `\"00\"` or `\"01\"`.\n",
"- **Fock State**: In Perceval, a `BasicState` is a list of photon occupation numbers for each mode. A single photon occupying one of $2^n$ possible modes (for an $n$-qubit group) is stored as a one-hot vector (e.g., `[0,1,0,0]` for mode 1 out of 4).\n",
"\n",
"These functions handle situations where **multiple groups** of qubits are each encoded in a **qudit**. For example:\n",
"\n",
"- A group of **size=2** means 4 modes\n",
"- A group of **size=3** means 8 modes, etc."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"def fock_to_qubit_state(fock_state: List[int], group_sizes: List[int]) -> Optional[str]:\n",
" \"\"\"\n",
" Convert a Perceval Fock-state representation back to a multi-qubit bitstring.\n",
"\n",
" Args:\n",
" fock_state: a one-hot vector for all groups (concatenated)\n",
" group_sizes: each integer indicates how many qubits are in that group\n",
"\n",
" Returns:\n",
" A bitstring (e.g. \"0101\"), or None if the Fock state is invalid.\n",
" \"\"\"\n",
"\n",
" fock_state = [i for i in fock_state]\n",
"\n",
" # Expected total length = sum of (2^group_size) for each group\n",
" expected_length = sum([2 ** size for size in group_sizes])\n",
" if len(fock_state) != expected_length:\n",
" return None\n",
"\n",
" offset = 0\n",
" qubit_state_binary = \"\"\n",
"\n",
" for size in group_sizes:\n",
" group_length = 2 ** size\n",
" group_fock_state = fock_state[offset : offset + group_length]\n",
"\n",
" # We expect exactly one '1' in the chunk (indicating the photon mode)\n",
" if group_fock_state.count(1) != 1:\n",
" return None\n",
"\n",
" state_index = group_fock_state.index(1)\n",
" # Convert index to binary (of width 'size' bits)\n",
" binary_state = format(state_index, f'0{size}b')\n",
" qubit_state_binary += binary_state\n",
"\n",
" offset += group_length\n",
"\n",
" return qubit_state_binary"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### How `fock_to_qubit_state` Works\n",
"\n",
"1. **Check** the total length needed: sum of $(2^{\\text{group\\_size}})$\n",
"2. **Slice** each group's chunk out of the big `fock_state`\n",
"3. Within that chunk, ensure exactly **one** entry is `1`\n",
"4. The **index** of that `1` is the integer representation of the bits for that group\n",
" - Convert that index to a binary string of width `n`\n",
"5. Append all these binary substrings together, forming the **full** qubit bitstring\n",
"\n",
"#### Example \n",
"- `fock_state = [1,0,0,0, 0,0,0,1]` (length=8)\n",
"- `group_sizes = [2,2]`\n",
"\n",
"**Step by Step**:\n",
"\n",
"- Group A chunk: `[1,0,0,0]` → exactly one '1' at index=0 → binary of `0` with width=2 → `\"00\"`\n",
"- Group B chunk: `[0,0,0,1]` → index=3 → binary= `\"11\"`\n",
"\n",
"Concatenate = `\"00\" + \"11\"` = `\"0011\"`\n",
"\n",
"### Edge Cases\n",
"\n",
"1. **Invalid Fock State**\n",
" If a chunk has more than one `1` or none at all, `fock_to_qubit_state` returns `None`.\n",
" This ensures we only accept states with exactly **one** photon per group.\n",
" Thus, we have post-selected\n",
"\n",
"2. **Bitstring Offsets**\n",
" The variable `offset` keeps track of how many bits we've already consumed from `qubit_state`.\n",
" This ensures we map each portion of the bitstring to its corresponding qudit group.\n",
"\n",
"---\n",
"\n",
"By using the helper function:\n",
"\n",
"- `fock_to_qubit_state`: you can interpret the circuit's **measurement results** (a one-hot outcome) back into a **bitstring** for classical post-processing or optimization"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## VII. QUBO Example with a Simple QLOQ ansatz Circuit (CVaR-VQE Approach)\n",
"\n",
"In this section of the notebook, we demonstrate how to tackle a **QUBO** (Quadratic Unconstrained Binary Optimization) problem using a **CVar-VQE** style approach in Perceval. This can also be verified with the other more photonic QUBO approach which uses the same QUBO matrix. We’ll show:\n",
"\n",
"1. **Building a simple QLOQ circuit** (e.g., with Ry layers) to produce a variational ansatz.\n",
"2. **Sampling** from the circuit to get a distribution of bitstrings.\n",
"3. **Computing CVaR** to measure the cost (objective function) and performing classical optimization (COBYLA).\n",
"4. **Identifying the best bitstring** based on the final solution.\n",
"5. **Optional**: Plotting the final probability distribution as a histogram for visual insight."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### What is CVaR-VQE?\n",
"\n",
"**CVaR-VQE** (Conditional Value-at-Risk Variational Quantum Eigensolver) is a hybrid quantum-classical optimization technique that **goes beyond** the usual average-cost minimization seen in standard VQE:\n",
"\n",
"1. **VQE Recap**\n",
" - In a typical VQE, you prepare a **parametrized quantum circuit** (ansatz).\n",
" - You **sample** from it to estimate the **average** energy (or cost).\n",
" - A **classical optimizer** tunes the circuit parameters to **minimize** this average cost.\n",
"\n",
"2. **Why CVaR?**\n",
" - In many problems, you don’t just care about the **average** cost. You also want to avoid **worst-case** outcomes.\n",
" - **CVaR** (Conditional Value-at-Risk) focuses on the **worst $\\alpha$-fraction** of possible outcomes in your distribution.\n",
" - Practically, we *sort* outcomes by cost and *average* the top $\\alpha$ portion (highest costs). If $\\alpha = 0.5$, that means we look at the top 50% of the distribution by cost. By **minimizing** that portion, we make sure the algorithm consistently avoids very high-cost states.\n",
"\n",
"3. **Combining CVaR with VQE**\n",
" - We still build a **variational circuit** and measure its outputs, but instead of updating parameters to reduce the simple average cost, we **focus on the worst tail**.\n",
" - This ensures the final circuit is **less likely** to produce very bad solutions.\n",
"\n",
"In short, **CVaR-VQE** aims to push the distribution of measured bitstrings toward reliably low-cost outcomes, rather than just optimizing the mean. This can be extremely useful for **QUBO** (Quadratic Unconstrained Binary Optimization) problems, where you want to avoid sampling high-cost bitstrings even occasionally.\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"def compute_cvar(probabilities: List[float], values: List[float], alpha: float) -> float:\n",
" \"\"\"\n",
" Compute the Conditional Value at Risk (CVaR).\n",
" Given a list of probabilities and corresponding values (costs),\n",
" we take the worst alpha portion of outcomes and average them\n",
" weighted by their probabilities.\n",
" \"\"\"\n",
" sorted_indices = np.argsort(values) # sort by ascending value\n",
" probs = np.array(probabilities)[sorted_indices]\n",
" vals = np.array(values)[sorted_indices]\n",
" cvar = 0\n",
" total_prob = 0\n",
"\n",
" for p, v in zip(probs, vals):\n",
" if p >= alpha - total_prob:\n",
" p = alpha - total_prob\n",
" total_prob += p\n",
" cvar += p * v\n",
"\n",
" return cvar / total_prob\n",
"\n",
"def expectation_value(vec_state: np.ndarray, qubo_matrix: np.ndarray) -> float:\n",
" \"\"\"\n",
" Compute the expectation value for a given state with respect to a QUBO matrix.\n",
" Here, vec_state is a binary vector (e.g., [0,1,0,1]) converted to float,\n",
" and qubo_matrix is the NxN matrix of the QUBO.\n",
" \"\"\"\n",
" return np.dot(vec_state.conjugate(), np.dot(qubo_matrix, vec_state))\n",
"\n",
"def extract_probability_distribution(job_results: Dict, group_sizes: List[int]) -> Tuple[Dict[str, float], int]:\n",
" \"\"\"\n",
" Extract probability distribution from sampling results.\n",
" Returns:\n",
" output_dict = {bitstring: probability}\n",
" sum_valid_outputs = sum of all valid counts (used for normalization)\n",
" \"\"\"\n",
" output_dict = {}\n",
" sum_valid_outputs = 0\n",
"\n",
" # First pass: count how many valid outputs (bitstrings)\n",
" for res in job_results['results']:\n",
" qb_state = fock_to_qubit_state(res, group_sizes)\n",
" if qb_state:\n",
" sum_valid_outputs += job_results['results'][res]\n",
" output_dict[qb_state] = job_results['results'][res]\n",
" \n",
" divisor = sum_valid_outputs\n",
" output_dict = {k: v/divisor for k, v in output_dict.items()}\n",
" #compute probabilities by dividing by sum\n",
"\n",
" return output_dict"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"def build_circuit(phases: List[float], group_sizes: List[int], layers: List[str]) -> pcvl.Experiment:\n",
" \"\"\"\n",
" Build the quantum circuit (QLOQ ansatz) with specified phases.\n",
" \n",
" \"\"\"\n",
" ansatz = catalog[\"qloq ansatz\"]\n",
" group_sizes_p = [Encoding.DUAL_RAIL if x == 1 else eval(f\"Encoding.QUDIT{x}\") for x in group_sizes]\n",
" return ansatz.build_experiment(\n",
" group_sizes=group_sizes_p,\n",
" layers=layers,\n",
" phases=phases,\n",
" ctype=\"cz\" #can be cx too\n",
" )"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"def create_objective_function(computer: pcvl.AComputer,\n",
" qubo_matrix: np.ndarray,\n",
" input_state: str,\n",
" group_sizes: List[int],\n",
" layers: List[str],\n",
" sampling_size: int,\n",
" alpha: float,\n",
" verbose):\n",
" \"\"\"\n",
" Create the CVaR-VQE objective function for optimization.\n",
" - computer: The computer on which to compute. Must be acquired externally\n",
" - qubo_matrix: the QUBO cost matrix\n",
" - input_state: initial computational basis string (e.g. \"000000\")\n",
" - group_sizes: list of integers, each representing # of qubits in that group\n",
" - layers: e.g. [\"Y\"] or [\"Y\",\"X\"]\n",
" - sampling_size: how many shots to gather per evaluation\n",
" - alpha: the fraction for CVaR computation\n",
"\n",
" Returns:\n",
" objective_function (callable): to be passed into an optimizer\n",
" best_result (list reference): to track the best (lowest) loss + best bitstring\n",
" \"\"\"\n",
" best_result = [None] # store (loss, bitstring)\n",
" iteration = [0] # track iteration count\n",
"\n",
" def objective_function(phases: np.ndarray) -> float:\n",
" experiment = build_circuit(phases.tolist(), group_sizes, layers)\n",
" experiment.with_input(LogicalState(input_state))\n",
"\n",
" factory = pcvl.ExecutionFactory(computer, experiment, max_shots_per_call=sampling_size)\n",
" execution_results = factory.sample_count(sampling_size)\n",
"\n",
" output_dict = extract_probability_distribution(execution_results, group_sizes)\n",
" if not output_dict:\n",
" return float('inf')\n",
"\n",
" probabilities = list(output_dict.values())\n",
" values = [expectation_value(np.array(list(state)).astype(int), qubo_matrix)\n",
" for state in output_dict.keys()]\n",
" loss = compute_cvar(probabilities, values, alpha)\n",
"\n",
" bitstring = max(output_dict, key=output_dict.get)\n",
" if best_result[0] is None or loss < best_result[0][0]:\n",
" best_result[0] = (loss, bitstring)\n",
"\n",
" iteration[0] += 1\n",
" if verbose:\n",
" print(f\"Iteration {iteration[0]}: Loss = {loss}, Best bitstring = {bitstring}\")\n",
" return loss\n",
"\n",
" return objective_function, best_result"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"def optimize_qubo(computer: pcvl.AComputer,\n",
" qubo_matrix: np.ndarray,\n",
" input_state: str,\n",
" group_sizes: List[int],\n",
" layers: List[str],\n",
" sampling_size: int = 100000,\n",
" alpha: float = 0.5,\n",
" maxiter: int = 50,\n",
" verbose=False) -> Tuple[float, str, np.ndarray]:\n",
" \"\"\"\n",
" Run the optimization using COBYLA to find phases that minimize CVaR for the given QUBO problem.\n",
" - computer: The computer on which to compute. Must be acquired externally\n",
" - qubo_matrix: your QUBO cost matrix\n",
" - input_state: initial bitstring\n",
" - group_sizes: e.g. [2,2,2]\n",
" - layers: e.g. [\"Y\"]\n",
" - sampling_size: how many shots per circuit evaluation\n",
" - alpha: fraction for CVaR\n",
" - maxiter: max COBYLA iterations\n",
"\n",
" Returns:\n",
" final_loss (float): best CVaR found\n",
" best_bitstring (str): best measured bitstring\n",
" optimal_phases (np.ndarray): the phase vector that achieved the best result\n",
" \"\"\"\n",
" ansatz = catalog[\"qloq ansatz\"]\n",
" \n",
" group_sizes_p = [Encoding.DUAL_RAIL if x == 1 else eval(f\"Encoding.QUDIT{x}\") for x in group_sizes]\n",
" nb_phases = ansatz.get_parameter_nb(group_sizes_p, len(layers))\n",
"\n",
" # Random initial guess for the phases\n",
" initial_phases = np.random.uniform(0, 2*np.pi, nb_phases)\n",
"\n",
" # Build the objective function\n",
" objective_function, best_result = create_objective_function(\n",
" computer, qubo_matrix, input_state, group_sizes, layers, sampling_size, alpha, verbose=verbose)\n",
"\n",
" # Minimize\n",
" result = minimize(\n",
" objective_function,\n",
" initial_phases,\n",
" method='cobyla',\n",
" options={\n",
" 'maxiter': maxiter,\n",
" }\n",
" )\n",
"\n",
" best_loss = result.fun\n",
" best_bitstring = best_result[0][1]\n",
" return best_loss, best_bitstring, result.x"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"def plot_bitstring_distribution(prob_dict: Dict[str, float], top_n: int = 10, title: str = \"Top Bitstring Probabilities\"):\n",
" \"\"\"\n",
" Plot a histogram of the top N most probable bitstrings.\n",
"\n",
" Args:\n",
" prob_dict: e.g. {\"000000\": 0.25, \"100100\": 0.15, ...}\n",
" top_n: how many top states to display\n",
" title: plot title\n",
" \"\"\"\n",
" # Convert dict to list of (bitstring, probability) pairs\n",
" items = list(prob_dict.items())\n",
"\n",
" # Sort descending by probability\n",
" items.sort(key=lambda x: x[1], reverse=True)\n",
"\n",
" # Take the top 'top_n' states\n",
" items = items[:top_n]\n",
"\n",
" # Unzip into two lists\n",
" bitstrings, probs = zip(*items)\n",
"\n",
" plt.figure(figsize=(8, 4))\n",
" plt.bar(bitstrings, probs, color='darkviolet')\n",
" plt.ylabel(\"Probability\")\n",
" plt.xlabel(\"Bitstring\")\n",
" plt.title(title)\n",
" plt.xticks(rotation=45, ha='right')\n",
" plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## VIII. Putting It All Together\n",
"\n",
"Below is a complete **example** usage, showing how to:\n",
"\n",
"1. Define a sample QUBO matrix (6-qubit problem).\n",
"2. Optimize using Ry layers in a QLOQ circuit.\n",
"3. Print the final result and best bitstring.\n",
"4. Optionally, sample the final circuit once more to plot the output distribution."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"=== Final Optimization Results ===\n",
"Final CVaR Loss: -28.0\n",
"Best Bitstring: 100100\n",
"Optimal Phases: [7.02757093 5.65959003 1.33693864 2.40314907 4.3244576 6.1949159\n",
" 0.8919302 2.4423774 0.78646718 6.65559397 5.87891948 5.02957491\n",
" 1.91064987 4.89706456 1.35410499 0.45822653 1.54761616 0.48820523\n",
" 6.21136091 3.65019813 2.69685407 3.3368383 0.11816978 5.72547821]\n"
]
},
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAArwAAAGrCAYAAAAigUT9AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjksIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvJkbTWQAAAAlwSFlzAAAPYQAAD2EBqD+naQAAZg9JREFUeJzt3Xl8TNf/P/DXJJFFJJGIrFVBEGuCSOxrKlFaawVtET58a4+gRVVsbewNtXW1daEtuqhGK4RWYylFEbGLLRFbNiSSef/+8MutkYQkIpO583o+HvNg7py5c87MvXdeOXPuuRoRERARERERqZSJvitARERERPQ8MfASERERkaox8BIRERGRqjHwEhEREZGqMfASERERkaox8BIRERGRqjHwEhEREZGqMfASERERkaox8BIRERGRqjHwEhm4CxcuQKPRYPXq1c/1dTw8PDBo0KASX29p1R8AVq9eDY1GgwsXLijLPDw80LVr1+f+2gAQExMDjUaDmJiYUnm94kpPT8f//vc/uLi4QKPRIDQ0VN9VKlXTp0+HRqMp0XUOGjQIHh4eJbrOsvy6RGUNAy9RGZcb0vK7TZo0Sd/Vy+PR+pmZmcHBwQFNmjTB2LFjceLEiRJ7neXLl5dKSC6Oslw3APDz84NGo8GKFSvyffyDDz7A6tWrMXz4cKxbtw5vvvkm/vrrL0yfPh137twp3coC2LNnD3r06AFnZ2dYWFjAw8MD//d//4eEhIRir/Pu3buYPn16mf/jozCuXr2K6dOn4/Dhw/quClGZpRER0XcliKhgq1evRkhICGbOnIlq1arpPFa/fn14e3sjMzMT5cqVg6mp6XOrh4eHB9q1a/fUIKfRaPDSSy9hwIABEBGkpKTgyJEj+O6775CRkYG5c+ciLCxMKS8ixap//fr14ejoWKTAkpOTgwcPHsDCwkLpwfPw8ED9+vWxZcuWQq+nuHXTarXIysqCubk5TEz0099w+vRp1KpVCx4eHnB3d8eff/6Zp0yzZs1gZmam89iCBQswceJEnD9/vlR7DD/66COMHTsW1atXx6BBg+Dq6oq4uDh89tlnAICtW7eiRYsWRV7vjRs3ULlyZYSHh2P69Ok6j2VnZyM7OxuWlpYl0QQAwIMHD6DVamFhYVFi68z1999/o2nTpli1alWeX2Ge5+sSGRIzfVeAiAqnc+fO8PX1zfexkvxiLgm1atXCG2+8obNszpw5eOWVVzB+/Hh4eXnh5ZdfBvAwID/v+mdkZMDa2hqmpqbP9Y+CpzExMdH7Z/Xll1/CyckJCxcuRO/evXHhwoU8Afb69euoW7duqdTn7t27KF++fL6P7dmzB6GhoWjVqhWioqJ0yg0fPhwtW7ZE7969cfz4cdjb25dYnczMzGBmVrJfj+XKlSvR9ZX11yUqc4SIyrRVq1YJADlw4EC+j58/f14AyKpVq5RlAwcOFGtra7l8+bJ069ZNrK2txdHRUcaPHy/Z2dk6z58/f740b95cHBwcxNLSUho3bizfffddntepWrWqDBw48Kn1BSAjR47M97GLFy+KmZmZtGjR4on1v3btmgwaNEjc3d3F3NxcXFxc5NVXX5Xz588rdQGgc2vbtq3O+xUTEyPDhw+XypUrS8WKFXUey11P7rq6dOki27ZtE29vb7GwsJA6derIxo0bdeoeHh4u+R0yH1/nk+q2c+dOASA7d+7UWce3334rjRs3FktLS6lUqZK8/vrrcvnyZZ0yRflMn8TT01NGjBghmZmZUrFiRXn//feVx3Lr9/ht4MCB+S5/9H1ct26d0gZ7e3sJDg6WhIQEnddu27at1KtXT/7++29p3bq1WFlZydixYwusa2BgoJiamsq5c+fyfXzNmjUCQCIiIvK8T2fPnpVOnTpJ+fLlxdXVVWbMmCFarVZE/tvmHr+Fh4eLSP6fde52/e2330qdOnXE0tJSmjVrJkePHhURkZUrV0qNGjXEwsJC2rZtq/Pe5NaratWqOu9FfnV4dF+4efOmjB8/XurXry/W1tZiY2MjQUFBcvjw4ad+ZrnrePx1RUTS09MlLCxMXnjhBTE3N5datWrJ/Pnzlffn8TZv3rxZ6tWrJ+bm5lK3bl359ddfC/zMiMoq9vASGYiUlBTcuHFDZ5mjo2OB5XNychAYGAh/f38sWLAA27dvx8KFC1GjRg0MHz5cKbd48WK8+uqreP3115GVlYX169fjtddew5YtW9ClS5cSbcOLL76Itm3bYufOnUhNTYWtrW2+5Xr16oXjx49j9OjR8PDwwPXr1/H7778jISEBHh4eiIyMxOjRo1GhQgW8++67AABnZ2eddYwYMQKVK1fGtGnTkJGR8cR6nT59GsHBwXjrrbcwcOBArFq1Cq+99hqioqLw0ksvFamNhanbo3KHrDRt2hQRERFISkrC4sWLsWfPHvzzzz+oWLGiUrawn2lB9u3bhzNnzmDVqlUwNzdHz5498dVXX2HKlCkAgDp16mDdunUYN24cXnjhBYwfPx4A0KBBA2RlZeGbb77Bhx9+qGx3lStXBgC8//77eO+999CnTx/873//Q3JyMj766CO0adMmTxtu3ryJzp07o2/fvnjjjTcKfG/u3r2L6OhotG7dOs9QnlzBwcEYNmwYtmzZojOePScnB0FBQWjWrBnmzZuHqKgohIeHIzs7GzNnzkTlypWxYsUKDB8+HD169EDPnj0BAA0bNnzi+/fHH3/gp59+wsiRIwEAERER6Nq1K95++20sX74cI0aMwO3btzFv3jwMHjwYO3bsKHBd7777Lv73v//pLPvyyy+xbds2ODk5AQDOnTuHH374Aa+99hqqVauGpKQkfPzxx2jbti1OnDgBNzc31KlTBzNnzsS0adMwbNgwtG7dGgAKHOYhInj11Vexc+dODBkyBD4+Pti2bRsmTpyIK1eu4MMPP9Qp/+eff2LTpk0YMWIEbGxssGTJEvTq1QsJCQmoVKnSE98vojJF34mbiJ4stwcxv5tIwT28AGTmzJk662rUqJE0adJEZ9ndu3d17mdlZUn9+vWlQ4cOOstLoodXRGTs2LECQI4cOZJv/W/fvi0AZP78+U98nXr16ik9p4/Kfb9atWqVp+ezoB5eADo9uikpKeLq6iqNGjVSlhW2h/dJdXu8hzcrK0ucnJykfv36cu/ePaXcli1bBIBMmzZNWVaUz7Qgo0aNkipVqig9eb/99psAkH/++UenXG6v96Pmz5+fp50iIhcuXBBTU1OdnmIRkX///VfMzMx0luf2aq5cufKpdT18+LAAeGIPsIhIw4YNxcHBQbmf+z6NHj1aWabVaqVLly5ibm4uycnJIiKSnJys06v7qIJ6eC0sLHTa//HHHwsAcXFxkdTUVGX55MmT87xX+fW0PmrPnj1Srlw5GTx4sLLs/v37kpOTo1Pu/PnzYmFhobMdHDhwIM8xoKDX/eGHHwSAzJ49W6dc7969RaPRyJkzZ3TabG5urrPsyJEjAkA++uijAttCVBZxlgYiA7Fs2TL8/vvvOreneeutt3Tut27dGufOndNZZmVlpfz/9u3bSElJQevWrXHo0KGSqfhjKlSoAABIS0vL93ErKyuYm5sjJiYGt2/fLvbrDB06tNDjdd3c3NCjRw/lvq2tLQYMGIB//vkHiYmJxa7D0/z999+4fv06RowYoTO2t0uXLvDy8sIvv/yS5zmF+Uzzk52djQ0bNiA4OFg5Ya9Dhw5wcnLCV199Vew2bNq0CVqtFn369MGNGzeUm4uLC2rWrImdO3fqlLewsEBISMhT15u7fdjY2DyxnI2NDVJTU/MsHzVqlPJ/jUaDUaNGISsrC9u3by9Ms/LVsWNHnfHO/v7+AB7+IvFoPXOXF+ZzAYDExET07t0bPj4+WL58ubLcwsJCObkxJycHN2/eRIUKFVC7du1i759bt26FqakpxowZo7N8/PjxEBH8+uuvOssDAgJQo0YN5X7Dhg1ha2tb6LYRlRUc0kBkIPz8/Ao8aS0/lpaWyk/Ouezt7fOEyC1btmD27Nk4fPgwMjMzleUlPQ9prvT0dAAFBxkLCwvMnTsX48ePh7OzM5o1a4auXbtiwIABcHFxKfTrFPQzeH48PT3ztLdWrVoAHs4TXJTXLYqLFy8CAGrXrp3nMS8vrzwzKBT2M83Pb7/9huTkZPj5+eHMmTPK8vbt2+Obb77B3LlzizVzxOnTpyEiqFmzZr6PP37SlLu7O8zNzZ+63tzto6A/jHKlpaXl2ZZMTExQvXp1nWWPfp7F9eKLL+rct7OzAwBUqVIl3+WF+Vyys7PRp08f5OTkYNOmTTqzKWi1WixevBjLly/H+fPnkZOTozxW3OEEFy9ehJubW573rE6dOsrjj3q8zUDhtzmisoSBl0ilCtO7+ccff+DVV19FmzZtsHz5cri6uqJcuXJYtWoVvv766+dSr2PHjsHU1PSJgTQ0NBSvvPIKfvjhB2zbtg3vvfceIiIisGPHDjRq1KhQr/Noz3VJKOgPgEdDyPP2LDNM5Pbi9unTJ9/Hd+3ahfbt2xd5vVqtFhqNBr/++mu+9cvt0c9V2M/F09MTZmZmOHr0aIFlMjMzER8fX6Q/BJ9FQe9/QculELN+Tpw4EbGxsdi+fTteeOEFncc++OADvPfeexg8eDBmzZoFBwcHmJiYIDQ0FFqttugNKIZnaRtRWcLAS2TENm7cCEtLS2zbtk2nZ2nVqlXP5fUSEhKwa9cuNG/e/Kk/VdeoUQPjx4/H+PHjcfr0afj4+GDhwoX48ssvAZRsD/SZM2cgIjrrPHXqFAAoP2HnTnt1584dnZOwHu8RK0rdqlatCgCIj49Hhw4ddB6Lj49XHn9WGRkZ+PHHHxEcHIzevXvneXzMmDH46quvnhh4C2pTjRo1ICKoVq2a0otaEqytrdG+fXvs2LEDFy9ezPe9+Pbbb5GZmZnnSnlarRbnzp3Tqc/jn+fz+gWjKNavX4/IyEhERkaibdu2eR7//vvv0b59e3z++ec6y+/cuaNzwmpR2lK1alVs3749T8/4yZMnlceJ1IhjeImMmKmpKTQajU4v5YULF/DDDz+U+GvdunUL/fr1Q05OjjJ7QX7u3r2L+/fv6yyrUaMGbGxsdIZcWFtbl9hVv65evYrNmzcr91NTU7F27Vr4+PgowxlyxzHu3r1bKZeRkYE1a9bkWV9h6+br6wsnJyesXLlSp22//vor4uLiSmyWjM2bNyMjIwMjR45E796989y6du2KjRs36tThcdbW1gCQp109e/aEqakpZsyYkafXT0Rw8+bNYtd76tSpEBEMGjQI9+7d03ns/PnzePvtt+Hq6or/+7//y/PcpUuX6tRj6dKlKFeuHDp27AgAypy++rhyHPDwl47//e9/eOONNzB27Nh8y5iamuZ5T7/77jtcuXJFZ1lBn01+Xn75ZeTk5Oi8PwDw4YcfQqPRoHPnzkVoBZHhYA8vkRHr0qULFi1ahKCgIPTv3x/Xr1/HsmXL4Onp+cSfkp/m1KlT+PLLLyEiSE1NVa60lp6errzek57bsWNH9OnTB3Xr1oWZmRk2b96MpKQk9O3bVynXpEkTrFixArNnz4anpyecnJzy9JIWVq1atTBkyBAcOHAAzs7O+OKLL5CUlKTT092pUye8+OKLGDJkCCZOnAhTU1N88cUXqFy5cp5L3Ba2buXKlcPcuXMREhKCtm3bol+/fsq0ZB4eHhg3blyx2vO4r776CpUqVSpwqqpXX30Vn376KX755Rdliq7HNWnSBMDD6bT69u2LcuXK4ZVXXkGNGjUwe/ZsTJ48GRcuXED37t1hY2OD8+fPY/PmzRg2bBgmTJhQrHq3adMGCxYsQFhYGBo2bKhcae3kyZP49NNPodVqsXXr1jwXnbC0tERUVBQGDhwIf39//Prrr/jll18wZcoUZQy0lZUV6tatiw0bNqBWrVpwcHBA/fr1Ub9+/WLVtahyT9xr06aN8qtFrhYtWqB69ero2rUrZs6ciZCQELRo0QL//vsvvvrqqzzjk2vUqIGKFSti5cqVsLGxgbW1Nfz9/fMdNvTKK6+gffv2ePfdd3HhwgV4e3vjt99+w48//ojQ0FCdE9SIVEU/k0MQUWE9y4UnHpffdEuff/651KxZUywsLMTLy0tWrVqVb7miTEuWezMxMZGKFStKo0aNZOzYsXL8+PGn1v/GjRsycuRI8fLyEmtra7GzsxN/f3/59ttvdZ6XmJgoXbp0ERsbm3wvPJHf+/W0C080bNhQeR/yu/jGwYMHxd/fX8zNzeXFF1+URYsW5bvOgupW0IUnNmzYII0aNRILCwtxcHB44oUnHlfQdGm5kpKSxMzMTN58880Cy9y9e1fKly8vPXr00HlPHjdr1ixxd3cXExOTPG3euHGjtGrVSqytrcXa2lq8vLxk5MiREh8fr5TJvfBEUe3evVu6desmjo6OUq5cOXnxxRdl6NChcuHChTxl87vwhLOzs4SHh+eZ4uuvv/6SJk2aiLm5eaEvPPGo3G338Sn0cj/nR7ehx6cHy+8CJbm33H3h/v37Mn78eHF1dRUrKytp2bKlxMbGStu2bfNMe/fjjz9K3bp1xczM7KkXnkhLS5Nx48aJm5ublCtXTmrWrPnEC088rrDHAqKyRCPCkedERKQOgwYNwvfff6/MBkJEBHAMLxERERGpHAMvEREREakaAy8RERERqRrH8BIRERGRqpWJHt5ly5bBw8MDlpaW8Pf3x/79+wssu2nTJvj6+qJixYqwtraGj48P1q1bp1Nm0KBB0Gg0OrcnTYNEREREROql93l4N2zYgLCwMKxcuRL+/v6IjIxEYGAg4uPj4eTklKe8g4MD3n33XXh5ecHc3BxbtmxBSEgInJycEBgYqJQLCgrSmUPz0atIEREREZHx0PuQBn9/fzRt2lS56otWq0WVKlUwevRoTJo0qVDraNy4Mbp06YJZs2YBeNjDe+fOnWJfLUqr1eLq1auwsbEpE5efJCIiIiJdIoK0tDS4ubnBxOTJgxb02sOblZWFgwcPYvLkycoyExMTBAQEIDY29qnPFxHs2LED8fHxmDt3rs5jMTExcHJygr29PTp06IDZs2ejUqVK+a4nMzNT55KaV65cQd26dYvZKiIiIiIqLZcuXcILL7zwxDJ6Dbw3btxATk4OnJ2ddZY7Ozvj5MmTBT4vJSUF7u7uyMzMhKmpKZYvX46XXnpJeTwoKAg9e/ZEtWrVcPbsWUyZMgWdO3dGbGwsTE1N86wvIiICM2bMyLP80qVLsLW1fYYWEhEREdHzkJqaiipVqsDGxuapZfU+hrc4bGxscPjwYaSnpyM6OhphYWGoXr062rVrBwDo27evUrZBgwZo2LAhatSogZiYGHTs2DHP+iZPnoywsDDlfu4baGtry8BLREREVIYVZvipXgOvo6MjTE1NkZSUpLM8KSkJLi4uBT7PxMQEnp6eAAAfHx/ExcUhIiJCCbyPq169OhwdHXHmzJl8A6+FhQVPaiMiIiJSKb1OS2Zubo4mTZogOjpaWabVahEdHY3mzZsXej1arVZnDO7jLl++jJs3b8LV1fWZ6ktEREREhkfvQxrCwsIwcOBA+Pr6ws/PD5GRkcjIyEBISAgAYMCAAXB3d0dERASAh+NtfX19UaNGDWRmZmLr1q1Yt24dVqxYAQBIT0/HjBkz0KtXL7i4uODs2bN4++234enpqTNtGREREREZB70H3uDgYCQnJ2PatGlITEyEj48PoqKilBPZEhISdKaayMjIwIgRI3D58mVYWVnBy8sLX375JYKDgwEApqamOHr0KNasWYM7d+7Azc0NnTp1wqxZszhsgYiIiMgI6X0e3rIoNTUVdnZ2SElJ4UlrRERERGVQUfJambi0MBERERHR88LAS0RERESqxsBLRERERKrGwEtEREREqsbAS0RERESqxsBLRERERKrGwEtEREREqqb3C0/QQ8s0x/RdhWIZKfX1XQUiIiKiJ2IPLxERERGpGgMvEREREakaAy8RERERqRoDLxERERGpGgMvEREREakaAy8RERERqRoDLxERERGpGgMvEREREakaAy8RERERqRoDLxERERGpGgMvEREREakaAy8RERERqRoDLxERERGpGgMvEREREakaAy8RERERqRoDLxERERGpGgMvEREREakaAy8RERERqRoDLxERERGpGgMvEREREakaAy8RERERqRoDLxERERGpGgMvEREREakaAy8RERERqRoDLxERERGpWpkIvMuWLYOHhwcsLS3h7++P/fv3F1h206ZN8PX1RcWKFWFtbQ0fHx+sW7dOp4yIYNq0aXB1dYWVlRUCAgJw+vTp590MIiIiIiqD9B54N2zYgLCwMISHh+PQoUPw9vZGYGAgrl+/nm95BwcHvPvuu4iNjcXRo0cREhKCkJAQbNu2TSkzb948LFmyBCtXrsS+fftgbW2NwMBA3L9/v7SaRURERERlhEZERJ8V8Pf3R9OmTbF06VIAgFarRZUqVTB69GhMmjSpUOto3LgxunTpglmzZkFE4ObmhvHjx2PChAkAgJSUFDg7O2P16tXo27fvU9eXmpoKOzs7pKSkwNbWtviNK4JlmmOl8jolbaTU13cViIiIyAgVJa/ptYc3KysLBw8eREBAgLLMxMQEAQEBiI2NferzRQTR0dGIj49HmzZtAADnz59HYmKizjrt7Ozg7+9f4DozMzORmpqqcyMiIiIiddBr4L1x4wZycnLg7Oyss9zZ2RmJiYkFPi8lJQUVKlSAubk5unTpgo8++ggvvfQSACjPK8o6IyIiYGdnp9yqVKnyLM0iIiIiojJE72N4i8PGxgaHDx/GgQMH8P777yMsLAwxMTHFXt/kyZORkpKi3C5dulRylSUiIiIivTLT54s7OjrC1NQUSUlJOsuTkpLg4uJS4PNMTEzg6ekJAPDx8UFcXBwiIiLQrl075XlJSUlwdXXVWaePj0++67OwsICFhcUztoaIiIiIyiK99vCam5ujSZMmiI6OVpZptVpER0ejefPmhV6PVqtFZmYmAKBatWpwcXHRWWdqair27dtXpHUSERERkTrotYcXAMLCwjBw4ED4+vrCz88PkZGRyMjIQEhICABgwIABcHd3R0REBICH4219fX1Ro0YNZGZmYuvWrVi3bh1WrFgBANBoNAgNDcXs2bNRs2ZNVKtWDe+99x7c3NzQvXt3fTWTiIiIiPRE74E3ODgYycnJmDZtGhITE+Hj44OoqCjlpLOEhASYmPzXEZ2RkYERI0bg8uXLsLKygpeXF7788ksEBwcrZd5++21kZGRg2LBhuHPnDlq1aoWoqChYWlqWevuIiIiISL/0Pg9vWcR5eAuP8/ASERGRPhjMPLxERERERM8bAy8RERERqRoDLxERERGpmt5PWiPjwrHKREREVNrYw0tEREREqsbAS0RERESqxsBLRERERKrGwEtEREREqsbAS0RERESqxsBLRERERKrGwEtEREREqsbAS0RERESqxsBLRERERKrGwEtEREREqsbAS0RERESqxsBLRERERKrGwEtEREREqsbAS0RERESqxsBLRERERKrGwEtEREREqsbAS0RERESqxsBLRERERKrGwEtEREREqsbAS0RERESqxsBLRERERKrGwEtEREREqsbAS0RERESqxsBLRERERKrGwEtEREREqsbAS0RERESqxsBLRERERKrGwEtEREREqsbAS0RERESqViYC77Jly+Dh4QFLS0v4+/tj//79BZb99NNP0bp1a9jb28Pe3h4BAQF5yg8aNAgajUbnFhQU9LybQURERERlkN4D74YNGxAWFobw8HAcOnQI3t7eCAwMxPXr1/MtHxMTg379+mHnzp2IjY1FlSpV0KlTJ1y5ckWnXFBQEK5du6bcvvnmm9JoDhERERGVMXoPvIsWLcLQoUMREhKCunXrYuXKlShfvjy++OKLfMt/9dVXGDFiBHx8fODl5YXPPvsMWq0W0dHROuUsLCzg4uKi3Ozt7UujOURERERUxug18GZlZeHgwYMICAhQlpmYmCAgIACxsbGFWsfdu3fx4MEDODg46CyPiYmBk5MTateujeHDh+PmzZsFriMzMxOpqak6NyIiIiJSB70G3hs3biAnJwfOzs46y52dnZGYmFiodbzzzjtwc3PTCc1BQUFYu3YtoqOjMXfuXOzatQudO3dGTk5OvuuIiIiAnZ2dcqtSpUrxG0VEREREZYqZvivwLObMmYP169cjJiYGlpaWyvK+ffsq/2/QoAEaNmyIGjVqICYmBh07dsyznsmTJyMsLEy5n5qaytBLREREpBJ67eF1dHSEqakpkpKSdJYnJSXBxcXlic9dsGAB5syZg99++w0NGzZ8Ytnq1avD0dERZ86cyfdxCwsL2Nra6tyIiIiISB30GnjNzc3RpEkTnRPOck9Aa968eYHPmzdvHmbNmoWoqCj4+vo+9XUuX76MmzdvwtXVtUTqTURERESGQ++zNISFheHTTz/FmjVrEBcXh+HDhyMjIwMhISEAgAEDBmDy5MlK+blz5+K9997DF198AQ8PDyQmJiIxMRHp6ekAgPT0dEycOBF79+7FhQsXEB0djW7dusHT0xOBgYF6aSMRERER6Y/ex/AGBwcjOTkZ06ZNQ2JiInx8fBAVFaWcyJaQkAATk/9y+YoVK5CVlYXevXvrrCc8PBzTp0+Hqakpjh49ijVr1uDOnTtwc3NDp06dMGvWLFhYWJRq24iIiIhI/zQiIvquRFmTmpoKOzs7pKSklNp43mWaY6XyOiVtpNQvUnljaScRERE9X0XJa3of0kBERERE9Dwx8BIRERGRqjHwEhEREZGqMfASERERkaox8BIRERGRqjHwEhEREZGqMfASERERkaox8BIRERGRqjHwEhEREZGqMfASERERkaox8BIRERGRqjHwEhEREZGqMfASERERkaox8BIRERGRqjHwEhEREZGqMfASERERkaox8BIRERGRqjHwEhEREZGqMfASERERkaox8BIRERGRqjHwEhEREZGqMfASERERkaox8BIRERGRqjHwEhEREZGqFSvw7ty5s6TrQURERET0XBQr8AYFBaFGjRqYPXs2Ll26VNJ1IiIiIiIqMcUKvFeuXMGoUaPw/fffo3r16ggMDMS3336LrKyskq4fEREREdEzKVbgdXR0xLhx43D48GHs27cPtWrVwogRI+Dm5oYxY8bgyJEjJV1PIiIiIqJieeaT1ho3bozJkydj1KhRSE9PxxdffIEmTZqgdevWOH78eEnUkYiIiIio2IodeB88eIDvv/8eL7/8MqpWrYpt27Zh6dKlSEpKwpkzZ1C1alW89tprJVlXIiIiIqIiMyvOk0aPHo1vvvkGIoI333wT8+bNQ/369ZXHra2tsWDBAri5uZVYRYmIiIiIiqNYgffEiRP46KOP0LNnT1hYWORbxtHRkdOXEREREZHeFWtIQ3h4OF577bU8YTc7Oxu7d+8GAJiZmaFt27bPXkMiIiIiomdQrMDbvn173Lp1K8/ylJQUtG/fvsjrW7ZsGTw8PGBpaQl/f3/s37+/wLKffvopWrduDXt7e9jb2yMgICBPeRHBtGnT4OrqCisrKwQEBOD06dNFrhcRERERGb5iBV4RgUajybP85s2bsLa2LtK6NmzYgLCwMISHh+PQoUPw9vZGYGAgrl+/nm/5mJgY9OvXDzt37kRsbCyqVKmCTp064cqVK0qZefPmYcmSJVi5ciX27dsHa2trBAYG4v79+0VrKBEREREZPI2ISGEL9+zZEwDw448/IigoSGdIQ05ODo4ePYratWsjKiqq0BXw9/dH06ZNsXTpUgCAVqtFlSpVMHr0aEyaNOmpz8/JyYG9vT2WLl2KAQMGQETg5uaG8ePHY8KECQAe9jw7Oztj9erV6Nu371PXmZqaCjs7O6SkpMDW1rbQbXkWyzTHSuV1StpIqf/0Qo8wlnYSERHR81WUvFakHl47OzvY2dlBRGBjY6Pct7Ozg4uLC4YN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Hzt2LKKiorBw4UKcPHkS06dPx99//41Ro0YBANLT0zFx4kTs3bsXFy5cQHR0NLp16wZPT08EBgbqpY1EREREpD96H8MbHByM5ORkTJs2DYmJifDx8UFUVJRyYlpCQgJMTP7L5S1atMDXX3+NqVOnYsqUKahZsyZ++OEH1K9fHwBgamqKo0ePYs2aNbhz5w7c3NzQqVMnzJo1CxYWFnppIxERERHpj0ZERN+VKGtSU1NhZ2eHlJSUUhvPu0xz7OmFyqCRUr9I5Y2hncbQRsB42klERGVTUfKa3oc0EBERERE9Twy8RERERKRqDLxEREREpGoMvERERESkagy8RERERKRqDLxEREREpGoMvERERESkagy8RERERKRqDLxEREREpGoMvERERESkagy8RERERKRqDLxEREREpGpm+q4AEVFZtUxzTN9VKJaRUr9I5Y2lnURkvNjDS0RERESqxsBLRERERKrGwEtEREREqsYxvEREpHocp0xk3NjDS0RERESqxsBLRERERKrGwEtEREREqsbAS0RERESqxsBLRERERKrGwEtEREREqsbAS0RERESqxsBLRERERKrGC08QERGpBC+wQZQ/9vASERERkaox8BIRERGRqjHwEhEREZGqMfASERERkarxpDUiIiIyGDwxj4qDPbxEREREpGrs4SUiIiIqY9iTXbLKRA/vsmXL4OHhAUtLS/j7+2P//v1PLP/dd9/By8sLlpaWaNCgAbZu3arzuIhg2rRpcHV1hZWVFQICAnD69Onn2QQiIiIiKqP0Hng3bNiAsLAwhIeH49ChQ/D29kZgYCCuX7+eb/m//voL/fr1w5AhQ/DPP/+ge/fu6N69O44d++8voXnz5mHJkiVYuXIl9u3bB2trawQGBuL+/ful1SwiIiIiKiP0HngXLVqEoUOHIiQkBHXr1sXKlStRvnx5fPHFF/mWX7x4MYKCgjBx4kTUqVMHs2bNQuPGjbF06VIAD3t3IyMjMXXqVHTr1g0NGzbE2rVrcfXqVfzwww+l2DIiIiIiKgv0OoY3KysLBw8exOTJk5VlJiYmCAgIQGxsbL7PiY2NRVhYmM6ywMBAJcyeP38eiYmJCAgIUB63s7ODv78/YmNj0bdv3zzrzMzMRGZmpnI/JSUFAJCamlrsthXVPaSX2muVpKK+R8bQTmNoI2Ac7TSGNgLG0U5jaCNg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",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"computer = pcvl.SimulatedComputer(\"SLOS\")\n",
"\n",
"# Example QUBO matrix (6-qubit), taken from the other QUBO notebook\n",
"H1 = np.array([\n",
" [2., 32., -32., -32., 32., 0.],\n",
" [0., 1., 32., 0., -32., -32.],\n",
" [0., 0., 35., 32., -64., -32.],\n",
" [0., 0., 0., 2., -32., 32.],\n",
" [0., 0., 0., 0., 35., 32.],\n",
" [0., 0., 0., 0., 0., 4.]\n",
"])\n",
"\n",
"group_sizes = [3, 3]\n",
"layers = [\"Y\"]\n",
"\n",
"with computer.acquire():\n",
" final_loss, best_bitstring, optimal_phases = optimize_qubo(\n",
" computer=computer,\n",
" qubo_matrix=H1,\n",
" input_state=\"000000\",\n",
" group_sizes=group_sizes,\n",
" layers=layers,\n",
" sampling_size=10_000_000,\n",
" alpha=0.25,\n",
" maxiter=600,\n",
" verbose=False\n",
" )\n",
"\n",
" print(\"\\n=== Final Optimization Results ===\")\n",
" print(f\"Final CVaR Loss: {final_loss}\")\n",
" print(f\"Best Bitstring: {best_bitstring}\")\n",
" print(f\"Optimal Phases: {optimal_phases}\")\n",
"\n",
" experiment = build_circuit(list(optimal_phases), group_sizes, layers)\n",
" experiment.with_input(LogicalState(\"000000\"))\n",
" factory = pcvl.ExecutionFactory(computer, experiment)\n",
" execution_results = factory.sample_count(10_000_000)\n",
"\n",
"output_dict = extract_probability_distribution(execution_results, group_sizes)\n",
"plot_bitstring_distribution(output_dict, title=\"Final Distribution After Optimization\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Explanation\n",
"\n",
"1. `optimize_qubo` calls our **objective function** repeatedly, adjusting phases to reduce the **CVaR** of the QUBO cost.\n",
"2. `alpha` in **CVaR** picks how much “worst tail” of outcomes we average over. $\\alpha = 0.5$ is a middle ground.\n",
"3. `best_bitstring` is chosen from the final distribution’s highest-probability outcome (in practice, you might also check the distribution).\n",
"4. The **plot** helps visualize which bitstrings are being sampled at the end of optimization.\n",
"\n",
"---\n",
"\n",
"With this approach, you have a working **CVaR-VQE** routine for **QUBO** problems using an ansatz in the **QLOQ** framework. You can expand this by:\n",
"- Increasing the number of **layers** (e.g., `[\"Y\",\"X\"]`).\n",
"- Using **CX** gates instead of CZ inside groups (`ctype=\"cx\"`).\n",
"- Changing **group_sizes** or QUBO matrix to match your real problem.\n",
"- Exploring advanced optimization methods or different cost functions beyond QUBO.\n",
"\n",
"This completes our demonstration of a **QUBO** problem solved by a **CVar-VQE**-like approach in **Perceval’s QLOQ** environment. QLOQ can be applied to various problems involving a variational circuit so it is recommended to refactor this example to your needs whether by changing the loss function to match your given problem or the circuit structure itself."
]
}
],
"metadata": {
"language_info": {
"name": "python"
}
},
"nbformat": 4,
"nbformat_minor": 1
}