Why OptiMods?

Diving head-first into Optimization can be daunting!


You should be proficient in:

  • maths
  • modeling
  • programming
  • data science

Drawbacks




Simplicity comes at the price of flexibility!

OptiMod: MetroMap


Idea

Create a schematic drawing of metro lines with stations and transfer stations

Input

  • stations with geographic positions
  • links between stations
  • (metro) lines (sequence of stations)

Output

  • positions of the stations for the schematic drawing
  • the drawing

Packages: networkx, pandas

octilinear representation

OptiMod: MetroMap

Some Background


Octilinear Representation

Edges in network are restricted to horizontals, verticals and diagonals at 45◦.


Objective

  • Planarity
  • The total length of all edges in the drawing should be small
  • The relative geographical position should be respected
  • Each metro line should have few line bends


Based on diploma thesis Automated Drawing of Metro Maps by M. Nöllenburg

Quick installation and usage instructions




  1. Installation via PyPI:

    pip install gurobi-optimods
  2. Documentation on your selected Mod:

https://gurobi-optimods.readthedocs.io/en/v2.2.0/mods/metromap.html

  1. Bring your data into the required format and run the Mod

Metromap Example

Use data of metro Munich

  • all data from wikipedia
    • stations with geographic coordinates
    • for each line sequence of stations

Metromap - Example

Prepare Data

  • csv files with station and link information:
number,name,posx,posy
1,Aidenbachstraße,11.525277777778,48.097777777778
2,Alte Heide,11.602777777778,48.178611111111
3,Am Hart,11.571944444444,48.196944444444
4,Am Knie,11.471388888889,48.145277777778
5,Arabellapark,11.621388888889,48.153611111111
6,Basler Straße,11.491666666667,48.091111111111
7,Bonner Platz,11.578888888889,48.166666666667
...
source,target
60,84
97,69
48,20
41,33
21,103
72,1
66,77
...
  • import data
import pandas as pd

node_data = pd.read_csv("data/muenchen_unodes.csv")
edge_data = pd.read_csv("data/muenchen_uedges.csv")

Metromap - Example

Prepare Data - Create Networkx Graph

import networkx as nx
# create graph
graph = nx.from_pandas_edgelist(edge_data.reset_index(), create_using=nx.Graph())
# add attribute 'pos' for the geographical position
for number, row in node_data.set_index("number").iterrows():
    graph.add_node(number, pos = (row['posx'], row['posy']))

# draw graph with geographical position
pos=nx.get_node_attributes(graph_out, "pos")
nx.draw(graph, pos, with_labels=False, node_size=12, edgecolors="black", node_color="white")

Metromap - Example

First Computation


Skip metro lines for now

from gurobi_optimods.metromap import metromap
graph_out, edge_directions = metromap(graph)
# Show the first 4 nodes with their attributes
first_four_nodes_with_attrs = list(graph_out.nodes.data())[:4]


[(60, {'pos': (11.598611111111, 48.135555555556), 'pos_oct': (12.0, 7.0)}),
 (84, {'pos': (11.606944444444, 48.139166666667), 'pos_oct': (13.0, 8.0)}),
 (97, {'pos': (11.642777777778, 48.094444444444), 'pos_oct': (16.0, 1.0)}),
 (69, {'pos': (11.645277777778, 48.088888888889), 'pos_oct': (17.0, 0.0)})]

Metromap - Example

First Computation - Plot Network


pos_oct=nx.get_node_attributes(graph_out, "pos_oct")
nx.draw(graph_out, pos_oct, with_labels=False, node_size=12, edgecolors="black", node_color="white")


Metromap - Example

Add Metro Lines

  • create csv file of following structure
linename,edge_source,edge_target
U1,77,27
U1,27,104
U1,104,28
U1,28,87
U1,87,56
...
U2,15,36
U2,36,13
U2,13,35
...

Metromap - Example

Computation and Plot Network

linepath_data = pd.read_csv("data/muenchen_ulinepaths.csv")
graph_out, edge_directions = metromap(graph, linepath_data)
pos_oct=nx.get_node_attributes(graph_out, "pos_oct")
nx.draw(graph_out, pos_oct, with_labels=False, node_size=12, edgecolors="black", node_color="white")


Metromap - Example

Solution - Plot Lines

from gurobi_optimods.metromap import plot_map
plot_map(graph_out, edge_directions, linepath_data)

Metromap - Example

Comparing with Official Line Network (Wikipedia)

  • individual design decisions (not covered)
  • competing objective, e.g., distance vs. line bends

Metromap - Example

Change Input Parameter

# default for all penalties in obj is 1, increase penalty for line bends
graph_out, edge_directions = metromap(graph, linepath_data, penalty_line_bends=5)
pos_oct=nx.get_node_attributes(graph_out, "pos_oct")
nx.draw(graph_out, pos_oct, with_labels=False, node_size=12, edgecolors="black", node_color="white")


line bend penalty 2

line bend penalty 5 (with the cost of distance)

Metromap - Example

Change Input Parameter 2

# default for all penalties in obj is 1, increase penalty for line bends
graph_out, edge_directions = metromap(graph, linepath_data, penalty_line_bends=2, penalty_distance=10, penalty_edge_directions=1)
pos_oct=nx.get_node_attributes(graph_out, "pos_oct")
nx.draw(graph_out, pos_oct, with_labels=False, node_size=12, edgecolors="black", node_color="white")


line bend penalty 2

line bend penalty 2 distance penalty 10

Metromap - Example

Change Solver Parameter


time limit can be changed directly:

graph_out, edge_directions = metromap(graph, linepath_data, time_limit=5)


other solver parameters:

solver_params = {
    "NoRelHeurTime": 5,
    "Cuts": 3,
    "TimeLimit": 5
    }
metromap(graph, linepath_data, solver_params=solver_params)


Details for every OptiMod in documentation

https://gurobi-optimods.readthedocs.io/en/v2.2.0/api.html

Summary and Conclusion

https://github.com/Gurobi/gurobi-optimods

  • OptiMods provide data-driven APIs to solve known problems as opt. problem
  • Great way to make optimization more accessible
  • Which problems / topics are still missing?

Tip

Get involved and share your feedback and ideas!

Thank You!