Skip to content

Add model diagnostics module #645

Open

Description

@FBumann

Describe the feature you'd like to see

Add a model.diagnostics module/accessor that provides insights into model structure, quality, and performance characteristics. This module would be experimental — the API may change between releases as we learn what's most useful. Users will be warned accordingly.

Motivation

Users often build large models and have limited visibility into why solves are slow or produce unexpected results. A diagnostics module gives them tools to inspect and understand their models without needing deep solver expertise.

Ideas and use cases

Below is a non-exhaustive list of possible diagnostics. Not all need to be implemented at once — the module can grow over time.

Pre-solve (no solve needed, pure matrix/model analysis)

Diagnostic Use case
Scaling report — coefficient range ratios per row/column, flagging problematic ratios Identify numerical issues that slow down solvers or cause inaccurate results
Big-M detection — find constraints where binary variables appear with very large coefficients Spot big-M formulations that weaken LP relaxation and slow branch-and-bound
RHS/coefficient mismatch — flag constraints where RHS is on a very different scale than coefficients Catch common modeling mistakes that hurt solver performance
Model summary/profile — variable/constraint counts by type, sparsity, density of constraint matrix Quick overview to sanity-check model size and structure
Coefficient histogram/distribution — distribution of coefficient magnitudes across the model Understand overall scaling characteristics at a glance
Duplicate/near-duplicate constraint detection — find rows of A that are identical or nearly identical Catch redundant constraints that bloat the model
Empty/trivial constraint detection — constraints with no variables or that are trivially satisfied Catch modeling bugs early
Variable bound analysis — variables with very wide or very tight bounds, or missing bounds Spot variables that may need tighter bounds for performance

Post-solve (requires a solve)

Diagnostic Use case
Constraint activity — classify constraints as tight, slack, or violated; report slack values Understand which constraints are binding and which are redundant in the solution
Variables at bounds — which variables are at their lower/upper bound Identify bottlenecks in the solution
LP relaxation quality — solve LP relaxation, report integrality gap and fractionality per integer variable Assess MIP formulation strength and identify variables with weak relaxation
Dual value analysis — distribution and extreme values of dual variables Understand shadow prices and constraint sensitivity

Solver-dependent (requires specific solver backends)

Diagnostic Use case
IIS analysis (already exists via compute_infeasibilities) Diagnose infeasible models
Basis condition number Assess numerical stability of the current basis

Design considerations

  • Experimental API — this module should clearly warn users (e.g. via a FutureWarning on first use or in docs) that the API is not yet stable and may change
  • Accessor pattern — similar to model.matrices, accessed via model.diagnostics
  • Lazy computation — diagnostics should be computed on demand, not on model creation
  • Solver-agnostic where possible — most diagnostics should work on the model's xarray data structures without requiring a specific solver
  • Return xarray objects — results should use xarray DataArrays/Datasets to stay consistent with linopy's data model and preserve labeled dimensions. This makes it easy to cross-reference diagnostics with the original variables and constraints
  • Non-invasive — diagnostics should never modify the model (LP relaxation analysis should restore state via unrelax())

What this is NOT

  • Not an automatic reformulation tool — diagnostics identify issues, the user decides how to fix them
  • Not a replacement for solver-specific analysis tools — but a solver-agnostic starting point
  • Not a stable API commitment (initially) — we should be free to iterate on what's useful

Activity

  1. added
    enhancementNew feature or request
    performanceThis improves performance while not (meaningfully) altering behaviour for users
    on Apr 7, 2026
  2. changed the title [-]Add model diagnostics for scaling quality and LP relaxation analysis[/-] [+]Add model diagnostics module[/+] on Apr 7, 2026
  3. FBumann commented on Apr 7, 2026

    @FBumann
    CollaboratorAuthor

    This is only idea for now and not sth i rely on. Might need some changes after #630.
    If someone thinks this is usefull we could add it.

  4. odow commented on Apr 7, 2026

    @odow
    Contributor
  5. added theissue type on May 4, 2026
  6. isanchez-ng commented on Sep 23, 2026

    @isanchez-ng
    Contributor

    Hello! :) Is this issue up to date? I was thinking of giving it a try!
    Asking because it seems rather old, and maybe the plans for it could have changed.

  7. MaykThewessen commented on Oct 4, 2026

    @MaykThewessen
    Contributor

    Note

    The following factual scope and benchmark notes were prepared with AI assistance.

    A concrete BirdFlow core-model build provides an example workload for this issue. The first 168 snapshots of an existing network produce 2,141,398 constraint rows, 970,368 variable columns and 3,446,992 stored coefficients. There are no numerically empty rows. Nonzero absolute coefficients range from 0.1562165595662344 to 1745.3292519943295, with a maximum within-row ratio of 11172.498337183812. That ratio is a diagnostic observation, not proof of instability.

    The prepared initial scope consists of read-only, on-demand outputs:

    • Nonzero coefficient ranges per constraint and variable, retaining labels and excluding explicit zeros from minima.
    • Empty-row classification that distinguishes satisfied rows from infeasible rows using the sign and RHS.
    • Post-solve constraint and variable-bound residuals with explicit row/column scaling and label-to-position mapping.
    • Dual availability, non-finite values, ranges and zero fractions, without declaring every zero dual invalid.

    The intended implementation would reuse model.matrices, retain sparse storage and return labeled xarray results. Warning thresholds would be explicit parameters. No public diagnostics accessor has been implemented or assigned, and no ownership decision is being made here; another contributor has already expressed interest above.

    The workload is a model-build example, without BirdFlow extra-functionality or a new solve. It does not validate post-solve diagnostics or full-year model health.

    Note: AI-assisted (model identity unavailable).

  8. MaykThewessen commented on Oct 5, 2026

    @MaykThewessen
    Contributor

    Note

    The following post-solve measurements were generated with AI assistance.

    Post-solve evidence following the earlier BirdFlow core-LP measurements: a matched dense/sparse comparison now retains two solved 1008-hour networks and scorecards. It uses production BirdFlow chunk constraints and restored runtime sidecars, a different scope from the earlier core-model-only coefficient-range example.

    All twelve weekly solves were optimal. Objective, dispatch, branch flows, storage SOC and nodal prices were exactly equal between dense legacy and sparse v1, with finite values on every compared output.

    Network-level diagnostic Both modes
    Maximum nodal imbalance 8.704e-10 MW
    Maximum cycle residual 3.069e-14 rad
    Maximum thermal violation 0 MW
    Localized slack 23.020316 GWh
    Scenario slack health gate FAIL, threshold 1 GWh

    This gives a concrete example where an optimal solve and passing physical checks coexist with a failed scenario health gate. These network-level checks do not yet validate generic LP constraint/bound residuals, scaling mappings or a dual-summary accessor. Identical nodal prices alone also do not establish their economic validity. Those need separate contracts and evidence.

    Provenance: linopy fa770b7 (merged #1003), PyPSA 1.3.0, HiGHS 1.15.1, HiPO with crossover, 18 threads. Both modes used the same frozen network, source revision and restored NTC/operative-limit sidecars. Six independent 168-hour weeks, no full-year claim. Production dependency pins were unchanged by this experiment. No public diagnostics API was added.

    Local grid context: the slack is attributable to thin-line limits in this local model and is unrelated to the linopy issue. It is unchanged between dense and sparse. SVG experiment comparison.

    Note: AI-assisted (written with model identity unavailable).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    data-modelenhancementNew feature or requestperformanceThis improves performance while not (meaningfully) altering behaviour for users

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions