The SIMUS tutorial

SIMUS (Sequential Interactive Model for Urban Systems)

Is a tool to aid decision-making problems with multiple objectives. The method solves successive scenarios formulated as linear programs. For each scenario, the decision-maker must choose the criterion to be considered objective while the remaining restrictions constitute the constrains system that the projects are subject to. In each case, if there is a feasible solution that is optimum, it is recorded in a matrix of efficient results. Then, from this matrix two rankings allow the decision maker to compare results obtained by different procedures. The first ranking is obtained through a linear weighting of each column by a factor - equivalent of establishing a weight - and that measures the participation of the corresponding project. In the second ranking, the method uses dominance and subordinate relationships between projects, concepts from the French school of MCDM.

The Case: Land rehabilitation

An important port city has been affected by the change in the modality of maritime transport, since the start of containers transport in the mid-20th century. The city was left with 39 hectares of empty docks, warehouses and a railway terminal.

Three projects was developed to decide what to do with this places

  • Project 1: Corporate towers - Hotels - Navy Base - Small park
  • Project 2: Habitational towers - Comercial Center in the old Railway terminal.
  • Project 3: Convention center - Big park and recreational area.

The criteria for the analysis of proposals are:

  1. New jobs positions (jobs).
  • Green spaces (green)
  • Financial feasibility (fin)
  • Environmental impact (env)

Only for the 2nd criteria a maximun limit pf \(500\) are provided. The decisor has the four criteria as goals, so 4 Linear Optimizations must be solved.

The data are provided in the next table:

Criteria Project 1 Project 2 Project 3 Right side value Optimal Sense
jobs 250 130 350
Maximize
green 120 200 340 500 Maximize
fin 20 40 15
Maximize
env 800 1000 600
Maximize

Data input

We can create a skcriteria.Data object with all this information (except the limits):

Note: SIMUS uses the alternatives as columns and the criteria as rows; but in scikit-criteria is the oposite, so expect to see the previous table transposed.
[1]:
# first lets import the DATA class
from skcriteria import Data

data = Data(
    # the alternative matrix
    mtx=[[250, 120, 20, 800],
         [130, 200, 40, 1000],
         [350, 340, 15, 600]],

    # optimal sense
    criteria=[max, max, min, max],

    # names of alternatives and criteria
    anames=["Prj 1", "Prj 2", "Prj 3"],
    cnames=["jobs", "green", "fin", "env"])

# show the data object
data
[1]:
ALT./CRIT. jobs (max) green (max) fin (min) env (max)
Prj 1 250 120 20 800
Prj 2 130 200 40 1000
Prj 3 350 340 15 600

Create the model

[2]:
# import the class
from skcriteria.madm.simus import SIMUS

# create the new simus and
dm = SIMUS()

By default the call SIMUS() create a solver that internally uses the PuLP solver to solve the linear programs. Other availables solvers are:

Note: The check the full list of available optimizers are stored in skcriteria.utils.lp.SOLVERS.

Also the njobs parameters determines how many cores the user want to use to run the linear programs. For example SIMUS(njobs=2) uses up to two cores. (By default all CPUs are used).

Also the last (and most important) parameter is rank_by (default is 1): determines which of the two ranks methods executed by SIMUS is the one that determines the final ranking. If the experiment is consistent, the two methos must detemines the same ranking (Please check the paper for more details).

Solve the problem

This is achived by calling the method decide() of the decision maker object (dm)

[3]:
# store the decision inside the dec variable
dec = dm.decide(data, b=[None, 500, None, None])

# let's see the decision
dec
[3]:

SIMUS (mnorm=none, wnorm=none) - Solution:

ALT./CRIT. jobs (max) green (max) fin (min) env (max) Rank
Prj 1 250 120 20 800 3
Prj 2 130 200 40 1000 2
Prj 3 350 340 15 600 1

If you check the last column the raking is:

  1. Project 3
  • Project 2
  • Project 1

Analysis

Most of the “intermediate” data of the SIMUS method are stored in the e_ field of the decision object dec.

[4]:
dec.e_
[4]:
Extra(rank_by, solver, stages, stage_results, points1, points2, tita_j_p, tita_j_d, doms, dom_by_crit)

for example the attribute stages stores all the Linear programs executed by SIMUS:

[5]:
dec._e.stages
[5]:
[no-name:
 MAXIMIZE
 250*x0 + 130*x1 + 350*x2 + 0
 SUBJECT TO
 _C1: 120 x0 + 200 x1 + 340 x2 <= 500

 _C2: 20 x0 + 40 x1 + 15 x2 >= 15

 _C3: 800 x0 + 1000 x1 + 600 x2 <= 1000

 VARIABLES
 x0 Continuous
 x0 Continuous
 x1 Continuous
 x1 Continuous
 x2 Continuous
 x2 Continuous, no-name:
 MAXIMIZE
 120*x0 + 200*x1 + 340*x2 + 0
 SUBJECT TO
 _C1: 250 x0 + 130 x1 + 350 x2 <= 350

 _C2: 20 x0 + 40 x1 + 15 x2 >= 15

 _C3: 800 x0 + 1000 x1 + 600 x2 <= 1000

 VARIABLES
 x0 Continuous
 x0 Continuous
 x1 Continuous
 x1 Continuous
 x2 Continuous
 x2 Continuous, no-name:
 MINIMIZE
 20*x0 + 40*x1 + 15*x2 + 0
 SUBJECT TO
 _C1: 250 x0 + 130 x1 + 350 x2 <= 350

 _C2: 120 x0 + 200 x1 + 340 x2 <= 500

 _C3: 800 x0 + 1000 x1 + 600 x2 <= 1000

 VARIABLES
 x0 Continuous
 x0 Continuous
 x1 Continuous
 x1 Continuous
 x2 Continuous
 x2 Continuous, no-name:
 MAXIMIZE
 800*x0 + 1000*x1 + 600*x2 + 0
 SUBJECT TO
 _C1: 250 x0 + 130 x1 + 350 x2 <= 350

 _C2: 120 x0 + 200 x1 + 340 x2 <= 500

 _C3: 20 x0 + 40 x1 + 15 x2 >= 15

 VARIABLES
 x0 Continuous
 x0 Continuous
 x1 Continuous
 x1 Continuous
 x2 Continuous
 x2 Continuous]

The attribute stages_results stores the eficients restults normalized matrix

[6]:
dec.e_.stage_results
[6]:
array([[0.125     , 0.        , 0.875     ],
       [0.        , 0.38888889, 0.61111111],
       [0.        , 0.        , 0.        ],
       [0.05681818, 0.94318182, 0.        ]])

References

Munier, N., Carignano, C., & Alberto, C. UN MÉTODO DE PROGRAMACIÓN MULTIOBJETIVO. Revista de la Escuela de Perfeccionamiento en Investigación Operativa, 24(39).
[7]:
import datetime as dt
import skcriteria
print("Scikit-Criteria version:", skcriteria.VERSION)
print("Running datetime:", dt.datetime.now())
Scikit-Criteria version: 0.2.10
Running datetime: 2019-08-27 19:50:11.820481