Skip to content

New GAMS solver interface, writer, and solution loader. #3683

Open
AnhTran01 wants to merge 96 commits intoPyomo:mainfrom
AnhTran01:GAMS_new_solver_interface
Open

New GAMS solver interface, writer, and solution loader. #3683
AnhTran01 wants to merge 96 commits intoPyomo:mainfrom
AnhTran01:GAMS_new_solver_interface

Conversation

@AnhTran01
Copy link
Contributor

Fixes NA

Summary/Motivation:

This PR introduced a new GAMS solver interface that uses the LinearRepnVisitor to enable coefficient gathering when writing out the scalar GAMS model in gms format.

Changes proposed in this PR:

  • New GAMS solver interface in pyomo.contrib.solver.solvers
  • New GAMS writer in pyomo.repn.plugins
  • New GAMS solution loader in pyomo.contrib.solver.solvers

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.

@AnhTran01
Copy link
Contributor Author

@boxblox
@abhosekar

@mrmundt
Copy link
Contributor

mrmundt commented Aug 7, 2025

@AnhTran01 - Please make sure to run black on your changes!

Copy link
Contributor

@mrmundt mrmundt left a comment

Choose a reason for hiding this comment

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

Immediate piece of feedback, recognizing that I have not thoroughly looked over anything - there are no tests anywhere, and there absolutely will need to be some.

Thanks for this, though, I am so excited!!

Copy link
Member

@jsiirola jsiirola left a comment

Choose a reason for hiding this comment

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

(Some initial comments, mostly on configuration)

if config.logfile is not None:
config.logfile = os.path.abspath(config.logfile)

config.writer_config.put_results_format = 'gdx' if gdxcc_available else 'dat'
Copy link
Member

Choose a reason for hiding this comment

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

This should probably only overwrite the configuration if it is still None (that way users can set / override the interface)

@codecov
Copy link

codecov bot commented Feb 6, 2026

Codecov Report

❌ Patch coverage is 89.10162% with 74 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.46%. Comparing base (f2d23a5) to head (e32b768).
⚠️ Report is 32 commits behind head on main.

Files with missing lines Patch % Lines
pyomo/contrib/solver/solvers/gams.py 89.29% 32 Missing ⚠️
pyomo/repn/plugins/gams_writer_v2.py 89.34% 31 Missing ⚠️
pyomo/contrib/solver/solvers/gms_sol_reader.py 87.35% 11 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3683      +/-   ##
==========================================
- Coverage   89.48%   89.46%   -0.03%     
==========================================
  Files         904      907       +3     
  Lines      105385   106064     +679     
==========================================
+ Hits        94299    94885     +586     
- Misses      11086    11179      +93     
Flag Coverage Δ
builders 28.96% <18.40%> (-0.08%) ⬇️
default 83.61% <89.10%> (?)
expensive 35.41% <18.40%> (?)
linux 86.71% <78.05%> (-2.50%) ⬇️
linux_other 86.71% <78.05%> (-0.06%) ⬇️
oldsolvers 29.62% <18.40%> (-0.08%) ⬇️
osx 82.88% <78.05%> (-0.04%) ⬇️
win 84.94% <77.31%> (-0.07%) ⬇️
win_other 84.94% <77.31%> (-0.07%) ⬇️
xpress95 ?

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@boxblox
Copy link

boxblox commented Feb 6, 2026

seems like codecov is not ideal. most of the missed lines are when gdx results are being read in with our gamsapi. instead of with a dat file. what do you think? we'd need to add another test dependency to the pipeline.

@mrmundt
Copy link
Contributor

mrmundt commented Feb 9, 2026

seems like codecov is not ideal. most of the missed lines are when gdx results are being read in with our gamsapi. instead of with a dat file. what do you think? we'd need to add another test dependency to the pipeline.

Oop, sorry, I didn't see this. The coverage is actually fine. 89% is more than okay.

@boxblox
Copy link

boxblox commented Feb 10, 2026

seems like codecov is not ideal. most of the missed lines are when gdx results are being read in with our gamsapi. instead of with a dat file. what do you think? we'd need to add another test dependency to the pipeline.

Oop, sorry, I didn't see this. The coverage is actually fine. 89% is more than okay.

Sounds good, thanks for the follow up. Is there anything I can do to help with this?

Copy link
Member

@blnicho blnicho left a comment

Choose a reason for hiding this comment

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

@boxblox I'm going to publish my comments/questions in batches as I'm reviewing since we're trying to move quickly on this to get it into the release. Here is the first set.

Comment on lines 35 to 41
self.primals: List[float] = []
self.duals: List[float] = []
self.var_suffixes: Dict[str, Dict[int, Any]] = {}
self.con_suffixes: Dict[str, Dict[Any]] = {}
self.obj_suffixes: Dict[str, Dict[int, Any]] = {}
self.problem_suffixes: Dict[str, List[Any]] = {}
self.other: List(str) = []
Copy link
Member

Choose a reason for hiding this comment

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

Do we still need to use typing.Dict and typing.List here or can we use the built-in Python dict and list?

Copy link

@boxblox boxblox Feb 12, 2026

Choose a reason for hiding this comment

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

if you want type hinting to catch that the dict structure should be a str key and a list value, then yes, you will need to keep these as is (same logic for the List) im fixing a typo on the List(str) should be List[str] (w/ brackets)

else:
for sym, obj in self._gms_info.var_symbol_map.bySymbol.items():
level = self._gdx_data[sym][0]
if obj.parent_component().ctype is Var:
Copy link
Member

Choose a reason for hiding this comment

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

Can var_symbol_map contain Pyomo component types other than Var?

Copy link

Choose a reason for hiding this comment

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

I will need to let @AnhTran01 give more detail here, but with the simple models that we have in the test library only Var components appear in this dict.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@blnicho The var_symbol_map only contains Pyomo component type Var (from the internal data of _gms_info below).

obj_data

Copy link
Member

Choose a reason for hiding this comment

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

In that case we can probably remove the ctype check on L61.

Copy link

Choose a reason for hiding this comment

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

ok, I have removed this.

RangeSet,
Port,
},
targets={Suffix, SOSConstraint, Objective},
Copy link
Member

Choose a reason for hiding this comment

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

I don't see any code for handling Suffix or SOSConstraint components. I suspect this writer will silently ignore any of these components declared on the model which seems like a bug. We should check for these components and log a warning or throw an error if any are encountered.

@jsiirola would removing Suffix and SOSConstraint from the target argument be sufficient for getting the writer to treat these component types as "unknown" and throw an error?

Copy link

Choose a reason for hiding this comment

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

throwing an appropriate error seems fine with me. Thank you for the help/guidance in crafting this properly.

boxblox and others added 2 commits February 12, 2026 10:15
Co-authored-by: Bethany Nicholson <blnicho@users.noreply.github.com>
Comment on lines 181 to 183
def available(
self, config: Optional[GAMSConfig] = None, recheck: bool = False
) -> Availability:
Copy link
Member

Choose a reason for hiding this comment

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

We have been re-working how we want to use / think about available(). For solvers that rely on an external executable, we are caching a dict that maps executable path to the version information. That allows the cache to be a class attribute (so we don't have to keep re-checking), and to be robust to users pointing the solver to a new executable. See ipopt.py for the current reference implementation

Copy link

Choose a reason for hiding this comment

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

ok, I am taking a look now

Copy link

Choose a reason for hiding this comment

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

yeah, i see now. good to not keep calling a subprocess if .solve() is in a loop. hopefully fe7b43f fixes this

Copy link

Choose a reason for hiding this comment

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

fixing the errors now... stay tuned

Copy link

Choose a reason for hiding this comment

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

apologies... my tests are are now passing locally

Copy link
Member

@blnicho blnicho left a comment

Choose a reason for hiding this comment

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

Updating the copyright headers for the new files in this PR following #3846

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

Labels

None yet

Projects

Status: Reviewer Approved

Development

Successfully merging this pull request may close these issues.

7 participants