-
Notifications
You must be signed in to change notification settings - Fork 67
feat: Quadratic constraints #523
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
* Add highs validation for quadratic constraints * Multi-dimensional quadratic constraints - Fixed LP file and Gurobi direct API export to handle constraints with coordinates
* Add highs validation for quadratic constraints * Multi-dimensional quadratic constraints - Fixed LP file and Gurobi direct API export to handle constraints with coordinates * Add quadratic constraints to netcdf serialization * add test_netcdf_roundtrip_multidimensional
* Add highs validation for quadratic constraints * Multi-dimensional quadratic constraints - Fixed LP file and Gurobi direct API export to handle constraints with coordinates * Add quadratic constraints to netcdf serialization * add test_netcdf_roundtrip_multidimensional * Added the following properties to MatrixAccessor in matrices.py: | Property | Description | |-----------|---------------------------------------------------------| | qclabels | Vector of labels of all quadratic constraints | | qc_sense | Vector of senses (<=, >=, =) | | qc_rhs | Vector of right-hand-side values | | Qc | List of sparse Q matrices (one per constraint) | | qc_linear | Sparse matrix of linear coefficients (n_qcons × n_vars) | The Q matrices follow the convention x'Qx where: - Diagonal terms are doubled (for x²) - Off-diagonal terms are symmetric (for xy) * Fix imports
* Add highs validation for quadratic constraints * Multi-dimensional quadratic constraints - Fixed LP file and Gurobi direct API export to handle constraints with coordinates * Add quadratic constraints to netcdf serialization * add test_netcdf_roundtrip_multidimensional * Added the following properties to MatrixAccessor in matrices.py: | Property | Description | |-----------|---------------------------------------------------------| | qclabels | Vector of labels of all quadratic constraints | | qc_sense | Vector of senses (<=, >=, =) | | qc_rhs | Vector of right-hand-side values | | Qc | List of sparse Q matrices (one per constraint) | | qc_linear | Sparse matrix of linear coefficients (n_qcons × n_vars) | The Q matrices follow the convention x'Qx where: - Diagonal terms are doubled (for x²) - Off-diagonal terms are symmetric (for xy) * Fix imports * Add dual support
* Add highs validation for quadratic constraints * Multi-dimensional quadratic constraints - Fixed LP file and Gurobi direct API export to handle constraints with coordinates * Add quadratic constraints to netcdf serialization * add test_netcdf_roundtrip_multidimensional * Added the following properties to MatrixAccessor in matrices.py: | Property | Description | |-----------|---------------------------------------------------------| | qclabels | Vector of labels of all quadratic constraints | | qc_sense | Vector of senses (<=, >=, =) | | qc_rhs | Vector of right-hand-side values | | Qc | List of sparse Q matrices (one per constraint) | | qc_linear | Sparse matrix of linear coefficients (n_qcons × n_vars) | The Q matrices follow the convention x'Qx where: - Diagonal terms are doubled (for x²) - Off-diagonal terms are symmetric (for xy) * Fix imports * Add dual support * Add Add mosek direct api * qc_linear property now handles the case when there are no linear terms in the quadratic constraints (the vars column doesn't exist in that case) * Remove separate quad checks * Add tests for verification of solver
* Add highs validation for quadratic constraints
* Multi-dimensional quadratic constraints - Fixed LP file and Gurobi direct API export to handle constraints with coordinates
* Add quadratic constraints to netcdf serialization
* add test_netcdf_roundtrip_multidimensional
* Added the following properties to MatrixAccessor in matrices.py:
| Property | Description |
|-----------|---------------------------------------------------------|
| qclabels | Vector of labels of all quadratic constraints |
| qc_sense | Vector of senses (<=, >=, =) |
| qc_rhs | Vector of right-hand-side values |
| Qc | List of sparse Q matrices (one per constraint) |
| qc_linear | Sparse matrix of linear coefficients (n_qcons × n_vars) |
The Q matrices follow the convention x'Qx where:
- Diagonal terms are doubled (for x²)
- Off-diagonal terms are symmetric (for xy)
* Fix imports
* Add dual support
* Add Add mosek direct api
* qc_linear property now handles the case when there are no linear terms in the quadratic constraints (the vars column doesn't exist in
that case)
* 1. generatevarnames: Changed np.arange(0, len(labels)) to np.arange(0, len(labels), dtype=np.int32) - MOSEK expects 32-bit integers for indices
2. putarowslice: Added explicit type casts:
- indptr → np.int64 (pointer arrays)
- indices → np.int32 (column indices)
- data → np.float64 (values)
- <= constraints: Changed bl=0.0 to bl=-np.inf (inactive bound should be -∞) - >= constraints: Changed bu=0.0 to bu=np.inf (inactive bound should be +∞)
* Add defensive validation to new quadratic constraint code: 1. linopy/matrices.py:185-238 - Uniqueness check for QC labels Added seen_labels tracking in both qc_sense and qc_rhs properties with assertions to detect duplicate quadratic constraint labels across different constraint objects. 2. linopy/matrices.py:241-305 - Document Q-matrix assembly assumptions Extended the docstring for Qc property to document the assumption that flat_qcons stores each quadratic term exactly once, and the symmetrization logic applied. 3. linopy/matrices.py:307-357 - Debug assertions for label consistency Added a skipped_rows counter in qc_linear with an assertion that fails if any linear terms are skipped due to missing variable or constraint labels. 4. linopy/constants.py:203-213 - Expose qc_dual in Result repr Updated Result.__repr__ to show the count of qc_duals when present (e.g., "Solution: 2 primals, 1 duals, 3 qc_duals"). 5. examples/quadratic-constraints.ipynb - Strip notebook outputs Cleared all execution outputs and reset execution counts to None for all code cells. 6. linopy/io.py:462-470 - Guard for missing label metadata Added an explicit check with a clear ValueError if a label from the flat representation is not found in the constraint metadata. Also fixed: - linopy/solvers.py:1213 - Removed the double assignment typo (solution = solution = ...). * Add more tests * Add section headers to lp file * Adjust tests
for more information, see https://pre-commit.ci
* Add guard against mps export with quadratic constraints * Add mps export support with gurobi * Add roundtrip tests
…ict) type guards at lines 498-499 and 946-947
…ne and assert direct_obj is not None after all objective.value assignments to handle the Optional type
1. linopy/expressions.py:1827 - Added missing from linopy import Model import to the doctest example in QuadraticExpression.to_constraint
|
wonderful initiative! let me know when I should take a first look |
|
@FabianHofmann Its ready for an initial review. I would be happy if you give me some quick feedback on what functionality is missing (if some), so i wont get lost in details. I will revisit the new tests tomorrow. |
|
From my analysis the QuadraticConstraint class is missing several wrapped xarray methods that Constraint has:
These are definitely a todo imo |
1. inequalities property - Returns a filtered QuadraticConstraints containing only inequality constraints (<= or >=) 2. equalities property - Returns a filtered QuadraticConstraints containing only equality constraints (=) 3. sanitize_zeros() method - Filters out terms with zero and close-to-zero coefficients from both quadratic and linear parts of constraints 4. sanitize_missings() method - Sets constraint labels to -1 where all variables in both quadratic and linear parts are missing (-1) 5. print_labels() method - Prints formatted representations of quadratic constraints by their labels, similar to the linear constraints version Added helper function (linopy/common.py): 6. print_single_quadratic_constraint() - Formats a single quadratic constraint for display, handling both squared terms (x²) and cross-product terms (x·y) Added tests (test/test_quadratic_constraint.py): - TestQuadraticConstraintsContainerMethods class with 9 tests covering all new functionality These additions bring QuadraticConstraints closer to feature parity with the linear Constraints container.
…ations in test/test_quadratic_constraint.py: Implemented QuadraticExpressionRolling and QuadraticExpressionGroupby
…d QuadraticExpression - both exclude internal dimensions (_term for linear, _term and _factor for quadratic)
for more information, see https://pre-commit.ci
resolved |
|
@FabianHofmann My PR includes a "fix" regarding the |
… LinearExpression 2. QuadraticExpression now defines its own versions without overriding anything from BaseExpression 3. No more Liskov substitution principle violations 4. No # type: ignore hacks needed
Closes #444
Changes proposed in this Pull Request
This PR adds comprehensive quadratic constraints support to linopy, enabling optimization problems of the form x'Qx + a'x ≤ b.
New Features
Core quadratic constraint infrastructure:
Solver support:
File I/O:
Matrix representations:
Additional:
Before Merging:
Remove dev-scripts
The dev-scripts/ directory contains planning documents that were committed but should be removed before merging (per CLAUDE.md, dev-scripts is for non-tracked
temporary code). Would you like me to create a commit removing those files?
Review and documentation
As this is quite a big feature, i would be happy if someone that understands the codebase more deeply than i do goes into detail and verifies my code, especially the matrix computation. Also, including examples and explanations into the docs would be appropriate
Solver capabilities
Im not entirely sure if SCIP supports MIQCQP. In my tests i got an LP read error.
Dual values can only be retrived for convex models. For gurobi, we need to set
QCPDual=1. Other solvers might need other settings. this is not set automatically.Follow Ups
Checklist
doc.doc/release_notes.rstof the upcoming release is included.