Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
179 changes: 134 additions & 45 deletions doc/OnlineDocs/explanation/modeling/math_programming/parameters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,42 +8,77 @@ Parameters
>>> import pyomo.environ as pyo
>>> model = pyo.ConcreteModel()

The word "parameters" is used in many settings. When discussing a Pyomo
model, we use the word to refer to data that must be provided in order
to find an optimal (or good) assignment of values to the decision
variables. Parameters are declared as instances of a :class:`Param`
class, which
takes arguments that are somewhat similar to the :class:`Set` class. For
example, the following code snippet declares sets ``model.A`` and
``model.B``, and then a parameter ``model.P`` that is indexed by
``model.A`` and ``model.B``:
The word "parameters" is used in many settings. In Pyomo, a :class:`Param`
represents the fixed data of an optimization model. Unlike variables
(:class:`Var`), which the solver determines, parameters are inputs that define
the specific instance of the problem you are solving.

Common examples of parameters include costs, demands, capacities, or physical
constants. While you could use standard Python variables to store these values,
using Pyomo :class:`Param` components offers several advantages:

* **Index Management:** Params can be indexed by Pyomo :class:`Set` objects,
ensuring consistency between your data and the model structure. In a
:class:`ConcreteModel`, they can also be indexed by standard Python iterables
like lists, tuples, or ranges.
* **Validation:** You can define rules to ensure that the data provided (e.g.,
from an external file) is valid before solving.
* **Symbolic Representation:** In large or complex models, using Params allows
Pyomo to maintain the structure of the model separately from the specific values.

.. note::

When working with a :class:`ConcreteModel`, many modelers choose to use
standard Python variables, lists, or dictionaries to store their data
instead of Pyomo :class:`Param` objects. This is a common and valid
practice.

However, you must use a Pyomo :class:`Param` if:

* You are using an :class:`AbstractModel` (which requires components to be
declared before data is loaded).
* You need a **mutable** parameter to change values and re-solve the model
without the overhead of rebuilding it from scratch.
* You want to leverage Pyomo's built-in data validation and index-checking
capabilities.

Declaration and Options
-----------------------

Parameters are declared as instances of the :class:`Param` class. They can be
scalar (single value) or indexed by one or more sets (Pyomo :class:`Set` or
other iterables). For example:

.. testcode::

model.A = pyo.RangeSet(1,3)
model.B = pyo.Set()
model.B = pyo.Set(initialize=['dog', 'cat'])
# Scalar parameter
model.rho = pyo.Param(initialize=0.5)
# Indexed parameter (by Set)
model.P = pyo.Param(model.A, model.B)
# Indexed parameter (by standard list)
model.Q = pyo.Param(['a', 'b', 'c'], initialize={'a': 1, 'b': 2, 'c': 3})

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

- ``default`` = The parameter value absent any other specification.
- ``default`` = The parameter value used if no other value is specified for an index.
- ``doc`` = A string describing the parameter.
- ``initialize`` = A function (or Python object) that returns data used to
initialize the parameter values.
- ``mutable`` = Boolean value indicating if the Param values are allowed
to change after the Param is initialized.
- ``validate`` = A callback function that takes the model, proposed
value, and indices of the proposed value; returning ``True`` if the value
is valid. Returning ``False`` will generate an exception.
- ``within`` = Set used for validation; it specifies the domain of
valid parameter values.

These options perform in the same way as they do for :class:`Set`. For
example, given ``model.A`` with values ``{1, 2, 3}``, then there are many
ways to create a parameter that represents a square matrix with 9, 16, 25 on the
main diagonal and zeros elsewhere, here are two ways to do it. First using a
Python object to initialize:
- ``initialize`` = A function, dictionary, or other Python object used to
provide initial data.
- ``mutable`` = Boolean indicating if values can be changed after construction
(see below).
- ``validate`` = A callback function to verify data integrity.
- ``within`` = A set (e.g., ``NonNegativeReals``) used for domain validation.

Basic Initialization
--------------------

There are many ways to provide data to a :class:`Param`. For example, given
``model.A`` with values ``{1, 2, 3}``, here are two ways to create a diagonal
matrix:

.. testcode::

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

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

.. testcode::

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

In this example, the index set contained integers, but index sets need
not be numeric. It is very common to use strings.

.. note::

Data specified in an input file will override the data specified by
the ``initialize`` option.
In an :class:`AbstractModel`, data specified in an external input file (e.g.,
a ``.dat`` file) will override the data specified by the ``initialize``
option.

Parameter values can be checked by a validation function. In the
following example, the every value of the parameter ``T`` (indexed by
``model.A``) is checked
to be greater than 3.14159. If a value is provided that is less than
that, the model instantiation will be terminated and an error message
issued. The validation function should be written so as to return
``True`` if the data is valid and ``False`` otherwise.
Validation
----------

Parameter values can be checked by a validation function. In the following
example, we ensure every value of ``model.T`` is greater than 3.14159:

.. testcode::

Expand All @@ -91,12 +120,72 @@ issued. The validation function should be written so as to return

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

This example will prodice the following error, indicating that the value
provided for ``T[2]`` failed validation:
This example will produce the following error:

.. testoutput::

Traceback (most recent call last):
...
ValueError: Invalid parameter value: T[2] = '3', value type=<class 'int'>.
Value failed parameter validation rule

Performance vs. Flexibility: Mutable Parameters
-----------------------------------------------

By default, Pyomo parameters are **immutable** (``mutable=False``). This choice
is driven by performance:

* **Immutable (Default):** Pyomo "pre-computes" these values into the algebraic
expressions during model construction. This results in faster model generation
and significantly lower memory usage, especially for large models.
* **Mutable:** Pyomo maintains the parameter as a symbolic object within
expressions. This allows you to change the value and re-solve without
rebuilding the entire model, but it adds computational overhead.

It is important to note that even immutable :class:`Param` objects carry some
overhead. For the fastest possible model instantiation in a
:class:`ConcreteModel`, using native Python data structures (like dictionaries
or lists) to provide values directly into expressions is usually faster than
using :class:`Param` components. However, as noted earlier, :class:`Param`
provides benefits like validation and the ability to update values if declared
as mutable.

When to use Mutable
~~~~~~~~~~~~~~~~~~~

**Use Immutable if:**
* The data is static and never changes during the execution of your script.
* You want to maximize performance and minimize memory usage for large models.

**Use Mutable if:**
* You are running a loop (e.g., sensitivity analysis) where you change
parameter values and re-solve.
* You want to update values frequently without the "re-construction"
bottleneck.
* The parameter is part of a nonlinear expression that you need to update.

Comparison: Param vs. Var
-------------------------

It is common to confuse mutable parameters with variables. The following table
summarizes the key differences:

.. list-table::
:header-rows: 1

* - Feature
- Param (Immutable)
- Param (Mutable)
- Var
* - Can change after solve()?
- No
- Yes
- Yes (by solver)
* - Rebuilds model on change?
- Yes (requires new Param)
- No
- No
* - Solver sees it as:
- A constant number
- A constant number
- An optimization variable
34 changes: 32 additions & 2 deletions doc/OnlineDocs/howto/manipulating.rst
Original file line number Diff line number Diff line change
Expand Up @@ -247,16 +247,46 @@ declared to be ``mutable`` (i.e., ``mutable=True``) with an
index that contains ``idx``, then the value in ``NewVal`` can be assigned to
it using

>>> instance.Theta[idx] = NewVal
>>> instance.Theta[idx] = NewVal

or, more explicitly using the ``set_value()`` method:

>>> instance.Theta[idx].set_value(NewVal)

For a singleton parameter named ``sigma`` (i.e., if it is not
indexed), the assignment can be made using

>>> instance.sigma = NewVal

or

>>> instance.sigma.set_value(NewVal)

Common Pitfalls: Updating Immutable Parameters
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

A common mistake is trying to update a parameter that was not declared as
mutable. For example:

.. code-block:: python

model.p = pyo.Param(initialize=10) # mutable=False by default
model.p = 5 # Raises TypeError

This will raise a ``TypeError`` because Pyomo has already "baked" the value
``10`` into the model's expressions. To allow updates, you **must** set
``mutable=True`` during declaration:

.. code-block:: python

model.p = pyo.Param(initialize=10, mutable=True)
model.p.set_value(5) # This works!

.. note::

If the ``Param`` is not declared to be mutable, an error will occur if an assignment to it is attempted.
While direct assignment (e.g., ``model.p = 5``) works for mutable parameters,
using ``set_value()`` is often clearer as it explicitly signals that you
are updating a Pyomo component rather than just a Python attribute.

For more information about access to Pyomo parameters, see the section
in this document on ``Param`` access :ref:`ParamAccess`. Note that for
Expand Down
Loading