Setting and Getting AMPL Options in amplpy#
Description: Shows every way amplpy exposes to set and get AMPL options — snake_case methods, dict-style indexing, camelCase aliases, and per-solve keyword overrides — covering interpreter options, solver-specific option strings, value types, and restore patterns.
Tags: amplpy, api, options, solver-options, highlights
Notebook author: Jürgen Lentz <lentz@ampl.com>
AMPL options control everything from which solver runs a model to how much diagnostic output the interpreter prints. amplpy gives you several equivalent ways to read and write them, which is convenient once you know they are equivalent — and confusing until then. This notebook lays them all out side by side.
Ways to set an option:
Method |
Example |
|---|---|
|
|
|
|
|
camelCase alias, identical behaviour |
|
sets a solver options string right before solving |
Ways to get an option:
Method |
Returns |
|---|---|
|
the current value, or |
|
same, dict-style |
|
camelCase alias |
# Install dependencies
%pip install -q amplpy
# Google Colab & Kaggle integration
from amplpy import AMPL, ampl_notebook
ampl = ampl_notebook(
modules=["highs"], # modules to install
license_uuid="default", # license to use
) # instantiate AMPL object and register magics
Running example: the diet problem#
We reuse the classic diet problem to see options take effect on a real solve — most of the option mechanics themselves are demonstrated with short, standalone snippets.
import pandas as pd
import numpy as np
from amplpy import AMPL
foods = ["BEEF", "CHK", "FISH", "HAM", "MCH", "MTL", "SPG", "TUR"]
nutrs = ["A", "B1", "B2", "C"]
food_df = pd.DataFrame(
{
"cost": [3.19, 2.59, 2.29, 2.89, 1.89, 1.99, 1.99, 2.49],
"f_min": [0] * 8,
"f_max": [100] * 8,
},
index=pd.Index(foods, name="FOOD"),
)
nutr_df = pd.DataFrame(
{"n_min": [700] * 4, "n_max": [10000] * 4},
index=pd.Index(nutrs, name="NUTR"),
)
amt_df = pd.DataFrame(
np.array(
[
[60, 8, 8, 40, 15, 70, 25, 60],
[10, 20, 15, 35, 15, 15, 25, 15],
[15, 20, 10, 10, 15, 15, 15, 10],
[20, 0, 10, 40, 35, 30, 50, 20],
]
),
index=pd.Index(nutrs, name="NUTR"),
columns=pd.Index(foods, name="FOOD"),
)
_MODEL = """
set FOOD;
set NUTR;
param cost {FOOD} > 0;
param f_min {FOOD} >= 0;
param f_max {j in FOOD} >= f_min[j];
param n_min {NUTR} >= 0;
param n_max {i in NUTR} >= n_min[i];
param amt {NUTR, FOOD} >= 0;
var Buy {j in FOOD} >= f_min[j], <= f_max[j];
minimize Total_Cost: sum {j in FOOD} cost[j] * Buy[j];
subject to Diet {i in NUTR}:
n_min[i] <= sum {j in FOOD} amt[i,j] * Buy[j] <= n_max[i];
"""
def make_ampl():
"""Return a fresh AMPL instance with the diet model and data loaded."""
a = AMPL()
a.eval(_MODEL)
a.set["FOOD"] = foods
a.set["NUTR"] = nutrs
a.set_data(food_df, "FOOD")
a.set_data(nutr_df, "NUTR")
a.param["amt"] = amt_df
return a
Two kinds of options#
Kind |
Examples |
Value |
|---|---|---|
Interpreter options |
|
a single number, string, or flag |
Solver option strings |
|
one string bundling several |
Both kinds are read and written the same way from amplpy — the only difference is what you put in the value.
ampl = make_ampl()
ampl.set_option("solver", "highs")
ampl.set_option("show_stats", 1)
print("--- with show_stats = 1 ---")
ampl.solve()
ampl.set_option("show_stats", 0)
print("--- with show_stats = 0 ---")
ampl.solve()
--- with show_stats = 1 ---
8 variables, all linear
4 constraints, all linear; 31 nonzeros
4 range constraints
1 linear objective; 8 nonzeros.
HiGHS 1.7.1:
HiGHS 1.7.1: optimal solution; objective 88.2
1 simplex iterations
0 barrier iterations
--- with show_stats = 0 ---
HiGHS 1.7.1:
HiGHS 1.7.1: optimal solution; objective 88.2
0 simplex iterations
0 barrier iterations
Three ways to set an option#
The three styles below all end up setting the same underlying AMPL option — they are interchangeable, so pick whichever reads best in context.
ampl = AMPL()
ampl.set_option("solver", "highs")
print("set_option: ", ampl.get_option("solver"))
ampl.option["solver"] = "cplex"
print("option[...]: ", ampl.get_option("solver"))
ampl.setOption("solver", "highs")
print("setOption: ", ampl.get_option("solver"))
set_option: highs
option[...]: cplex
setOption: highs
Three ways to get an option#
get_option, option[...], and getOption all hand back a native Python value directly and are fully interchangeable.
ampl = AMPL()
ampl.set_option("solver", "highs")
print("get_option: ", ampl.get_option("solver"))
print("option[...]: ", ampl.option["solver"])
print("getOption: ", ampl.getOption("solver"))
get_option: highs
option[...]: highs
getOption: highs
Solver-specific option strings#
A solver options string bundles several suboptions into one value, space-separated as key=value pairs. amplpy has no per-suboption getter for these — you set and get the whole string, and parse it yourself if you need individual values.
ampl = AMPL()
ampl.set_option("highs_options", "outlev=1 timelim=10 presolve=off")
raw = ampl.get_option("highs_options")
print("raw string:", raw)
parsed = dict(pair.split("=") for pair in raw.split())
print("parsed: ", parsed)
# Change a single suboption and write the whole string back
parsed["timelim"] = "30"
ampl.set_option("highs_options", " ".join(f"{k}={v}" for k, v in parsed.items()))
print("updated: ", ampl.get_option("highs_options"))
raw string: outlev=1 timelim=10 presolve=off
parsed: {'outlev': '1', 'timelim': '10', 'presolve': 'off'}
updated: outlev=1 timelim=30 presolve=off
Setting solver options via solve()#
ampl.solve() accepts <solver>_options as a keyword argument — a shortcut for calling set_option() right before solving. The option is set as a normal option and keeps its value afterwards, it is not automatically reverted.
ampl = make_ampl()
print("before solve:", ampl.get_option("highs_options"))
ampl.solve(solver="highs", highs_options="outlev=1")
print("after solve: ", ampl.get_option("highs_options"))
before solve: None
HiGHS 1.7.1:
tech:outlev = 1
Running HiGHS 1.7.1 (git hash: dcf3813): Copyright (c) 2024 HiGHS under MIT licence terms
Coefficient ranges:
Matrix [8e+00, 7e+01]
Cost [2e+00, 3e+00]
Bound [1e+02, 1e+02]
RHS [7e+02, 1e+04]
Presolving model
4 rows, 8 cols, 31 nonzeros 0s
4 rows, 8 cols, 31 nonzeros 0s
Presolve : Reductions: rows 4(-0); columns 8(-0); elements 31(-0) - Not reduced
Problem not reduced by presolve: solving the LP
Using EKK dual simplex solver - serial
Iteration Objective Infeasibilities num(sum)
0 0.0000000000e+00 Pr: 4(2800) 0s
1 8.8200000000e+01 Pr: 0(0) 0s
Model status : Optimal
Simplex iterations: 1
Objective value : 8.8200000000e+01
HiGHS run time : 0.00
HiGHS 1.7.1: optimal solution; objective 88.2
1 simplex iterations
0 barrier iterations
after solve: outlev=1
Value types and unset options#
set_option() accepts Python int, float, str, and bool. AMPL options are fundamentally numeric or string values, so a Python bool is stored and returned as 0/1, not as True/False. get_option() returns None only for a name that was never set — well-known options always have a default.
ampl = AMPL()
ampl.set_option("show_stats", True)
print("bool in: ", ampl.get_option("show_stats"), type(ampl.get_option("show_stats")))
ampl.set_option("solver", "highs")
print("string: ", ampl.get_option("solver"), type(ampl.get_option("solver")))
print("known default (never set): presolve =", ampl.get_option("presolve"))
print("unknown, never set: ", ampl.get_option("not_a_real_option"))
bool in: 1 <class 'int'>
string: highs <class 'str'>
known default (never set): presolve = 10
unknown, never set: None
Pattern — temporarily overriding an option#
Save the previous value with get_option(), set the new one, and restore it in a finally block — a small context manager makes this a one-liner and mirrors the save/restore pattern used for output and error handlers in the companion notebooks.
from contextlib import contextmanager
@contextmanager
def temporary_option(ampl, name, value):
previous = ampl.get_option(name)
ampl.set_option(name, value)
try:
yield
finally:
ampl.set_option(name, previous)
ampl = make_ampl()
ampl.set_option("solver", "highs")
print("before:", ampl.get_option("show_stats"))
with temporary_option(ampl, "show_stats", 1):
print("inside:", ampl.get_option("show_stats"))
ampl.solve()
print("after: ", ampl.get_option("show_stats"))
before: 0
inside: 1
8 variables, all linear
4 constraints, all linear; 31 nonzeros
4 range constraints
1 linear objective; 8 nonzeros.
HiGHS 1.7.1:
HiGHS 1.7.1: optimal solution; objective 88.2
1 simplex iterations
0 barrier iterations
after: 0
Resetting all options to their defaults#
ampl.eval("reset options;") clears every option back to its AMPL default in one call — useful between independent runs in the same session so leftover options from a previous experiment cannot leak in.
ampl = AMPL()
ampl.set_option("solver", "highs")
ampl.set_option("show_stats", 1)
print("before reset:", ampl.get_option("solver"), ampl.get_option("show_stats"))
ampl.eval("reset options;")
print("after reset: ", repr(ampl.get_option("solver")), ampl.get_option("show_stats"))
before reset: highs 1
after reset: '' 0
Summary#
Way |
Set |
Get |
|---|---|---|
snake_case methods |
|
|
dict-style |
|
|
camelCase alias |
|
|
per-solve shortcut |
|
— |
Key points:
All setter/getter styles are interchangeable — they read and write the exact same underlying AMPL option.
Interpreter options (
solver,show_stats,presolve, …) hold a single value; solver option strings (highs_options,cplex_options, …) bundle severalkey=valuesuboptions in one string that you parse yourself.ampl.solve(solver=..., <solver>_options=...)is a convenience for setting a solver’s option string right before solving — the value persists afterwards, it is not scoped to that call.get_option()returnsNoneonly for a name that has never been set; documented options always come back with their AMPL default.ampl.eval("reset options;")clears every option back to its default.Errors while setting an invalid option name go through the active
ErrorHandler— seeerror_handler.ipynb.