Skip to content

Ignore units in container keys; add constraint dual keys in a consistent order#109

Merged
jd-lara merged 6 commits into
mainfrom
ac/canonical-key-component-type
Jun 8, 2026
Merged

Ignore units in container keys; add constraint dual keys in a consistent order#109
jd-lara merged 6 commits into
mainfrom
ac/canonical-key-component-type

Conversation

@acostarelli
Copy link
Copy Markdown
Member

No description provided.

Anthony Costarelli and others added 4 commits May 30, 2026 15:18
`_copy_dual_values!` copied dual values positionally (`dual.data .=
constraint.data`). When the dual and constraint containers carry the same
axis labels in a different order, this misaligns each label's value.

This happens for the network `NodalBalanceActiveConstraint`: the dual
container is sized from `get_name.(devices)` (component order) while the
constraint axis follows the PowerModels bus map order, so each bus's dual
was written onto the wrong bus. The permutation is invisible in
uncongested hours (flat nodal prices) and only surfaces once congestion
makes nodal prices differ — e.g. the DCP-vs-PTDF LMP comparison in
PowerOperationsModels.

Take the fast broadcast path only when the axes match exactly; otherwise
copy by label so values follow their labels regardless of axis order.

Also bump PowerNetworkMatrices compat to allow ^0.22.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PSY6 parameterized some component families (e.g. ReserveDemandCurve) on a
unit-system type, so a partially-applied type like ReserveDemandCurve{ReserveUp}
is now a UnionAll rather than a concrete DataType. IS strip_module_name reads
T.parameters (which only exists on DataType), throwing
`FieldError: type UnionAll has no field parameters` whenever such a type appears
in an optimization-container key (e.g. VariableKey for an ORDC reserve variable).

Add _strip_module_name_for_key, which unwraps the UnionAll, keeps the bound
parameters and drops the free TypeVars (so keys stay unique per ReserveUp vs
ReserveDown), and route encode_symbol through it. DataType keys are unchanged.

The proper fix belongs upstream in IS strip_module_name (handle `T isa UnionAll`
like it already handles `T isa Union`); this is a local workaround because IS is
pulled in as a pinned git-branch dependency, not an editable in-repo checkout.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Build the dual container from the existing constraint container's axes so the
dual axis matches the constraint exactly, instead of sizing it from
IS.get_name.(devices). The PowerModels nodal-balance constraint is built in
bus-map order, which differs from component order, so the old positional dual
copy wrote each bus's dual onto the wrong bus once congestion made nodal prices
differ. _copy_dual_values! now asserts the axes match rather than papering over
a mismatch.
PSY6 parameterizes cost-bearing components on a unit system (e.g.
ReserveDemandCurve{ReserveUp, NaturalUnit}). Keys built from a component
instance (typeof(service)) carried the unit parameter while keys built from the
user-declared model type (ReserveDemandCurve{ReserveUp}) did not, so storage and
lookups produced different VariableKey{T,U} objects and lookups failed with
InvalidValue.

Normalize the component type at the single point every container key is
constructed: canonical_component_type drops trailing AbstractUnitSystem
parameters in the OptimizationContainerKey constructors and make_key. It is
@generated so it folds to a compile-time constant (type stable, no runtime
cost), and a no-op for component types without a unit-system parameter.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts optimization container key canonicalization to treat unit-system type parameters as non-identifying, and updates dual-container construction/copying to ensure constraint duals align with the constraint axis ordering.

Changes:

  • Canonicalize component types used in OptimizationContainerKeys by stripping trailing unit-system parameters so keys built from instances and user-declared types match.
  • Ensure DenseAxisArray dual copying is axis-safe by asserting axes match before positional copying.
  • Build network-model dual containers using the existing constraint container’s axis ordering; expand PowerNetworkMatrices compat to include 0.22.

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated 1 comment.

File Description
src/core/optimization_container_keys.jl Introduces canonical component-type normalization for container keys (unit-system params ignored).
src/core/dual_processing.jl Adds an axis-equality assertion before positional dual copying for dense containers.
src/common_models/add_constraint_dual.jl Reworks network-model dual container assignment to reuse constraint axes (but currently introduces an order-dependence issue).
Project.toml Expands compat range for PowerNetworkMatrices to include ^0.22.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/common_models/add_constraint_dual.jl Outdated
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 2, 2026

Performance Results
Main

Network: 10 nodes, 13 edges, 3 cost segments
Generators: 5, Demands: 5
Loss coefficients (a, b, c) per generator:
  n1: a=0.007287  b=0.005968  c=0.003850
  n2: a=0.001094  b=0.009002  c=0.007028
  n3: a=0.007897  b=0.008552  c=0.003001
  n4: a=0.008179  b=0.002530  c=0.009637
  n5: a=0.009778  b=0.009934  c=0.009099

Solver logs: /home/runner/work/InfrastructureOptimizationModels.jl/InfrastructureOptimizationModels.jl/main/test/performance/logs/solver_2026-06-05T17-26-55.log

==============================================================================================================================================
Bilinear Approximation Benchmarks
  Refinement = depth for all methods
==============================================================================================================================================
Method          R   Vars Constrs   Bins    Objective   Gap(%) MIPGap(%)     LowerBnd  rmse δbi   max δbi   rmse δq    max δq  build_t  solve_t
----------------------------------------------------------------------------------------------------------------------------------------------
NLP (Ipopt)     -     40     105      0     0.956760        -         -            -  0.00e+00  0.00e+00  0.00e+00  0.00e+00   0.0314   0.0043

NLP (Uno)       -     40     105      0     0.956760   0.0000         -            -  0.00e+00  0.00e+00  0.00e+00  0.00e+00   0.0013   0.0053

Bin2+sSOS       4    190     535      0     1.180700  23.4061    0.0000     1.180700  8.07e-02  1.41e-01  2.49e+01  5.48e+01   0.0055   1.8184
Bin2+sSOS       6    250     655      0     1.098175  14.7806    0.0000     1.098175  1.26e-01  2.86e-01  5.47e+00  9.07e+00   0.0058   7.5321
Bin2+sSOS       8    310     775      0     1.053069  10.0661    0.0088     1.052976  8.62e-02  2.43e-01  4.18e+01  9.32e+01   0.0060  26.5886

Bin2+mSOS       4    310     805    120     1.180700  23.4061    0.0000     1.180700  8.07e-02  1.41e-01  2.49e+01  5.48e+01   0.0023   1.7679
Bin2+mSOS       6    430    1045    180     1.098175  14.7806    0.0000     1.098175  1.26e-01  2.86e-01  5.47e+00  9.07e+00   0.0024   5.7936
Bin2+mSOS       8    550    1285    240     1.053069  10.0661    0.0080     1.052984  8.62e-02  2.43e-01  4.18e+01  9.32e+01   0.0025  19.0581

Bin2+Saw        4    310    1075    120     0.985859   3.0414    0.0055     0.985805  1.15e-01  2.48e-01  2.65e+02  5.18e+02   0.0027  11.5108
Bin2+Saw        6    430    1495    180     0.958677   0.2004    0.0077     0.958603  7.52e-02  1.68e-01  8.94e+01  1.71e+02   0.0031  13.5339
Bin2+Saw        8    550    1915    240     0.956996   0.0247    0.0072     0.956927  7.52e-02  1.68e-01  1.37e+02  2.09e+02   0.0034  49.6601

HybS+sSOS       4    310    1165      0     0.812261  15.1030    0.0000     0.812261  5.52e-01  1.00e+00  2.04e+02  2.97e+02   0.0063   2.7075
HybS+sSOS       6    410    1545      0     0.891208   6.8515    0.0000     0.891208  5.49e-01  1.00e+00  3.69e+02  7.09e+02   0.0068   4.8634
HybS+sSOS       8    510    1925      0     0.934789   2.2965    0.0087     0.934707  5.48e-01  1.00e+00  3.47e+02  5.22e+02   0.0074  15.5740

HybS+mSOS       4    390    1345     80     0.812261  15.1030    0.0046     0.812224  5.52e-01  1.00e+00  2.04e+02  2.97e+02   0.0032   2.8454
HybS+mSOS       6    530    1805    120     0.891208   6.8515    0.0000     0.891208  5.49e-01  1.00e+00  3.69e+02  7.09e+02   0.0035   6.2706
HybS+mSOS       8    670    2265    160     0.934789   2.2965    0.0047     0.934744  5.48e-01  1.00e+00  3.47e+02  5.22e+02   0.0040  16.1867

HybS+Saw        4    390    1525     80     0.951869   0.5113    0.0084     0.951789  5.48e-01  1.00e+00  2.16e+03  4.23e+03   0.0035  10.4856
HybS+Saw        6    530    2105    120     0.956386   0.0391    0.0099     0.956291  5.48e-01  1.00e+00  4.57e+02  7.57e+02   0.0039  33.2437
HybS+Saw        8    670    2685    160     0.956734   0.0027    0.0078     0.956659  5.48e-01  1.00e+00  6.08e+00  1.07e+01   0.0046 100.3099

DNMDT           4    395    1640     80     0.954878   0.1967    0.0004     0.954874  1.39e-03  3.13e-03  4.70e-04  1.05e-03   0.0031   4.1327
DNMDT           6    550    2315    120     0.956636   0.0130    0.0098     0.956541  6.75e-05  1.57e-04  4.26e+03  9.54e+03   0.0034  17.6488
DNMDT           8    705    2990    160     0.956754   0.0007    0.0096     0.956662  4.62e-06  1.14e-05  9.42e+01  1.49e+02   0.0038  55.5909

==============================================================================================================================================

This branch

Network: 10 nodes, 13 edges, 3 cost segments
Generators: 5, Demands: 5
Loss coefficients (a, b, c) per generator:
  n1: a=0.007287  b=0.005968  c=0.003850
  n2: a=0.001094  b=0.009002  c=0.007028
  n3: a=0.007897  b=0.008552  c=0.003001
  n4: a=0.008179  b=0.002530  c=0.009637
  n5: a=0.009778  b=0.009934  c=0.009099

Solver logs: /home/runner/work/InfrastructureOptimizationModels.jl/InfrastructureOptimizationModels.jl/branch/test/performance/logs/solver_2026-06-05T17-35-09.log

==============================================================================================================================================
Bilinear Approximation Benchmarks
  Refinement = depth for all methods
==============================================================================================================================================
Method          R   Vars Constrs   Bins    Objective   Gap(%) MIPGap(%)     LowerBnd  rmse δbi   max δbi   rmse δq    max δq  build_t  solve_t
----------------------------------------------------------------------------------------------------------------------------------------------
NLP (Ipopt)     -     40     105      0     0.956760        -         -            -  0.00e+00  0.00e+00  0.00e+00  0.00e+00   0.0324   0.0048

NLP (Uno)       -     40     105      0     0.956760   0.0000         -            -  0.00e+00  0.00e+00  0.00e+00  0.00e+00   0.0014   0.0055

Bin2+sSOS       4    190     535      0     1.180700  23.4061    0.0000     1.180700  8.07e-02  1.41e-01  2.49e+01  5.48e+01   0.0063   1.8146
Bin2+sSOS       6    250     655      0     1.098175  14.7806    0.0000     1.098175  1.26e-01  2.86e-01  5.47e+00  9.07e+00   0.0060   7.5052
Bin2+sSOS       8    310     775      0     1.053069  10.0661    0.0088     1.052976  8.62e-02  2.43e-01  4.18e+01  9.32e+01   0.0060  26.4847

Bin2+mSOS       4    310     805    120     1.180700  23.4061    0.0000     1.180700  8.07e-02  1.41e-01  2.49e+01  5.48e+01   0.0024   1.7534
Bin2+mSOS       6    430    1045    180     1.098175  14.7806    0.0000     1.098175  1.26e-01  2.86e-01  5.47e+00  9.07e+00   0.0025   5.7573
Bin2+mSOS       8    550    1285    240     1.053069  10.0661    0.0080     1.052984  8.62e-02  2.43e-01  4.18e+01  9.32e+01   0.0025  18.8238

Bin2+Saw        4    310    1075    120     0.985859   3.0414    0.0055     0.985805  1.15e-01  2.48e-01  2.65e+02  5.18e+02   0.0027  11.3835
Bin2+Saw        6    430    1495    180     0.958677   0.2004    0.0077     0.958603  7.52e-02  1.68e-01  8.94e+01  1.71e+02   0.0030  13.3377
Bin2+Saw        8    550    1915    240     0.956996   0.0247    0.0072     0.956927  7.52e-02  1.68e-01  1.37e+02  2.09e+02   0.0034  49.3735

HybS+sSOS       4    310    1165      0     0.812261  15.1030    0.0000     0.812261  5.52e-01  1.00e+00  2.04e+02  2.97e+02   0.0067   2.6923
HybS+sSOS       6    410    1545      0     0.891208   6.8515    0.0000     0.891208  5.49e-01  1.00e+00  3.69e+02  7.09e+02   0.0069   4.8165
HybS+sSOS       8    510    1925      0     0.934789   2.2965    0.0087     0.934707  5.48e-01  1.00e+00  3.47e+02  5.22e+02   0.0075  15.4312

HybS+mSOS       4    390    1345     80     0.812261  15.1030    0.0046     0.812224  5.52e-01  1.00e+00  2.04e+02  2.97e+02   0.0032   2.8213
HybS+mSOS       6    530    1805    120     0.891208   6.8515    0.0000     0.891208  5.49e-01  1.00e+00  3.69e+02  7.09e+02   0.0035   6.1705
HybS+mSOS       8    670    2265    160     0.934789   2.2965    0.0047     0.934744  5.48e-01  1.00e+00  3.47e+02  5.22e+02   0.0208  16.2134

HybS+Saw        4    390    1525     80     0.951869   0.5113    0.0084     0.951789  5.48e-01  1.00e+00  2.16e+03  4.23e+03   0.0034  10.4898
HybS+Saw        6    530    2105    120     0.956386   0.0391    0.0099     0.956291  5.48e-01  1.00e+00  4.57e+02  7.57e+02   0.0039  33.2388
HybS+Saw        8    670    2685    160     0.956734   0.0027    0.0078     0.956659  5.48e-01  1.00e+00  6.08e+00  1.07e+01   0.0046 101.6112

DNMDT           4    395    1640     80     0.954878   0.1967    0.0004     0.954874  1.39e-03  3.13e-03  4.70e-04  1.05e-03   0.0032   4.1897
DNMDT           6    550    2315    120     0.956636   0.0130    0.0098     0.956541  6.75e-05  1.57e-04  4.26e+03  9.54e+03   0.0034  17.6515
DNMDT           8    705    2990    160     0.956754   0.0007    0.0096     0.956662  4.62e-06  1.14e-05  9.42e+01  1.49e+02   0.0039  55.6907

==============================================================================================================================================

Comment thread src/core/optimization_container_keys.jl Outdated
Copy link
Copy Markdown
Member

@jd-lara jd-lara left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean up excessive AI commentary

@acostarelli acostarelli requested a review from jd-lara June 3, 2026 19:07
const CONTAINER_KEY_EMPTY_META = ""

# Strip units before making keys for parametric structs
@generated function canonical_component_type(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have methods to get this from the key get_component_type

Copy link
Copy Markdown
Member

@jd-lara jd-lara left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove unnecessary method

PNM #312 made deepcopy of live solver-factorization caches (KLU/libSparse
handles) a deliberate error. Strip PTDF/MODF matrices from the network model
before the template deepcopy in the DecisionModel constructor and re-attach
the same objects to both the original and the copy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jd-lara jd-lara merged commit 37dbcf6 into main Jun 8, 2026
5 of 6 checks passed
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.

4 participants