Skip to content

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!!

@@ -518,6 +518,11 @@ def write(self, model):
con_list[label] = declaration + definition + bounds
self.var_symbol_map.addSymbol(obj, label)

# Write the GAMS model
ostream.write("$offlisting\n")
# $offdigit ignores extra precise digits instead of erroring
Copy link

Choose a reason for hiding this comment

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

what was the error that you were seeing that prompted this addition? maybe something was being set above 1e300?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

in the previous commit test (b0c3450), the listing file output error message is "Too many digits in number $offdigit can be used to ignore trailing digits"

Copy link

Choose a reason for hiding this comment

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

but do you know what triggered this? which test was causing this error?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I went back to check the failed test. The following tests that triggered the "Too many digits error" are:

  1. TestSolvers.test_equality
  2. TestSolvers.test_linear_expression
  3. TestSolvers.test_mutable_param_with_range
  4. TestSolvers.test_param_changes
  5. TestSolvers.test_no_objective
  6. TestSolvers.test_time_limit
  7. TestLegacySolverInterface.test_param_updates

By testing locally, I see there are trailing decimal places and not the cases of 1e300 or large numbers.

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)

self.writer_config: ConfigDict = self.declare(
'writer_config', GAMSWriter.CONFIG()
)
# Share the same config as the writer, passes at the function call write
Copy link
Member

Choose a reason for hiding this comment

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

Here and below: this should just insert a copy of the CONFIG from the writer. See Ipopt here for an example.

n,
)

def version(self, config=None):
Copy link
Member

Choose a reason for hiding this comment

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

In our review of the new solver interfaces, we changed the API to:

def version(self, rehash: bool=False) -> Tuple:

Much like available(), this should leverage a cache (class dict attribute). You can look at the ipopt implementation for an idea (although that hasn't been updated to the new API).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Could you clarify the purpose of using rehash and whether the user can call rehash from the solve statement?

I found the documentation here: https://pyomo.readthedocs.io/en/latest/api/pyomo.common.fileutils.PathManager.html

Would the implementation be Executable(config.executable.path()).rehash()?

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)

@AnhTran01 AnhTran01 requested a review from mrmundt August 22, 2025 18:54
@AnhTran01
Copy link
Contributor Author

@mrmundt is the requested changes related to adding tests for the new solver interface/writer?

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