Skip to content

feat(xpress): add direct io#596

Open
CharlieFModo wants to merge 13 commits intoPyPSA:masterfrom
CharlieFModo:feat/xpress/write-api
Open

feat(xpress): add direct io#596
CharlieFModo wants to merge 13 commits intoPyPSA:masterfrom
CharlieFModo:feat/xpress/write-api

Conversation

@CharlieFModo
Copy link
Contributor

Closes # (if applicable).

Changes proposed in this Pull Request

  • Adds xpress direct solver IO
  • To ensure xpress direct IO is faster than lp-polars this PR removes pandas from the hot-path of matrix operations required to get problem coefficients to write to xpress
    • This seems to have large performance gains throughout the package beyond what I have tested for this PR
    • I would like some guidance on whether these changes are ok in the broader context of the package. I've only tested them on the problems that I am interested in solving and found this gives a c. 10x improvement from the lp-polars IO

Checklist

  • Code changes are sufficiently documented; i.e. new functions contain docstrings and further explanations may be given in doc.
  • Unit tests for new features were added (if applicable).
  • A note for the release notes doc/release_notes.rst of the upcoming release is included.
  • I consent to the release of this PR's code under the MIT license.

perf(xpress): faster and logging

perf(xpress): int32 index support

perf: numpy matrix building

perf: numpy filter missing

perf: cache data

perf: raw numpy array ops for other components

refactor: clean up
@coroa
Copy link
Member

coroa commented Mar 1, 2026

Hi @CharlieFModo,

thanks, i can see quite some performance impacts (ie. up to 30% improvement for a typical highs direct io model i am sometimes using), with your changes here.

So, i'd like very much if we were able to include it, but we will have to be very careful about the memory of these changes.

  1. We had memory spikes in earlier versions, which is why we introduced the whole iterate_slices of the constraints objects.
  2. You are putting quite a lot of data into these variable_data and constraint_data cached properties .

I don't understand the impact of these changes to well yet, but the speed-up gain is very promising.

@coroa
Copy link
Member

coroa commented Mar 1, 2026

We probably will need to restructure the package a bit as more and more alternative io paths are crowding into io.py. Maybe some of it should go into solvers or we start an io directory with specific sub modules.

@CharlieFModo
Copy link
Contributor Author

Thanks @coroa - what would you like the scope of this PR to be to include the changes? I'd prefer to avoid refactoring the package if possible and just focus on adding the xpress IO and these performance changes I needed to make to make it a large enough improvement from lp-polars.

I'm hopeful these changes should be pretty respectful of memory because the parts of code that I've refactored work with the raw ndarray data which I guess should already be in memory as part of the model. It avoid loading them into pandas dataframes, which are really awful for consuming memory. Not 100% though because I haven't profiled it but I can have a look at adding iterate_slices style logic if you think that would be better. Can you suggest a benchmark that would indicate if this is relevant? One of the pypsa models?

dependabot bot and others added 11 commits March 12, 2026 12:32
Bumps the github-actions group with 3 updates: [actions/download-artifact](https://github.com/actions/download-artifact), [actions/upload-artifact](https://github.com/actions/upload-artifact) and [crazy-max/ghaction-chocolatey](https://github.com/crazy-max/ghaction-chocolatey).


Updates `actions/download-artifact` from 7 to 8
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](actions/download-artifact@v7...v8)

Updates `actions/upload-artifact` from 6 to 7
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](actions/upload-artifact@v6...v7)

Updates `crazy-max/ghaction-chocolatey` from 3 to 4
- [Release notes](https://github.com/crazy-max/ghaction-chocolatey/releases)
- [Commits](crazy-max/ghaction-chocolatey@v3...v4)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '8'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/upload-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: crazy-max/ghaction-chocolatey
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* code: expose knitro context and modify _extract_values

* doc: update release_notes.rst

* code: include pre-commit checks
* enable quadratic for win with scip

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Add release note

* Drop reference to SCIP bug

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Fabian Hofmann <fab.hof@gmx.de>
* Refactor piecewise constraints: add piecewise/segments/slopes_to_points API, LP formulation for convex/concave cases, and simplify tests

* piecewise: replace bp_dim/seg_dim params with constants, remove dead code, improve errors

* Fix piecewise linear constraints: add binary indicators to incremental formulation, add domain bounds to LP formulation

- Incremental method now uses binary indicator variables with link/order constraints to enforce proper segment filling order (Markowitz & Manne)
- LP method now adds x ∈ [min(xᵢ), max(xᵢ)] domain bound constraints to prevent extrapolation beyond breakpoints

* update signatures of breakpoints and segments, apply convexity check only where needed

* update doc

* Reject interior NaN and skip_nan_check+NaN in piecewise formulations

Validate trailing-NaN-only for SOS2 and disjunctive methods to prevent
corrupted adjacency. Fail fast when skip_nan_check=True but breakpoints
actually contain NaN.

* Allow piecewise() on either side of comparison operators

Support reversed syntax (y == piecewise(...)) via __le__/__ge__/__eq__
dispatch in BaseExpression and ScalarLinearExpression. Fix LP example
to use power == demand for more illustrative results.

* Fix mypy type errors for piecewise constraint types

- Add @overload to comparison operators (__le__, __ge__, __eq__) in
  BaseExpression and Variable to distinguish PiecewiseExpression from
  SideLike return types
- Update ConstraintLike type alias to include PiecewiseConstraintDescriptor
- Fix PiecewiseConstraintDescriptor.lhs type from object to LinExprLike
- Fix dict/sequence type mismatches in _dict_to_array, _dict_segments_to_array,
  _segments_list_to_array
- Remove unused type: ignore comments
- Narrow ScalarLinearExpression/ScalarVariable return types to not include
  PiecewiseConstraintDescriptor (impossible at runtime)

* rename header of jupyter notebook

* doc: rename notebook again

* feat: add active parameter to piecewise linear constraints (PyPSA#604)

* feat: add `active` parameter to piecewise linear constraints

Add an `active` parameter to the `piecewise()` function that accepts a
binary variable to gate piecewise linear functions on/off. This enables
unit commitment formulations where a commitment binary controls the
operating range.

The parameter modifies each formulation method as follows:
- Incremental: δ_i ≤ active (tightened bounds) + base terms × active
- SOS2: Σλ_i = active (instead of 1)
- Disjunctive: Σz_k = active (instead of 1)

When active=0, all auxiliary variables are forced to zero, collapsing
x and y to zero. When active=1, the normal PWL domain is active.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: tighten active parameter docstrings

Clarify that zero-forcing is the only linear formulation possible —
relaxing the constraint would require big-M or indicator constraints.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: add active parameter to release notes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: resolve mypy type errors for x_base/y_base assignment

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: add unit commitment example to piecewise notebook

Example 6 demonstrates the active parameter with a gas unit that
stays off at t=1 (low demand) and commits at t=2,3 (high demand),
showing power=0 and fuel=0 when the commitment binary is off.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Update notebook

* test: comprehensive active parameter test coverage

Add tests for gaps identified in review:
- Inequality + active (incremental and SOS2, on and off)
- auto method selection + active (equality and auto-LP rejection)
- active with LinearExpression (not just Variable)
- active with NaN-masked breakpoints
- LP file output comparison (active vs plain)
- Multi-dimensional solver test (per-entity on/off)
- SOS2 non-zero base + active off
- SOS2 inequality + active off
- Disjunctive active on (solver)
- Fix: reject active when auto resolves to LP

159 tests pass (was 122).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: extract PWL_ACTIVE_BOUND_SUFFIX constant

Move the active bound constraint name suffix to constants.py,
consistent with all other PWL suffix constants.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test: remove redundant active parameter tests

Keep only tests that exercise unique code paths or verify distinct
mathematical properties.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: FBumann <117816358+FBumann@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…sets and supersets (PyPSA#572)

* refac: introduce consistent convention for linopy operations with subsets and supersets

* move scalar addition to add_constant

* add overwriting logic to add constant

* add join parameter to control alignment in operations

* Add le, ge, eq methods with join parameter for constraints

Add le(), ge(), eq() methods to LinearExpression and Variable classes,
mirroring the pattern of add/sub/mul/div methods. These methods support
the join parameter for flexible coordinate alignment when creating constraints.

* Extract constant alignment logic into _align_constant helper

Consolidate repetitive alignment handling in _add_constant and
_apply_constant_op into a single _align_constant method. This
eliminates code duplication and makes the alignment behavior
(handling join parameter, fill_value, size-aware defaults) testable
and maintainable in one place.

* update notebooks

* update release notes

* fix types

* add regression test

* fix numpy array dim mismatch in constraints and add RHS dim tests

numpy_to_dataarray no longer inflates ndim beyond arr.ndim, fixing
lower-dim numpy arrays as constraint RHS. Also reject higher-dim
constant arrays (numpy/pandas) consistently with DataArray behavior.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* remove pandas reindexing warning

* Fix mypy errors: type ignores for xr.align/merge, match override signature, add test type hints

* remove outdated warning tests

* reintroduce expansions of extra rhs dims, fix multiindex alignment

* refactor test fixtures and use sign constants

* add tests for pandas series subset/superset

* test: add TestMissingValues for same-shape constants with NaN entries

* Fix broken test imports, stray docstring char, and incorrect test assertion from fixture refactor

* Fill NaN with neutral elements in expression arithmetic, preserve NaN as 'no constraint' in RHS

- Fill NaN with 0 (add/sub) or fill_value (mul/div) in _add_constant/_apply_constant_op
- Fill NaN coefficients with 0 in Variable.to_linexpr
- Restore NaN mask in to_constraint() so subset RHS still signals unconstrained positions

* Fix CI doctest collection by deferring linopy import in test/conftest.py

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Strengthen masked IIS regression test

* Fix Xpress IIS mapping for masked constraints

* Fix typing in masked IIS regression test
…PSA#601)

* handle missing dual values when barrier solution has no crossover

* Add release notes

---------

Co-authored-by: Fabian Hofmann <fab.hof@gmx.de>
* Add semi-continous variables as an option

* Run the pre-commit

* Fix mypy issues

* Add release notes note

* Fabian feedback

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Missing to_culpdx

---------

Co-authored-by: Fabian Hofmann <fab.hof@gmx.de>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants