Skip to content

GAMS writer: map non-finite float values to GAMS special values (NA / INF) - #4029

Closed
cjck944084735-dot wants to merge 1 commit into
Pyomo:mainfrom
cjck944084735-dot:fix/gams-writer-nonfinite-values
Closed

GAMS writer: map non-finite float values to GAMS special values (NA / INF)#4029
cjck944084735-dot wants to merge 1 commit into
Pyomo:mainfrom
cjck944084735-dot:fix/gams-writer-nonfinite-values

Conversation

@cjck944084735-dot

@cjck944084735-dot cjck944084735-dot commented Aug 23, 2026

Copy link
Copy Markdown

Fixes #3911 .

Summary/Motivation:

The GAMS writer emits Python's string rendering of non-finite floats (nan, inf, -inf), which are not valid GAMS numeric literals. Exporting a model whose variables or fixed expressions hold such values produces a .gms file that GAMS rejects with errors like the symbol nan is undefined (reported in #3911). All numeric output in pyomo/repn/plugins/gams_writer.py goes through ftoa() (format '%.17g'), which renders non-finite floats as nan/inf, e.g. on current main with default warmstart=True:

c2.. GAMS_OBJECTIVE =e= power(x1, 2) + x2 + nan ;
x1.l = nan;
x2.l = inf;

GAMS's special numeric literals are NA and INF/-INF, so every one of those lines is rejected by GAMS. This hits all value-emitting paths of the writer: constraint bounds, expression constants (fixed Vars/Params), warmstart .l values, and fixed-variable .fx values.

Changes proposed in this PR:

  • Add a gams_ftoa() wrapper that post-processes the ftoa() result: nan maps to GAMS NA, and ±inf maps to ±INF (including the parenthesized (-inf) branch).
  • Route all 18 numeric output sites of the GAMS writer through gams_ftoa().
  • Add two regression tests: test_nonfinite_var_to_string (expression path with fixed nan/inf/-inf Vars) and test_nonfinite_var_values_write_gams (full model write asserting .l warmstart values, the fixed-expression constant, and that no bare nan/inf appears in the exported file).

After the change the same model exports as:

c2.. GAMS_OBJECTIVE =e= power(x1, 2) + x2 + NA ;
x1.l = NA;
x2.l = INF;

Two notes on float semantics that the tests document: -1 * nan is nan (the sign is lost), so - z with z = nan exports as + NA; and -inf values in monomials are parenthesized by ftoa, hence + (-INF).

Local verification: full existing suite pyomo/repn/tests/gams/ passes (24 passed, 1 skipped), and black --check is clean.

Note for reviewers: pyomo/repn/plugins/gams_writer_v2.py has the same pattern for warmstart values (ostream.write(f"{v}.l = {pyomo_v.value};\n"), line ~537). I left it untouched to keep this PR focused on the default gams writer from the issue; happy to apply the equivalent mapping there in this PR or a follow-up, whichever the maintainers prefer.

AI-Use Disclosure

  • AI tools were NOT used during the preparation of this PR

or

  • AI tools contributed to the development of this PR

    • AI tools generated documentation (including the PR description/comments, code comments, and/or Sphinx documentation)
    • AI tools generated tests (baselines, examples, and/or code)
    • AI tools generated code (apart from tests)

    Review process (select ONE):

    • Rewritten: All AI-generated content was rewritten by me before being committed.
    • Reviewed/verified: I retained AI-generated content and verified it before committing. Verification included (as applicable):
      • Ran the code and fixed issues
      • Added and ran tests
      • Checked correctness/logic of code and tests
      • Checked for alignment with the contribution guide
      • Considered security implications
    • As-is: AI-generated content was commited directly to the repository

Notes for reviewers (optional): Where should reviewers focus? (1) The gams_ftoa() mapping of nan/inf to NA/INF — please confirm NA (rather than an error) is the desired semantic for undefined variable values in exported GAMS files. (2) Whether gams_writer_v2 should be fixed in this PR or a follow-up.

Legal Acknowledgement

By contributing to this software project, I have read the contribution guide and agree to the following terms and conditions for my contribution:

  1. I agree my contributions are submitted under the BSD license.
  2. I represent I am authorized to make the contributions and grant the license. If my employer has rights to intellectual property that includes these contributions, I represent that I have received permission to make contributions and grant the required license on behalf of that employer.

Python renders non-finite floats as the strings 'nan'/'inf', which are
not valid GAMS numeric literals, so exporting a model whose variables or
fixed expressions hold such values produces a .gms file that GAMS
rejects with errors like "the symbol nan is undefined".  Route all
numeric output in the GAMS writer through a gams_ftoa() wrapper that
maps them to GAMS's special values NA and INF.

Fixes Pyomo#3911
@blnicho

blnicho commented Aug 24, 2026

Copy link
Copy Markdown
Member

@cjck944084735-dot thank you for the bug fix. We welcome contributions from the community but please make sure to review our contributor guide: https://pyomo.readthedocs.io/en/latest/contribution_guide.html

In particular, we require all contributors to use our PR template so this will be closed by policy. Please feel free to reopen with the correct template and we will happily review it.

@cjck944084735-dot

Copy link
Copy Markdown
Author

@blnicho thank you for the pointer and sorry for missing the template! I've reopened the contribution as #4033 from the same branch, now using the PR template (including the AI-use disclosure and legal acknowledgement). Happy to adjust anything per the contributor guide.

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.

Handling of float('nan') especially in GAMSWriter

2 participants