Skip to content

problems.dyn

Includes modified code from pymoo.

Sources:

Licensed under the Apache License, Version 2.0. Original copyright and license terms are preserved.

Classes

DynamicApplProblem

DynamicApplProblem(
    nt: int,
    taut: int,
    t0: int = 50,
    tau: int = 1,
    time: float | None = None,
    **kwargs,
)

Bases: DynamicProblem

Dynamic optimization problem for real-world applications.

This class defines dynamic optimization problems that model practical, real-world scenarios where the problem characteristics change systematically over time.

Parameters:

Name Type Description Default
nt int

Severity of change. Controls how significantly the problem changes at each change point. Higher values indicate more substantial changes in problem characteristics.

required
taut int

Frequency of change. Specifies how often (in generations) the problem undergoes changes. Lower values mean more frequent changes.

required
t0 int

The first change occurs after t0 generations, by default 50. That is, the generation at which a change occurs is (t0+1), (t0+taut+1), etc. This allows for an initial stabilization period before the first change.

50
tau int

Current simulation time counter (in generations), by default 1.

1
time float

Explicit simulation time value (overrides calculated time), by default None. Used for manual time control in specific scenarios.

None
**kwargs dict

Additional keyword arguments passed to the parent Problem class.

{}

Attributes:

Name Type Description
tau int

Current simulation time counter in generations.

nt int

Severity of change at each change point.

taut int

Frequency of change between consecutive changes.

t0 int

Initial stabilization period before first change occurs.

Notes

This class models real-world dynamic scenarios where:

  • Changes occur at predictable intervals (every taut generations)
  • Change severity is controlled by nt parameter
  • Initial period t0 allows for system stabilization
Source code in pydmoo/problems/dyn.py
def __init__(self, nt: int, taut: int, t0: int = 50, tau: int = 1, time: float | None = None, **kwargs):
    super().__init__(**kwargs)
    self.tau = tau  # time counter
    self.nt = nt  # severity of change
    self.taut = taut  # frequency of change
    self.t0 = t0  # Initial time offset
    self._time = time

Functions

update_to_next_time
update_to_next_time()

Advance problem to the next significant time step.

Returns:

Type Description
elapsed: The actual time units advanced
Source code in pydmoo/problems/dyn.py
def update_to_next_time(self):
    """Advance problem to the next significant time step.

    Returns
    -------
        elapsed: The actual time units advanced
    """
    # Calculate how many time steps to advance
    count = max((self.tau + self.taut - (self.t0 + 1)), 0) // self.taut

    # Calculate exact elapsed time needed to reach next discrete time point
    elapsed = int(count * self.taut + (self.t0 + 1) - self.tau)

    # Advance time by calculated amount
    self.tic(elapsed=elapsed)

    return elapsed

DynamicProblem

Bases: Problem, ABC

Abstract base class for dynamic optimization problems.

DynamicTestProblem

DynamicTestProblem(
    nt,
    taut,
    t0=50,
    tau=1,
    time=None,
    add_time_perturbation=False,
    **kwargs,
)

Bases: DynamicProblem

Dynamic optimization problem for testing and benchmarking.

Parameters:

Name Type Description Default
nt int

Severity of change. Controls how significantly the problem changes at each change point. Higher values indicate more substantial changes in problem characteristics.

required
taut int

Frequency of change. Specifies how often (in generations) the problem undergoes changes. Lower values mean more frequent changes.

required
t0 int

The first change occurs after t0 generations, by default 50. That is, the generation at which a change occurs is (t0+1), (t0+taut+1), etc. This allows for an initial stabilization period before the first change.

50
tau int

Current simulation time counter (in generations), by default 1.

1
time float

Explicit simulation time value (overrides calculated time), by default None. Used for manual time control in specific scenarios.

None
add_time_perturbation bool

If True, adds perturbations to the time calculation, by default False.

False
**kwargs dict

Additional keyword arguments passed to the parent Problem class.

{}

Attributes:

Name Type Description
tau int

Current simulation time counter in generations.

nt int

Severity of change at each change point.

taut int

Frequency of change between consecutive changes.

t0 int

Initial stabilization period before first change occurs.

add_time_perturbation bool

Flag indicating whether to add stochastic perturbations.

Notes

This class is designed for testing scenarios where:

  • Changes occur at predictable intervals (every taut generations)
  • Change severity is controlled by nt parameter
  • Initial period t0 allows for system stabilization
  • Stochastic perturbations can be added for more complex testing
  • Reproducibility is important for benchmarking
Source code in pydmoo/problems/dyn.py
def __init__(self, nt, taut, t0=50, tau=1, time=None, add_time_perturbation=False, **kwargs):
    super().__init__(**kwargs)
    self.tau = tau  # time counter
    self.nt = nt  # severity of change
    self.taut = taut  # frequency of change
    self.t0 = t0  # Initial time offset - added by DynOpt Team
    self._time = time

    self.add_time_perturbation = add_time_perturbation  # Stochastic perturbation flag - added by DynOpt Team

Attributes

time property writable
time

Time.

Notes

The discrete time \(t\) is defined as follows:

\[\begin{equation} t = \frac{1}{n_t} \left\lfloor \frac{\tau}{\tau_t} \right\rfloor + \frac{1}{n_t} \left(0.5 \times \frac{\pi_{\tau}}{9}\right), \ \tau = 0, 1, 2, \dots \end{equation}\]

Here, \(\pi_{\tau}\) is given by:

\[\begin{equation} \pi_{\tau} = \begin{cases} 0, & \text{if } \left\lfloor \frac{\tau}{\tau_t} \right\rfloor = 0, \\ \text{the } \left\lfloor \frac{\tau}{\tau_t} \right\rfloor\text{-th decimal digit of } \pi, & \text{otherwise.} \end{cases} \end{equation}\]

This formulation introduces a dynamic environment with an irregular change pattern. When \(\pi_{\tau} = 0\), the time variation reduces to the commonly used form with a regular change pattern:

\[\begin{equation} \label{eq:time_regular} t = \frac{1}{n_t} \left\lfloor \frac{\tau}{\tau_t} \right\rfloor, \ \tau = 0, 1, 2, \dots \end{equation}\]

In the above expressions, \(\tau\) denotes the generation counter, \(n_t\) controls the severity of change, and \(\tau_t\) represents the number of generations per time step.

Functions

update_to_next_time
update_to_next_time()

Advance problem to the next significant time step.

Returns:

Type Description
elapsed: The actual time units advanced
Source code in pydmoo/problems/dyn.py
def update_to_next_time(self):
    """Advance problem to the next significant time step.

    Returns
    -------
        elapsed: The actual time units advanced
    """
    # Calculate how many time steps to advance
    count = max((self.tau + self.taut - (self.t0 + 1)), 0) // self.taut

    # Calculate exact elapsed time needed to reach next discrete time point
    elapsed = int(count * self.taut + (self.t0 + 1) - self.tau)

    # Advance time by calculated amount
    self.tic(elapsed=elapsed)

    return elapsed

TimeSimulation

Bases: Callback

Callback for simulating time evolution in dynamic optimization problems.

Handles time-linkage properties and time step updates.

Functions

update
update(algorithm)

Update method called at each algorithm iteration.

Source code in pydmoo/problems/dyn.py
def update(self, algorithm):
    """Update method called at each algorithm iteration."""
    problem = algorithm.problem

    # Added by DynOpt Team
    # Emulate time-linkage property: Update problem state based on current optimal solutions
    # Must execute before the problem.tic() to ensure proper time sequencing
    if hasattr(problem, "time_linkage") and hasattr(problem, "cal"):
        # Calculate time-linkage effects using current optimal objective values
        problem.cal(algorithm.opt.get("F"))

    # Advance time step for dynamic problem simulation
    if hasattr(problem, "tic"):
        problem.tic()  # Progress the dynamic problem to next time step
    else:
        raise Exception("TimeSimulation can only be used for dynamic test problems.")