Closed
Migrate QAOA to explicit objective-sense + canonical energy semantics#58
Conversation
Copilot
AI
changed the title
[WIP] Implement repository-wide objective direction migration for QAOA
Migrate QAOA to explicit objective-sense + canonical energy semantics
Jul 31, 2026
Contributor
There was a problem hiding this comment.
Pull request overview
This PR migrates QAOA’s sign conventions to an explicit objective_sense model where QAOA always minimizes canonical energy(x), while exposing natural objective_value(x) and retaining legacy APIs (e.g., deprecated cost() / get_Exp() aliases).
Changes:
- Introduces
ObjectiveSenseand standardizesobjective_value/energyconversion at theProblemlevel. - Updates QAOA optimization/selection/statistics to be energy-first (including CVaR becoming lower-tail).
- Extends serialization and docs to persist and explain objective-vs-energy semantics.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| unittests/test_statistic_utility.py | Updates CVaR expectation to lower-tail semantics. |
| unittests/test_objective_sense_migration.py | Adds comprehensive tests for objective/energy invariants, validation, and serialization schema versioning. |
| README.md | Documents explicit objective direction and canonical energy sign convention; updates example outputs. |
| qaoa/utils/validation.py | Updates exact phase-separator validation to check exp(-i * t * energy(x)). |
| qaoa/utils/statistic.py | Changes CVaR implementation to lower-tail aggregation and guards small-sample rounding. |
| qaoa/utils/qaoaIO.py | Adds objective_sense fields, best_energy/objective to results, and schema_version handling. |
| qaoa/utils/post.py | Switches post-processing to record/aggregate energy CVaR directly. |
| qaoa/utils/plotroutines.py | Moves approximation ratio computation to objective values and adds objective-sense-aware normalization. |
| qaoa/utils/flip.py | Updates bit-flip “boosting” to reduce energy (minimization) rather than increase cost. |
| qaoa/qaoa.py | Refactors optimization history/results and landscape sampling to be energy-centric; adds get_energy()/get_objective() with get_Exp() deprecated. |
| qaoa/problems/qubo_problem.py | Makes QUBO expose natural objective; applies sign only through energy mapping/circuit construction; updates validation reporting. |
| qaoa/problems/portfolio_problem.py | Switches portfolio problem to natural minimization objective and energy-based brute-force selection. |
| qaoa/problems/maxkcut_one_hot_problem.py | Declares MAXIMIZE objective sense and renames cost to natural objective. |
| qaoa/problems/graph_problem.py | Declares MAXIMIZE objective sense and renames cost to natural objective. |
| qaoa/problems/exactcover_problem.py | Switches exact cover to natural minimization objective and energy-based brute-force selection. |
| qaoa/problems/base_problem.py | Adds ObjectiveSense, objective_value/energy/bounds helpers, and deprecates legacy cost APIs. |
| qaoa/problems/init.py | Re-exports ObjectiveSense from the problems package. |
Suppressed comments (2)
qaoa/qaoa.py:391
get_objective()has the same depth handling issue asget_energy()(falsydepthand allowingdepth == current_depth + 1), which can lead to unexpected list-return behavior orKeyError. Usedepth is Noneand validate1 <= depth <= current_depth.
if not depth:
ret = []
for i in range(1, self.current_depth + 1):
ret.append(self.optimization_results[i].get_best_objective())
return ret
qaoa/qaoa.py:785
- Same issue as in
sample_cost_landscape():MaxCost_sampled_p1/MinCost_sampled_p1should be-MinEnergy_sampled_p1/-MaxEnergy_sampled_p1to preserve the deprecatedcost(x) == -energy(x)alias semantics.
self.MaxCost_sampled_p1 = self.MinEnergy_sampled_p1
self.MinCost_sampled_p1 = self.MaxEnergy_sampled_p1
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+377
to
+384
| if not depth: | ||
| ret = [] | ||
| for i in range(1, self.current_depth + 1): | ||
| ret.append(self.optimization_results[i].get_best_energy()) | ||
| return ret | ||
| if depth > self.current_depth + 1: | ||
| raise ValueError | ||
| return self.optimization_results[depth].get_best_energy() |
Comment on lines
+674
to
+675
| self.MaxCost_sampled_p1 = self.MinEnergy_sampled_p1 | ||
| self.MinCost_sampled_p1 = self.MaxEnergy_sampled_p1 |
Comment on lines
201
to
205
| for string in hist: | ||
| if qaoa_instance.problem.isFeasible(string): | ||
| cost = qaoa_instance.problem.cost(string) | ||
| cost = qaoa_instance.problem.objective_value(string) | ||
| counts += hist[string] | ||
| stat.add_sample(cost, hist[string], string) |
Comment on lines
137
to
140
| if self.cvar < 1: | ||
| cvarK = int(np.round(self.cvar * len(self.all_values))) | ||
| cvar = np.sum(self.all_values[-cvarK:]) / cvarK | ||
| cvarK = max(1, int(np.round(self.cvar * len(self.all_values)))) | ||
| cvar = np.sum(self.all_values[:cvarK]) / cvarK | ||
| return cvar |
Comment on lines
206
to
210
| 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*cost(e)), | ||
| equal to the expected matrix with diagonal elements | ||
| exp(-j*t*energy(e)), | ||
| where e is the corresponding binary state, up to a global phase. |
Comment on lines
91
to
95
| class PortfolioOptimizationProblemData(ProblemData): | ||
| risk: float = 0.0 | ||
| exp_returns: np.ndarray = None | ||
| exp_return: np.ndarray = None | ||
| cov_matrix: np.ndarray = None | ||
| budget: int = 0 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This change migrates the repository from implicit sign conventions to an explicit
Problem-level objective direction model. QAOA internals now consistently minimize canonical energy, while preserving legacy APIs through deprecations and compatibility aliases.Problem API: explicit objective direction (single source of truth)
ObjectiveSense(MINIMIZE/MAXIMIZE) inbase_problem.py.objective_senseobjective_value(bitstring)(natural mathematical objective)energy(bitstring)(canonical minimized quantity)cost()as deprecated compatibility API with legacy score behavior (cost == -energy).objective_bounds(),optimal_objective(),energy_bounds().computeMinMaxCosts()as deprecated wrapper.Built-in problems: natural objectives + centralized sign conversion
MAXIMIZE.MINIMIZE, supports explicitobjective_sense, and applies sign only through energy mapping/circuit construction.QAOA internals: energy-first optimization and result APIs
get_energy()get_objective()get_Exp()as deprecated alias for compatibility.Energy_sampled_p1,MinEnergy_sampled_p1,MaxEnergy_sampled_p1) while retaining legacy aliases.CVaR/statistics/utilities aligned to minimization semantics
Statistic.get_CVaR()now computes lower-tail CVaR.flip.py,post.py, and plotting/post-processing paths were updated to evaluate/compare via energy or explicit objective APIs.Phase-separator validation and sign checks
exp(-1j * t * energy(x))Serialization schema + metadata
qaoaIOmodels with schema/versioned payload updates.Docs + migration guidance
objective_valuevsenergy).