What's New in GAMSPy? A Look at the Awesome Features Since Version 1.0.0!

Posted on: 22 Oct, 2025 GAMSPy

It’s been an exciting year for the GAMSPy community! Since the stable 1.0.0 release, we’ve been hard at work making our favorite optimization modeling package even more powerful, intuitive, and efficient. If you haven’t updated in a while, you’re in for a treat.

This post will walk you through the major enhancements and new features that will improve your workflow. Let’s dive in!


1. A Sleek New CLI with Typer

Your command-line experience just got a major upgrade. We’ve migrated GAMSPy CLI to Typer , a modern and robust framework to build CLI applications.

What does this mean for you?

  • Better Help & Autocompletion: Get where you’re going faster with intelligent tab-completion and clearer help messages for all commands.

  • A Modern Feel: The CLI is now more intuitive and user-friendly.

  • GDX Dump and Diff Utilities: GAMSPy CLI has been extended with a new gdx api. You can dump the contents of gdx files with gamspy gdx dump <filename> and compare two gdx files with gamspy gdx diff <file1> <file2>.

Simply run gamspy --help in your terminal to see the difference!


2. Intuitive Access to Records

Accessing the records of your expressions is now more direct. In order to get the results of an expression, you normally assign it to a symbol with a matching domain and print symbol.records. You can now use the .records property of expressions to get a clean DataFrame directly. GAMSPy will take care of the creation of the intermediate symbol.

import gamspy as gp

m = gp.Container()
i = gp.Set(m, records=['i1', 'i2'])
a = gp.Parameter(m, domain=i, records=[('i1', 1), ('i2', 2)])
b = gp.Parameter(m, domain=i, records=[('i1', 3), ('i2', 4)])

# Old way
c = gp.Parameter(m, domain=i)
c[i] = a[i] + b[i]
print(c.records)

# New, simple way:
print((a[i] + b[i]).records)

This syntax simplifies debugging and data analysis, making your code cleaner and easier to read.


3. Simplified Lag/Lead Syntax

Writing dynamic models with time-series data is now much more natural. We’ve replaced .lead() and .lag() methods with simple arithmetic operators. This makes your equations look much closer to their mathematical representation.

Before:

inventory_balance[t] = inventory[t] == inventory[t.lag(1)] + production[t] - sales[t]

After:

inventory_balance[t] = inventory[t] == inventory[t-1] + production[t] - sales[t]

4. Save and Load Your Work with Serialization

Ever wanted to save the state of your model and come back to it later? Now you can! GAMSPy containers can be easily serialized (saved to a file) and deserialized (loaded from a file). This is perfect for checkpointing long-running processes, sharing your model setup, or simply pausing your work.

import gamspy as gp

# Assuming 'm' is your Container object
# Save the entire model state to a file
gp.serialize(m, "serialized.zip")

# Later, you can load it back
m2 = gp.deserialize("serialized.zip")

5. Speed Up Data Loading with Bulk setRecords

Loading data into your symbols just got a lot faster. Instead of calling setRecords individually for each symbol, you can now pass a dictionary of symbols and their records to a single setRecords call on the container. This significantly reduces overhead for data-heavy models.

import gamspy as gp

m = gp.Container()
i = gp.Set(m)
j = gp.Set(m)

# Pass all records in a single, efficient call
m.setRecords({i: range(5), j: range(5, 10)})

# instead of doing separate calls for each symbol
i.setRecords(range(5))
j.setRecords(range(5, 10))

6. Effortless Summation with .sum()

Here’s another great piece of syntactic sugar. Instead of wrapping a symbol in a Sum statement, you can now call the .sum() method directly on the symbol itself. It’s a small change that can make your code cleaner.

Before:

import gamspy as gp

m = gp.Container()
i = gp.Set(m)
j = gp.Set(m)
distances = gp.Parameter(m, domain=[i, j])
total = gp.Parameter(m)

# The classic way
total[...] = gp.Sum((i, j), distances[i, j])

# The new, concise way
total[...] = distances.sum()

The same syntactic sugar is also available for product , smin , smax , sand and sor operations.


7. Configure GAMSPy with Package Options

You now have more control over GAMSPy’s behavior through new package options . This allows you to set global configurations for your sessions, such as skipping validations for performance improvement.

import gamspy as gp
gp.set_options({"SOLVER_OPTION_VALIDATION": 0})

...
...
your_model_definition
...
...

In the example above, we disable solver option validations that GAMSPy performs to make sure that the provided solver options are valid. After you make sure that your model behaves the way you planned, you can just disable the validations to gain extra performance.


8. Automatic Name Inference

With GAMSPy 1.17.0, the behavior of automatic name generation for symbols with no names has changed. For example, if you execute the following code snippet in GAMSPy 1.0.0:

import gamspy as gp

with gp.Container():
  i = gp.Set()
  print(f"Name of the set: {i.name}")

GAMSPy would generate an autoname that would look like the following:

Name of the set: s0f54b804_41a5_461a_a6b9_69bac2beb72c

With the changes in 1.17.0, GAMSPy now tries to get the Python variable name from the frames in the stack and assign it to the symbol. So, the same code snippet above would now print:

Name of the set: i

Beware that the frames might not always be available (e.g. in certain REPL sessions). In that case, GAMSPy will still generate a name automatically. This behavior can be controlled via USE_PY_VAR_NAME .


9. Rename Symbols When Loading from GDX

Importing data from GDX files is now more flexible. The symbol_names argument of container.loadRecordsFromGdx method now accepts a dictionary, allowing you to map GDX symbol names to different names within your GAMSPy model. This is incredibly useful for avoiding name collisions or aligning imported data with your existing naming conventions.

import gamspy as gp

m = gp.Container()
model_demand = gp.Parameter(m, "model_demand")
m.loadRecordsFromGdx(
    "data.gdx",
    symbol_names={"gdx_demand": "model_demand"}
)

10. Performance Boosts

Who doesn’t love a speed-up? We’ve invested significant effort under the hood to make GAMSPy faster and more memory-efficient. Many models will be generated more quickly, letting you iterate on your work faster than ever before. For example, a simple benchmark on indus89.py model shows that there is 31% improvement in the model generation time between GAMSPy 1.0.0 and GAMSPy 1.17.0.

hyperfine -w 3 -r 10 'python indus89.py'
Benchmark 1 (GAMSPy 1.17.0): python indus89.py
  Time (mean ± σ):      1.514 s ±  0.029 s    [User: 2.900 s, System: 0.157 s]
  Range (min … max):    1.484 s …  1.565 s    10 runs
  
Benchmark 2 (GAMSPy 1.0.0): python indus89.py
  Time (mean ± σ):      2.197 s ±  0.067 s    [User: 2.671 s, System: 0.097 s]
  Range (min … max):    2.078 s …  2.335 s    10 runs

11. Enhancements for Machine Learning Workflows

We’re continuing to bridge the worlds of mathematical optimization and machine learning. Recent updates include:

  • Expanded Model Support: We now support embedding a wider range of neural network blocks and constructs, including:
  • Enhanced Performance:
    • Improved stability for matrix multiplication, particularly in edge cases.
    • Better bound propagation for a variety of blocks, including Linear, flatten_dims, and all pooling and convolutional layers.
  • New Capabilities:
    • Support for piecewise linear functions.
    • The ability to use singleton sets as domains.
    • New examples demonstrating the embedding of feedforward and convolutional neural networks.
import gamspy as gp
import torch.nn as nn
from gamspy.math import dim

m = gp.Container()

model = nn.Sequential(
    nn.Conv2d(1, 20, 5), nn.ReLU(), nn.Conv2d(20, 64, 5), nn.ReLU()
)
# load or train your Sequential model
# ...

m = gp.Container()
x = gp.Variable(m, domain=dim([1, 1, 32, 32]))

seq_formulation = gp.formulations.TorchSequential(m, model)
y, eqs = seq_formulation(x) # apply Sequential model to x and get y
print(y.domain) # 1, 64, 24, 24 -> batch, channels, height, width

Conclusion

The latest version of GAMSPy is all about making you more productive. With a slicker CLI, more Pythonic syntax, major performance gains, and powerful new features like serialization, there’s never been a better time to build your optimization models.

We encourage you to upgrade to the latest version (pip install gamspy --upgrade) and give these new features a try. Check out our documentation for more details and let us know what you think!

Happy modeling!