Skip to content

Commit 698e873

Browse files
authored
Merge branch 'main' into feature/pre-commit-hooks
2 parents 9a95576 + e55e4a2 commit 698e873

5 files changed

Lines changed: 235 additions & 59 deletions

File tree

.github/workflows/test_branches.yml

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,14 @@ jobs:
304304
python -m pip install --cache-dir cache/pip pymysql || \
305305
python -m pip install --cache-dir cache/pip pymysql
306306
if test -z "${{matrix.slim}}"; then
307-
python -m pip install --cache-dir cache/pip 'cplex!=22.1.2.1' docplex \
307+
# Disallow cplex 22.1.2.1 because it errors fatally when
308+
# computing IIS on python 3.13 and 3.14
309+
if [[ ${{matrix.python}} =~ 3.1[34] ]]; then
310+
CPLEX='cplex!=22.1.2.1'
311+
else
312+
CPLEX='cplex'
313+
fi
314+
python -m pip install --cache-dir cache/pip "$CPLEX" docplex \
308315
|| echo "WARNING: CPLEX Community Edition is not available"
309316
python -m pip install --cache-dir cache/pip gurobipy \
310317
|| echo "WARNING: Gurobi is not available"
@@ -375,6 +382,18 @@ jobs:
375382
# possibly if it outputs messages to stderr)
376383
conda install --update-deps -q -y python="${{matrix.python}}" $CONDA_DEPENDENCIES
377384
if test -z "${{matrix.slim}}"; then
385+
TIMEOUT_MSG="TIMEOUT: killing conda install process"
386+
PYVER=$(echo "py${{matrix.python}}" | sed 's/\.//g')
387+
echo "Installing for $PYVER"
388+
#
389+
# Disallow cplex 22.1.2 and 22.1.2.1, as they error fatally when
390+
# computing IIS on Python 3.13 and 3.14
391+
# Disallow cplex 12.9 (caused segfaults in tests)
392+
if [[ ${{matrix.python}} =~ 3.1[34] ]]; then
393+
CPLEX='cplex>=12.10,<22.1.2|>22.1.2.1'
394+
else
395+
CPLEX='cplex>=12.10'
396+
fi
378397
# xpress.init() (xpress 9.5.1 from conda) hangs indefinitely
379398
# on GHA/Windows under Python 3.10 and 3.11. Exclude that
380399
# release on that platform.
@@ -387,10 +406,7 @@ jobs:
387406
else
388407
XPRESS='xpress'
389408
fi
390-
TIMEOUT_MSG="TIMEOUT: killing conda install process"
391-
PYVER=$(echo "py${{matrix.python}}" | sed 's/\.//g')
392-
echo "Installing for $PYVER"
393-
for PKG in 'cplex>=12.10' docplex gurobi "$XPRESS" cyipopt pymumps scip; do
409+
for PKG in "$CPLEX" docplex gurobi "$XPRESS" cyipopt pymumps scip; do
394410
echo ""
395411
echo "*** Install $PKG ***"
396412
echo ""

.github/workflows/test_pr_and_main.yml

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,14 @@ jobs:
356356
python -m pip install --cache-dir cache/pip pymysql || \
357357
python -m pip install --cache-dir cache/pip pymysql
358358
if test -z "${{matrix.slim}}"; then
359-
python -m pip install --cache-dir cache/pip 'cplex!=22.1.2.1' docplex \
359+
# Disallow cplex 22.1.2.1 because it errors fatally when
360+
# computing IIS on python 3.13 and 3.14
361+
if [[ ${{matrix.python}} =~ 3.1[34] ]]; then
362+
CPLEX='cplex!=22.1.2.1'
363+
else
364+
CPLEX='cplex'
365+
fi
366+
python -m pip install --cache-dir cache/pip "$CPLEX" docplex \
360367
|| echo "WARNING: CPLEX Community Edition is not available"
361368
python -m pip install --cache-dir cache/pip gurobipy \
362369
|| echo "WARNING: Gurobi is not available"
@@ -427,6 +434,18 @@ jobs:
427434
# possibly if it outputs messages to stderr)
428435
conda install --update-deps -q -y python="${{matrix.python}}" $CONDA_DEPENDENCIES
429436
if test -z "${{matrix.slim}}"; then
437+
TIMEOUT_MSG="TIMEOUT: killing conda install process"
438+
PYVER=$(echo "py${{matrix.python}}" | sed 's/\.//g')
439+
echo "Installing for $PYVER"
440+
#
441+
# Disallow cplex 22.1.2 and 22.1.2.1, as they error fatally when
442+
# computing IIS on Python 3.13 and 3.14
443+
# Disallow cplex 12.9 (caused segfaults in tests)
444+
if [[ ${{matrix.python}} =~ 3.1[34] ]]; then
445+
CPLEX='cplex>=12.10,<22.1.2|>22.1.2.1'
446+
else
447+
CPLEX='cplex>=12.10'
448+
fi
430449
# xpress.init() (xpress 9.5.1 from conda) hangs indefinitely
431450
# on GHA/Windows under Python 3.10 and 3.11. Exclude that
432451
# release on that platform.
@@ -439,10 +458,7 @@ jobs:
439458
else
440459
XPRESS='xpress'
441460
fi
442-
TIMEOUT_MSG="TIMEOUT: killing conda install process"
443-
PYVER=$(echo "py${{matrix.python}}" | sed 's/\.//g')
444-
echo "Installing for $PYVER"
445-
for PKG in 'cplex>=12.10' docplex gurobi "$XPRESS" cyipopt pymumps scip; do
461+
for PKG in "$CPLEX" docplex gurobi "$XPRESS" cyipopt pymumps scip; do
446462
echo ""
447463
echo "*** Install $PKG ***"
448464
echo ""

doc/OnlineDocs/explanation/modeling/math_programming/parameters.rst

Lines changed: 159 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -8,42 +8,77 @@ Parameters
88
>>> import pyomo.environ as pyo
99
>>> model = pyo.ConcreteModel()
1010

11-
The word "parameters" is used in many settings. When discussing a Pyomo
12-
model, we use the word to refer to data that must be provided in order
13-
to find an optimal (or good) assignment of values to the decision
14-
variables. Parameters are declared as instances of a :class:`Param`
15-
class, which
16-
takes arguments that are somewhat similar to the :class:`Set` class. For
17-
example, the following code snippet declares sets ``model.A`` and
18-
``model.B``, and then a parameter ``model.P`` that is indexed by
19-
``model.A`` and ``model.B``:
11+
The word "parameters" is used in many settings. In Pyomo, a :class:`Param`
12+
represents the fixed data of an optimization model. Unlike variables
13+
(:class:`Var`), which the solver determines, parameters are inputs that define
14+
the specific instance of the problem you are solving.
15+
16+
Common examples of parameters include costs, demands, capacities, or physical
17+
constants. While you could use standard Python variables to store these values,
18+
using Pyomo :class:`Param` components offers several advantages:
19+
20+
* **Index Management:** Params can be indexed by Pyomo :class:`Set` objects,
21+
ensuring consistency between your data and the model structure. In a
22+
:class:`ConcreteModel`, they can also be indexed by standard Python iterables
23+
like lists, tuples, or ranges.
24+
* **Validation:** You can define rules to ensure that the data provided (e.g.,
25+
from an external file) is valid before solving.
26+
* **Symbolic Representation:** In large or complex models, using Params allows
27+
Pyomo to maintain the structure of the model separately from the specific values.
28+
29+
.. note::
30+
31+
When working with a :class:`ConcreteModel`, many modelers choose to use
32+
standard Python variables, lists, or dictionaries to store their data
33+
instead of Pyomo :class:`Param` objects. This is a common and valid
34+
practice.
35+
36+
However, you must use a Pyomo :class:`Param` if:
37+
38+
* You are using an :class:`AbstractModel` (which requires components to be
39+
declared before data is loaded).
40+
* You need a **mutable** parameter to change values and re-solve the model
41+
without the overhead of rebuilding it from scratch.
42+
* You want to leverage Pyomo's built-in data validation and index-checking
43+
capabilities.
44+
45+
Declaration and Options
46+
-----------------------
47+
48+
Parameters are declared as instances of the :class:`Param` class. They can be
49+
scalar (single value) or indexed by one or more sets (Pyomo :class:`Set` or
50+
other iterables). For example:
2051

2152
.. testcode::
2253

2354
model.A = pyo.RangeSet(1,3)
24-
model.B = pyo.Set()
55+
model.B = pyo.Set(initialize=['dog', 'cat'])
56+
# Scalar parameter
57+
model.rho = pyo.Param(initialize=0.5)
58+
# Indexed parameter (by Set)
2559
model.P = pyo.Param(model.A, model.B)
60+
# Indexed parameter (by standard list)
61+
model.Q = pyo.Param(['a', 'b', 'c'], initialize={'a': 1, 'b': 2, 'c': 3})
2662

27-
In addition to sets that serve as indexes, :class:`Param` takes
28-
the following options:
63+
If there are indexes for a :class:`Param`, they are provided as the first
64+
positional arguments and do not have a keyword label. In addition to these
65+
optional indexes, :class:`Param` takes the following keyword arguments:
2966

30-
- ``default`` = The parameter value absent any other specification.
67+
- ``default`` = The parameter value used if no other value is specified for an index.
3168
- ``doc`` = A string describing the parameter.
32-
- ``initialize`` = A function (or Python object) that returns data used to
33-
initialize the parameter values.
34-
- ``mutable`` = Boolean value indicating if the Param values are allowed
35-
to change after the Param is initialized.
36-
- ``validate`` = A callback function that takes the model, proposed
37-
value, and indices of the proposed value; returning ``True`` if the value
38-
is valid. Returning ``False`` will generate an exception.
39-
- ``within`` = Set used for validation; it specifies the domain of
40-
valid parameter values.
41-
42-
These options perform in the same way as they do for :class:`Set`. For
43-
example, given ``model.A`` with values ``{1, 2, 3}``, then there are many
44-
ways to create a parameter that represents a square matrix with 9, 16, 25 on the
45-
main diagonal and zeros elsewhere, here are two ways to do it. First using a
46-
Python object to initialize:
69+
- ``initialize`` = A function, dictionary, or other Python object used to
70+
provide initial data.
71+
- ``mutable`` = Boolean indicating if values can be changed after construction
72+
(see below).
73+
- ``validate`` = A callback function to verify data integrity.
74+
- ``within`` = A set (e.g., ``NonNegativeReals``) used for domain validation.
75+
76+
Basic Initialization
77+
--------------------
78+
79+
There are many ways to provide data to a :class:`Param`. For example, given
80+
``model.A`` with values ``{1, 2, 3}``, here are two ways to create a diagonal
81+
matrix:
4782

4883
.. testcode::
4984

@@ -53,9 +88,7 @@ Python object to initialize:
5388
v[3,3] = 25
5489
model.S1 = pyo.Param(model.A, model.A, initialize=v, default=0)
5590

56-
And now using an initialization function that is automatically called
57-
once for each index tuple (remember that we are assuming that
58-
``model.A`` contains ``{1, 2, 3}``)
91+
You can also use an initialization function that Pyomo calls for each index:
5992

6093
.. testcode::
6194

@@ -66,21 +99,17 @@ once for each index tuple (remember that we are assuming that
6699
return 0.0
67100
model.S2 = pyo.Param(model.A, model.A, initialize=s_init)
68101

69-
In this example, the index set contained integers, but index sets need
70-
not be numeric. It is very common to use strings.
71-
72102
.. note::
73103

74-
Data specified in an input file will override the data specified by
75-
the ``initialize`` option.
104+
In an :class:`AbstractModel`, data specified in an external input file (e.g.,
105+
a ``.dat`` file) will override the data specified by the ``initialize``
106+
option.
76107

77-
Parameter values can be checked by a validation function. In the
78-
following example, the every value of the parameter ``T`` (indexed by
79-
``model.A``) is checked
80-
to be greater than 3.14159. If a value is provided that is less than
81-
that, the model instantiation will be terminated and an error message
82-
issued. The validation function should be written so as to return
83-
``True`` if the data is valid and ``False`` otherwise.
108+
Validation
109+
----------
110+
111+
Parameter values can be checked by a validation function. In the following
112+
example, we ensure every value of ``model.T`` is greater than 3.14159:
84113

85114
.. testcode::
86115

@@ -91,12 +120,97 @@ issued. The validation function should be written so as to return
91120

92121
model.T = pyo.Param(model.A, validate=t_validate, initialize=t_data)
93122

94-
This example will prodice the following error, indicating that the value
95-
provided for ``T[2]`` failed validation:
123+
This example will produce the following error:
96124

97125
.. testoutput::
98126

99127
Traceback (most recent call last):
100128
...
101129
ValueError: Invalid parameter value: T[2] = '3', value type=<class 'int'>.
102130
Value failed parameter validation rule
131+
132+
Performance vs. Flexibility: Mutable Parameters
133+
-----------------------------------------------
134+
135+
By default, Pyomo parameters are **immutable** (``mutable=False``). This choice
136+
is driven by performance:
137+
138+
* **Immutable (Default):** Pyomo "pre-computes" these values into the algebraic
139+
expressions during model construction. This results in faster model generation
140+
and significantly lower memory usage, especially for large models. Key
141+
advantages include:
142+
143+
* **Memory Efficiency:** For indexed parameters, Pyomo avoids creating
144+
individual component data objects, significantly reducing memory overhead.
145+
* **Expression Speed:** Values are injected as constants directly into the
146+
expression tree. This allows Pyomo to optimize expression tree walking.
147+
* **Simplification:** Pyomo can simplify constant sub-expressions during
148+
model construction (e.g., ``5 * model.p * model.q[i]`` is simplified to a
149+
single float if ``p`` and ``q`` are immutable), further accelerating
150+
subsequent processing.
151+
* **Mutable:** Pyomo maintains the parameter as a symbolic object within
152+
expressions. This allows you to change the value and re-solve without
153+
rebuilding the entire model, but it adds computational overhead.
154+
155+
It is important to note that even immutable :class:`Param` objects carry some
156+
overhead. For the fastest possible model instantiation in a
157+
:class:`ConcreteModel`, using native Python data structures (like dictionaries
158+
or lists) to provide values directly into expressions is usually faster than
159+
using :class:`Param` components. However, as noted earlier, :class:`Param`
160+
provides benefits like validation and the ability to update values if declared
161+
as mutable.
162+
163+
When to use Mutable
164+
~~~~~~~~~~~~~~~~~~~
165+
166+
**Use Immutable if:**
167+
* The data is static and never changes during the lifetime of the model.
168+
* You want to maximize performance and minimize memory usage for large models.
169+
170+
**Use Mutable if:**
171+
* You are running a loop (e.g., sensitivity analysis) where you change
172+
parameter values and re-solve.
173+
* You want to update values frequently without the "re-construction"
174+
bottleneck.
175+
* The parameter is part of a nonlinear expression that you need to update.
176+
* You want named constants to be preserved in the Pyomo expressions (e.g., for documentation of debugging purposes)
177+
178+
Comparison: Param vs. Var
179+
-------------------------
180+
181+
It is common to confuse mutable parameters with variables. The following table
182+
summarizes the key differences:
183+
184+
.. list-table::
185+
:header-rows: 1
186+
187+
* - Feature
188+
- Param (Immutable)
189+
- Param (Mutable)
190+
- Var (fixed)
191+
- Var (free)
192+
* - Can change after model construction?
193+
- No
194+
- Yes
195+
- Yes
196+
- Yes
197+
* - Rebuilds model on change?
198+
- Yes (requires new Param)
199+
- No
200+
- No
201+
- No
202+
* - Solver sees it as:
203+
- A constant number
204+
- A constant number
205+
- A constant number
206+
- An optimization variable
207+
208+
.. note::
209+
210+
**Should I use a mutable Param or a fixed Var?**
211+
While functionally similar, you should use a :class:`Param` for data that
212+
defines the problem instance (like costs or demands) and a :class:`Var` for
213+
the decisions the solver needs to make. Use `fix()` on a :class:`Var` when
214+
you want to temporarily hold a decision constant, and use a mutable
215+
:class:`Param` when you need to update input data for sensitivity analysis
216+
or iterative algorithms.

doc/OnlineDocs/getting_started/solvers.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ the license requirements for their desired solver.
4444
* - cyipopt
4545
- ``pip install cyipopt``
4646
- ``conda install ‑c conda‑forge cyipopt``
47-
- `License <https://cyipopt.readthedocs.io/en/stable/#copyright>`__
48-
`Docs <https://cyipopt.readthedocs.io/en/stable/install.html>`__
47+
- `License <https://cyipopt.readthedocs.io/stable/#copyright>`__
48+
`Docs <https://cyipopt.readthedocs.io/stable/install.html>`__
4949
* - glpk
5050
- N/A
5151
- ``conda install ‑c conda‑forge glpk``

0 commit comments

Comments
 (0)