Skip to main content

StochOptim is a Stochastic Optimization package with scenario generation tools for two-stage and multi-stage problems

Project description

StochOptim Description

StochOptim is a Stochastic Optimization package that provides tools for formulating and solving two-stage and multi-stage problems.

Three main reasons why you would like to use it:

  • If you want an easy way to formulate your stochastic problem, represent its uncertainty via a scenario tree, and generate/solve the equivalent deterministic program.
  • If you want to build non-standard optimized scenario trees from given probability distributions (scenario generation)
  • If you want to build non-standard optimized scenario trees directly from historical data (scenario clustering)

By non-standard we mean scenario trees with non-homogeneous branching structures (which may depend on the stage and the scenarios).
By optimized we mean scenario trees with a branching structure that minimizes some criterion measuring its suitability to the problem.

The concept of building scenario trees suitable to the problem is gaining popularity over the past years. Suitability is measured by the ability of the equivalent deterministic program to provide optimal decisions that are as close as possible from those of the original problem. It falls under the umbrella of problem-driven methods, which are opposed to distribution-driven methods where suitability is defined with respect to the uncertainty and not the problem.

The tools implemented in this package have been developed in the papers:

for the scenario generation part, and in:

for the scenario clustering part.

Installation

PyPI

pip install stochoptim

Dependencies

  • docplex (version >= 2.9.133)

Basic Usage

Let's consider the following multistage stochastic optimization problem:

https://github.com/julienkeutchayan/StochOptim/blob/master/notebooks/Images/multistage_problem.PNG

To solve it:

  • First, we write the class for our problem and let it inherit from StochasticProblemBasis. Our class must define the decision variables, random parameters, objective function and constraints:
from stochoptim.stochprob.stochastic_problem_basis import StochasticProblemBasis

class MyMultistageStochasticProblem(StochasticProblemBasis):

    def __init__(self, T, A, B, c, d0):

        self.T = T         # last stage
        self.A = A         # A = {t: A[t] for t=0,...,T} where A[t] is a 2d-array
        self.B = B         # B = {t: B[t] for t=1,...,T} where B[t] is a 2d-array
        self.c = c         # c = {t: c[t] for t=0,...,T} where c[t] is a 1d-array
        self.d0 = d0       # 1d-array
        self.n = {t: A[t].shape[1] for t in range(self.T + 1)} # number of variables per stage
        self.m = {t: A[t].shape[0] for t in range(self.T + 1)} # number of constraints per stage

        StochasticProblemBasis.__init__(self, 
                                        name='Simple Example of Multistage Problem',
                                        n_stages=self.T + 1,        # number of stages 
                                        objective_sense='max',      # are we maximizing or minimizing?
                                        is_obj_random=False,        # does the objective function contain randomness?
                                        is_mip=False)               # does the problem include integer or binary variables?

    def decision_variables_definition(self, t):
        yield 'x', range(self.n[t]), 0, None, 'C'

    def random_variables_definition(self, t):
        if t >= 1:
            yield 'd', range(self.m[t])

    def objective(self):
        return self.sum([self.dot(self.c[t], self.get_dvar(t, 'x')) 
                         for t in range(self.T + 1)])                           # c[t].x[t] summed over t

    def deterministic_linear_constraints(self, t):
        if t == 0:
            yield iter(self.dot(self.A[t][i], self.get_dvar(t, 'x')) \
                       <= self.d0[i] for i in range(self.m[t]))                 # A[0].x[0] <= d[0]

    def random_linear_constraints(self, t):
        if t >= 1:
            yield iter(self.dot(self.A[t][i], self.get_dvar(t, 'x')) \
                       + self.dot(self.B[t][i], self.get_dvar(t-1, 'x')) \
                       <= self.get_rvar(t, 'd')[i] for i in range(self.m[t]))    # A[t].x[t] + B[t].x[t-1] <= d[t]

We instantiate the problem with the parameters of interest; for example for T = 2, 2 variables and 3 constraints per stage:

import numpy as np

T = 2

A = {0: np.array([[4, 1], [6, 4], [5, 9]]), 
     1: np.array([[2, 6], [7, 9], [8, 9]]), 
     2: np.array([[6, 3], [9, 2], [7, 5]])}

B = {1: np.array([[9, 5], [7, 5], [2, 7]]), 
     2: np.array([[9, 9], [2, 7], [1, 5]])}

c = {0: np.array([4, 3]), 
     1: np.array([-5,  4]), 
     2: np.array([-3,  1])}

d0 = np.array([8, 1, 1])

my_stochastic_problem = MyMultistageStochasticProblem(T, A, B, c, d0)
  • Then, we build the uncertainty of our problem using a scenario tree: (for the sake of simplicity we consider here a standard (non-optimized) tree with 2 branches per stage)
from stochoptim.scengen.scenario_tree import ScenarioTree
from stochoptim.scengen.scenario_process import ScenarioProcess

def scenario_fct(stage, epsilon, scenario_path):
    if stage >= 1:
        return {'d': np.random.uniform(1, 10, size=(3,))}

scenario_process = ScenarioProcess(scenario_fct, None)      # scenario generator
my_scenario_tree = ScenarioTree.from_bushiness([2,2])       # naked tree structure with 2 branches per stage
my_scenario_tree.fill(scenario_process)                     # tree structure filled with scenarios
  • Finally, we call the .solve() method of the problem on the scenario tree:
solution = my_stochastic_problem.solve(my_scenario_tree)
  • We can now plot the solutions:
import matplotlib.pyplot as plt

fig, axes = plt.subplots(ncols=5, figsize=(15, 5))
solution.scenario_tree.plot_scenarios('d', ax=axes[0])
axes[1].axis('off')
solution.scenario_tree.plot(lambda node: np.round(node.data['decision'].get('x'), 3), ax=axes[2])
axes[3].axis('off')
solution.scenario_tree.plot(lambda node: np.round(node.data['v'], 3), ax=axes[4])

https://github.com/julienkeutchayan/StochOptim/blob/master/notebooks/Images/scenario_tree_solution.png

Left: values and probabilities of the 3-dimensional random parameter 'd'.
Middle: optimal decisions 'x'.
Right: optimal objective values (at the root node and conditionally on the scenarios)

If you want to try other scenario trees, see notebook Basic Example

Tutorials and Examples

Case studies:

Scenario generation:

Scenario clustering:

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

stochoptim-0.0.12.tar.gz (87.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

stochoptim-0.0.12-py3-none-any.whl (102.5 kB view details)

Uploaded Python 3

File details

Details for the file stochoptim-0.0.12.tar.gz.

File metadata

  • Download URL: stochoptim-0.0.12.tar.gz
  • Upload date:
  • Size: 87.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/49.1.2 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.7.3

File hashes

Hashes for stochoptim-0.0.12.tar.gz
Algorithm Hash digest
SHA256 a4a086e799afdee7521171dfdf36a8866961673e1056b0569bd1e5c31af62495
MD5 b423b3fc10e9870b616a3f8dbd8a68a8
BLAKE2b-256 891402b5a8a8c744667caf11055c717db9c66390f3bb80cb7fceeb9d1e6b200b

See more details on using hashes here.

File details

Details for the file stochoptim-0.0.12-py3-none-any.whl.

File metadata

  • Download URL: stochoptim-0.0.12-py3-none-any.whl
  • Upload date:
  • Size: 102.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/49.1.2 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.7.3

File hashes

Hashes for stochoptim-0.0.12-py3-none-any.whl
Algorithm Hash digest
SHA256 a347d4f9ccbe825dce7bfa1e2435777002a68349cb6cbb27dd6be41b8b0af233
MD5 1c10e1b17e4803cb72ecce44d271f1ca
BLAKE2b-256 e7aab10cbe378c67a2dc022ccafeab246302e7b4a886019e164bbf48da6ab462

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page