From f6ef30e6248e0456c5de1f5b5c3acf141f60b7f6 Mon Sep 17 00:00:00 2001 From: sannesg Date: Wed, 26 Aug 2026 11:05:08 +0200 Subject: [PATCH 1/2] added updated validation.py --- qaoa/utils/validation.py | 45 +++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/qaoa/utils/validation.py b/qaoa/utils/validation.py index f8ac5b1..cd1bc5b 100644 --- a/qaoa/utils/validation.py +++ b/qaoa/utils/validation.py @@ -9,17 +9,29 @@ def _bitstring(i, n, flip=False): else: return format(i, f'0{n}b') +def phase_cost(problem, bitstring, global_phase, omit_invalid_states, flip): + idx = int(bitstring[::-1] if flip else bitstring, 2) + try: + energy = problem.objective_value(bitstring) + energies[idx] = energy + except: + energies[idx] = global_phase + if omit_invalid_states: + mask[idx] = False def check_phase_separator_exact_qaoa(qaoa, *arg, **kwarg): return check_phase_separator_exact_problem(qaoa.problem, *arg, **kwarg) -def check_phase_separator_exact_problem(problem, t=1, flip=True, atol=1e-8, rtol=1e-8): +def check_phase_separator_exact_problem(problem, t=1, flip=True, atol=1e-7, rtol=1e-7, global_phase = 0.0, omit_invalid_states = False): """ - Exact check that the problem's circuit represents the problem's energy function. + Exact check that the problem's circuit represents the problem's cost function. This tests checks that the unitary operator represented by the quantum circuit is equal to the expected matrix with diagonal elements - exp(-j*t*energy(e)), + exp(-j*t*cost(e)), where e is the corresponding binary state, up to a global phase. + + Works for anzats' that remain in a feasible subspace. If the anzats' can reach infeasible spaces, + the problem must define a suitable penalty for such solutions. Suitable for <= 10 qubits as this check uses the full unitary matrix of size 2^n x 2^n). Returns: (ok: bool, report: dict) @@ -30,31 +42,33 @@ def check_phase_separator_exact_problem(problem, t=1, flip=True, atol=1e-8, rtol {problem.circuit.parameters[0]: t}, inplace = False ) - energy_fn = problem.energy U = Operator(circ).data # complex ndarray + diag = np.diag(U) n = circ.num_qubits d = 2**n + # Compare diagonal phases to expected, modulo a global phase # expected diag entries - energies = [] + global energies, mask + + energies = [0]*len(diag) + mask = np.ones(len(diag), dtype=bool) + for i in range(d): - energies.append(energy_fn(_bitstring(i, n, flip=flip))) + phase_cost(problem, _bitstring(i, n, flip=flip), global_phase, omit_invalid_states, flip=flip) + expected = np.exp(-1j * t * np.asarray(energies, dtype=float)) - - diag = np.diag(U) - # if n < 4: - # for i in range(d): - # print(expected[i]* diag[0], diag[i]) # Remove global phase by aligning first nonzero expected ref_idx = 0 g = diag[ref_idx] / expected[ref_idx] # global phase factor ratios = diag / (expected * g) # Errors - mag_err = np.max(np.abs(np.abs(diag) - 1.0)) - phase_err = np.max(np.abs(np.angle(ratios))) # max residual phase after removing global + mag_err = np.max(np.abs(np.abs(diag[mask]) - 1.0)) + phase_err = np.max(np.abs(np.angle(ratios[mask]))) + ok = (mag_err <= rtol) and (phase_err <= atol) report = { @@ -63,6 +77,8 @@ def check_phase_separator_exact_problem(problem, t=1, flip=True, atol=1e-8, rtol "max_phase_error_rad_after_global": float(phase_err), "global_phase_rad": float(np.angle(g)), } + + if not ok: # include a few worst offenders idx_sorted = np.argsort(-np.abs(np.angle(ratios))) @@ -71,9 +87,10 @@ def check_phase_separator_exact_problem(problem, t=1, flip=True, atol=1e-8, rtol bad.append({ "bitstring": list(_bitstring(k, n, flip=flip)), "diag_entry": complex(diag[k]), + "raw expected": complex(expected[k]), "expected": complex(expected[k]*g), "phase_residual_rad": float(np.angle(ratios[k])), "magnitude": float(np.abs(diag[k])), }) report["examples"] = bad - return ok, report + return ok, report \ No newline at end of file From 708817a7bce9d32a29bee637402788e664926734 Mon Sep 17 00:00:00 2001 From: Sanne Selmer Gilje Date: Thu, 27 Aug 2026 14:48:16 +0200 Subject: [PATCH 2/2] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- qaoa/utils/validation.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/qaoa/utils/validation.py b/qaoa/utils/validation.py index cd1bc5b..a252067 100644 --- a/qaoa/utils/validation.py +++ b/qaoa/utils/validation.py @@ -11,13 +11,11 @@ def _bitstring(i, n, flip=False): def phase_cost(problem, bitstring, global_phase, omit_invalid_states, flip): idx = int(bitstring[::-1] if flip else bitstring, 2) - try: - energy = problem.objective_value(bitstring) - energies[idx] = energy - except: + if omit_invalid_states and not problem.isFeasible(bitstring): energies[idx] = global_phase - if omit_invalid_states: - mask[idx] = False + mask[idx] = False + return + energies[idx] = problem.energy(bitstring) def check_phase_separator_exact_qaoa(qaoa, *arg, **kwarg): return check_phase_separator_exact_problem(qaoa.problem, *arg, **kwarg)