Skip to content

Commit a5c9ce0

Browse files
authored
Merge pull request #28 from TAMUparametric/easymodeler
Easier Interface for generating Models
2 parents 288bdf5 + b92394b commit a5c9ce0

File tree

17 files changed

+885
-611
lines changed

17 files changed

+885
-611
lines changed

doc/conf.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
author = "Dustin Kenefake, Efstratios Pistikopoulos"
3030

3131
# The full version, including alpha/beta/rc tags
32-
release = "1.4.0"
32+
release = "1.6.0"
3333

3434
# -- General configuration ---------------------------------------------------
3535

@@ -57,5 +57,5 @@
5757
# relative to this directory. They are copied after the builtin static files,
5858
# so a file named "default.css" will overwrite the builtin "default.css".
5959
html_static_path = ["_static"]
60-
source_suffix = [".rst", ".md"]
61-
nbsphinx_execute = "never"
60+
source_suffix = {".rst": 'restructuredtext', ".md": 'markdown'}
61+
nbsphinx_execute = "never"

doc/index.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ Tutorial
1616
control_allocation_example
1717
portfolio
1818
mpc
19-
mpoc_examples
2019

2120

2221
API

doc/mplp_tut.rst

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ This optimization problem leads to the following multiparametric optimization pr
2020
2121
Using PPOPT, this is translated as the following python code. (The latex above was generated for me with ``prog.latex()`` if you were wondering if I typed that all out by hand.)
2222

23-
.. code:: python
23+
.. code-block:: python
2424
2525
import numpy
2626
from ppopt.mpqp_program import MPLP_Program
@@ -35,6 +35,45 @@ Using PPOPT, this is translated as the following python code. (The latex above w
3535
3636
prog = MPLP_Program(A, b, c, H, CRa, CRb, F)
3737
38+
Alternatively, we can use ``MPModeler`` to build the program. This can be a more user-friendly way to build the program, and it is easier to read and understand. It does not require the user to specify the problem data as matrices, but uses an interface that is more similar to a mathematical formulation.
39+
40+
.. code-block:: python
41+
42+
from itertools import product
43+
from ppopt.mpmodel import MPModeler
44+
45+
model = MPModeler()
46+
47+
# define problem data
48+
factories = ['se', 'sd']
49+
markets = ['ch', 'to']
50+
capacities = {'se': 350, 'sd': 600}
51+
cost = {('se', 'ch'): 178, ('se', 'to'): 187, ('sd', 'ch'): 187, ('sd', 'to'): 151}
52+
53+
# make a variable for each factory-market production pair
54+
x = {(f, m): model.add_var(name=f'x[{f},{m}]') for f, m in product(factories, markets)}
55+
56+
# make a parameter for each market demand
57+
d = {m: model.add_param(name=f'd_[{m}]') for m in markets}
58+
59+
# bounds on the production capacity of each factory
60+
model.add_constrs(sum(x[f, m] for m in markets) <= capacities[f] for f in factories)
61+
62+
# demand satisfaction for each market
63+
model.add_constrs(sum(x[f, m] for f in factories) >= d[m] for m in markets)
64+
65+
# bounds on the parametric demand
66+
model.add_constrs(d[m] <= 1000 for m in markets)
67+
model.add_constrs(d[m] >= 0 for m in markets)
68+
69+
# non-negativity of the production variables
70+
model.add_constrs(x[f, m] >= 0 for f, m in product(factories, markets))
71+
72+
# set the objective to minimize the total cost
73+
model.set_objective(sum(cost[f, m] * x[f, m] for f, m in product(factories, markets)))
74+
75+
prog = model.formulate_problem()
76+
3877
3978
But before you go forward and solve this, I would always recommend processing the constraints. Removing all strongly and weakly redundant constraints and rescaling them leads to significant performance increases and robustifying the numerical stability. In PPOPT, processing the constraints is a simple task.
4079

@@ -57,15 +96,15 @@ This results in the following (identical) multiparametric optimization problem.
5796
5897
That wasn't that bad, and we were able to cut away some constraints that didn't matter in the process! Now we are ready to solve it. We import the solver functionalities and then specify an algorithm to use. Here we are specifying the combinatorial algorithm. Even though we are using the ``solve_mpqp`` function, this is also the main backend to solve mpLPs!
5998

60-
.. code:: python
99+
.. code-block:: python
61100
62101
from ppopt.mp_solvers.solve_mpqp import solve_mpqp, mpqp_algorithm
63102
solution = solve_mpqp(prog, mpqp_algorithm.combinatorial)
64103
65104
66105
Now we have the solution, we can either export the solution via the micropop module, or we can plot it. Let's plot it here. The extra arguments mean we are saving a picture of the plot and displaying it to the user (you can give a file path, so it saves somewhere that is not the current working directory).
67106

68-
.. code:: python
107+
.. code-block:: python
69108
70109
from ppopt.plot import parametric_plot
71110

doc/mpoc_examples.ipynb

Lines changed: 0 additions & 586 deletions
This file was deleted.

doc/portfolio.rst

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Here the covariance matrix and the return coefficients were generated from rando
2525
S = S@S.T / 10
2626
mu = numpy.random.rand(num_assets)/100
2727
28-
Here is the problem that we are going to be tackling in this post. Some of the constraints have been noticeably modified. This is due to a standard preprocessing pass that ``ppopt`` runs. This modification increases numerical stability for ill-conditioned optimization problems but has nearly for the problem we are looking at in this example, as it is numerically well conditioned.
28+
Here is the problem that we are going to be tackling in this tutorial. Some of the constraints have been noticeably modified. This is due to a standard preprocessing pass that ``ppopt`` runs. This modification increases numerical stability for ill-conditioned optimization problems but has nearly for the problem we are looking at in this example, as it is numerically well conditioned.
2929

3030
.. code-block:: python
3131
@@ -42,6 +42,39 @@ Here is the problem that we are going to be tackling in this post. Some of the c
4242
H = numpy.zeros((A.shape[1],F.shape[1]))
4343
portfolio = MPQP_Program(A, b, c, H, Q, A_t, b_t, F,equality_indices= [0,1])
4444
45+
Alternatively, we can use ``MPModeler`` to build the program. This can be a more user-friendly way to build the program, and it is easier to read and understand. It does not require the user to specify the problem data as matrices, but uses an interface that is more similar to a mathematical formulation.
46+
47+
.. code-block:: python
48+
49+
import numpy
50+
from ppopt.mpmodel import MPModeler
51+
52+
model = MPModeler()
53+
54+
# make a variable for each asset
55+
assets = [model.add_var(name=f'w[{i}]') for i in range(num_assets)]
56+
57+
# define the parametric return
58+
r = model.add_param(name='R')
59+
60+
# investment must add to one
61+
model.add_constr(sum(assets) == 1)
62+
63+
# the expected return must be r
64+
model.add_constr(sum(mu[i] * assets[i] for i in range(num_assets)) == r)
65+
66+
# all assets must be non-negative (no shorting)
67+
model.add_constrs(asset >= 0 for asset in assets)
68+
69+
# parametric return must be constrained to be [min(mu), max(mu)]
70+
model.add_constr(r >= min(mu))
71+
model.add_constr(r <= max(mu))
72+
73+
# set the objective to minimize the risk
74+
model.set_objective(sum(S[i, j] * assets[i] * assets[j] for i in range(num_assets) for j in range(num_assets)))
75+
76+
portfolio = model.formulate_problem()
77+
4578
This formulates the parametric problem as follows, we want to parameterize the return :math:`R^*` as :math:`\theta`, so that we can solve over all feasible bounds of return.
4679

4780
.. math::

doc/requirements.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@ sphinx_mdinclude
33
sphinx-rtd-theme
44
nbsphinx
55
ipykernel
6-
ipython
6+
ipython
7+
lxml[html_clean]
8+
pandoc

doc/tutorial.rst

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ This optimization problem leads to the following multiparametric optimization pr
1919
2020
Using PPOPT, this is translated as the following python code. (The latex above was generated for me with ``prog.latex()`` if you were wondering if I typed that all out by hand.)
2121

22-
.. code:: python
22+
.. code-block:: python
2323
2424
import numpy
2525
from ppopt.mpqp_program import MPQP_Program
@@ -36,9 +36,51 @@ Using PPOPT, this is translated as the following python code. (The latex above w
3636
prog = MPQP_Program(A, b, c, H, Q, CRa, CRb, F)
3737
3838
39+
Alternatively, we can use ``MPModeler`` to build the program. This can be a more user-friendly way to build the program, and it is easier to read and understand. It does not require the user to specify the problem data as matrices, but uses an interface that is more similar to a mathematical formulation.
40+
41+
.. code-block:: python
42+
43+
from itertools import product
44+
from ppopt.mpmodel import MPModeler
45+
46+
model = MPModeler()
47+
48+
# define problem data
49+
factories = ['se', 'sd']
50+
markets = ['ch', 'to']
51+
capacities = {'se': 350, 'sd': 600}
52+
cost = {('se', 'ch'): 178, ('se', 'to'): 187, ('sd', 'ch'): 187, ('sd', 'to'): 151}
53+
54+
# make a variable for each factory-market production pair
55+
x = {(f, m): model.add_var(name=f'x[{f},{m}]') for f, m in product(factories, markets)}
56+
57+
# make a parameter for each market demand
58+
d = {m: model.add_param(name=f'd_[{m}]') for m in markets}
59+
60+
# bounds on the production capacity of each factory
61+
model.add_constrs(sum(x[f, m] for m in markets) <= capacities[f] for f in factories)
62+
63+
# demand satisfaction for each market
64+
model.add_constrs(sum(x[f, m] for f in factories) >= d[m] for m in markets)
65+
66+
# bounds on the parametric demand
67+
model.add_constrs(d[m] <= 1000 for m in markets)
68+
model.add_constrs(d[m] >= 0 for m in markets)
69+
70+
# non-negativity of the production variables
71+
model.add_constrs(x[f, m] >= 0 for f, m in product(factories, markets))
72+
73+
# set the objective to minimize the total cost
74+
model.set_objective(sum(cost[f, m] * x[f, m] ** 2 + 25 * x[f, m] for f, m in product(factories, markets)))
75+
76+
prog = model.formulate_problem()
77+
78+
3979
But before you go forward and solve this, I would always recommend processing the constraints. Removing all strongly and weakly redundant constraints and rescaling them leads to significant performance increases and robustifying the numerical stability. In PPOPT, processing the constraints is a simple task.
4080

41-
.. code:: python
81+
82+
83+
.. code-block:: python
4284
4385
prog.process_constraints()
4486
@@ -57,15 +99,15 @@ This results in the following (identical) multiparametric optimization problem.
5799
58100
That wasn't that bad, and we were able to cut away some constraints that didn't matter in the process! Now we are ready to solve it. We import the solver functionalities and then specify an algorithm to use. Here we are specifying the combinatorial algorithm.
59101

60-
.. code:: python
102+
.. code-block:: python
61103
62104
from ppopt.mp_solvers.solve_mpqp import solve_mpqp, mpqp_algorithm
63105
solution = solve_mpqp(prog, mpqp_algorithm.combinatorial)
64106
65107
66108
Now we have the solution, we can either export the solution via the micropop module, or we can plot it. Let's plot it here. The extra arguments mean we are saving a picture of the plot and displaying it to the user (you can give a file path, so it saves somewhere that is not the current working directory).
67109

68-
.. code:: python
110+
.. code-block:: python
69111
70112
from ppopt.plot import parametric_plot
71113

environment.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,6 @@ dependencies:
2525
- ipykernel
2626
- ipython
2727
- daqp
28+
- lxml[html_clean]
29+
- pandoc
2830
prefix: /home/docs/.conda/envs/PPOPT

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from setuptools import find_packages, setup
22

3-
__version__ = "1.5.1"
3+
__version__ = "1.6.0"
44

55
short_desc = (
66
"Extensible Multiparametric Solver in Python"

src/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55
os.environ["OPENBLAS_NUM_THREADS"] = "1" # export OPENBLAS_NUM_THREADS=1
66
os.environ["MKL_NUM_THREADS"] = "1" # export MKL_NUM_THREADS=1
77
os.environ["VECLIB_MAXIMUM_THREADS"] = "1" # export VECLIB_MAXIMUM_THREADS=1
8-
os.environ["NUMEXPR_NUM_THREADS"] = "1" # export NUMEXPR_NUM_THREADS=1
8+
os.environ["NUMEXPR_NUM_THREADS"] = "1" # export NUMEXPR_NUM_THREADS=1

0 commit comments

Comments
 (0)