Skip to content

Instantly share code, notes, and snippets.

@yogabonito
Last active July 4, 2017 10:24
Show Gist options
  • Save yogabonito/27a25614182994f6de32deac0db6893f to your computer and use it in GitHub Desktop.
Save yogabonito/27a25614182994f6de32deac0db6893f to your computer and use it in GitHub Desktop.
Regionalization using exact optimization model called "Tree" in Duque et al. (2011)
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np\n",
"import libpysal as ps"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Making the lattice"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We are generating a 3x3 lattice: "
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"lat_w = ps.weights.util.lat2W(nrows=3, ncols=3, rook=True, id_type=\"int\")"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{0: [3, 1],\n",
" 1: [0, 4, 2],\n",
" 2: [1, 5],\n",
" 3: [0, 6, 4],\n",
" 4: [1, 3, 7, 5],\n",
" 5: [2, 4, 8],\n",
" 6: [3, 7],\n",
" 7: [4, 6, 8],\n",
" 8: [5, 7]}"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"neighbor_dict = lat_w.neighbors\n",
"neighbor_dict"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# lat_w.full()"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# networkx_graph = lat_w.to_networkx()\n",
"# networkx_graph.adj"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Adding data to the lattice (_hardcoded_)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{0: 726.7,\n",
" 1: 623.6,\n",
" 2: 487.3,\n",
" 3: 200.4,\n",
" 4: 245.0,\n",
" 5: 481.0,\n",
" 6: 170.9,\n",
" 7: 225.9,\n",
" 8: 226.9}"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"value_list = [726.7, 623.6, 487.3, 200.4, 245.0, 481.0, 170.9, 225.9, 226.9]\n",
"value_dict = {area: value for area, value in zip(neighbor_dict.keys(), value_list)}\n",
"value_dict"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Defining the optimization model from Duque et al. (2011): \"The p-Regions Problem\""
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"from pyomo.environ import RangeSet, Param, Var, \\\n",
" ConcreteModel, Binary, \\\n",
" PositiveIntegers, \\\n",
" NonNegativeIntegers, \\\n",
" Objective, minimize, \\\n",
" Constraint, \\\n",
" Set, value"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"model = ConcreteModel()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Parameters"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"model.n = Param(initialize=len(value_dict)) # number of areas\n",
"model.p = Param(initialize=2) # number of regions\n",
"# use value(model.n) to get the value of parameter n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"model.I = RangeSet(0, model.n-1)\n",
"\n",
"model.II = model.I * model.I\n",
"\n",
"II_upper_triangle = [(i, j) for i, j in model.II if i < j]\n",
"model.II_upper_triangle = Set(initialize=II_upper_triangle,\n",
" ordered=True)"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(1, 0)"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def c_init(model, i, j):\n",
" \"\"\"\\\n",
" Returns 1, if areas i and j share a border and\n",
" 0 otherwise.\n",
" \"\"\"\n",
" return int(i in neighbor_dict[j])\n",
"\n",
"model.c = Param(model.I, model.I, initialize=c_init)\n",
"\n",
"c_init(None, 1,2), c_init(None, 2,3)"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[1, 5]"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def N_init(model, i):\n",
" \"\"\"\\\n",
" Returns list of areas that are adjacent to \n",
" area i.\n",
" \"\"\"\n",
" return neighbor_dict[i]\n",
"\n",
"model.N = Param(model.I, initialize=N_init)\n",
"\n",
"N_init(None, 2)"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def dissim_measure(v1, v2):\n",
" \"\"\"\\\n",
" Returns the dissimilarity between the values v1 and\n",
" v2.\n",
" \"\"\"\n",
" return abs(v1 - v2)"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def d_init(model, i, j):\n",
" \"\"\"\\\n",
" Returns the dissimilarity between areas i and j.\n",
" \"\"\"\n",
" return dissim_measure(value_dict[i], \n",
" value_dict[j])\n",
"\n",
"model.d = Param(model.I, model.I, initialize=d_init)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Decision variables"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"model.t = Var(model.II, domain=Binary,\n",
" doc=\"1 if areas i and j belong to the \"\n",
" \"same region.\\n\"\n",
" \"0 otherwise.\")"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"model.x = Var(model.II, domain=Binary,\n",
" doc=\"1 if link (i, j) is selected as a \"\n",
" \"part of a tree.\\n\"\n",
" \"0 otherwise.\")"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"model.u = Var(model.I, domain=PositiveIntegers,\n",
" doc=\"Order assigned to each area in a \"\n",
" \"tree. This variable is used to build\"\n",
" \" constraints preventing cycles.\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Objective function"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# (3) in Duque et al. (2011): \"The p-Regions Problem\"\n",
"def obj_rule(model):\n",
" return sum(model.d[i,j] * model.t[i,j]\n",
" for i, j in model.II_upper_triangle)\n",
"\n",
"model.Obj = Objective(rule=obj_rule, sense=minimize)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Constraints"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# (4) in Duque et al. (2011): \"The p-Regions Problem\"\n",
"def _four(model):\n",
" return sum(model.x[i, j] \n",
" for i in model.I\n",
" for j in model.N[i]) == model.n - model.p\n",
"\n",
"model._four_constr = Constraint(rule=_four)"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# (5) in Duque et al. (2011): \"The p-Regions Problem\"\n",
"def _five(model, i):\n",
" return sum(model.x[i, j] for j in model.N[i]) <= 1\n",
"\n",
"model._five_constr = Constraint(model.I, rule=_five)"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# (6) in Duque et al. (2011): \"The p-Regions Problem\"\n",
"III_not_equal = [(i, j, m) \n",
" for i, j, m in model.I * model.I * model.I\n",
" if i != j and i != m and j != m]\n",
"model.III_not_equal = Set(initialize=III_not_equal, ordered=True)\n",
"\n",
"def _six(model, i, j, m):\n",
" return model.t[i, j] + model.t[i, m] - model.t[j, m] <= 1\n",
"\n",
"model._six_constr = Constraint(model.III_not_equal, rule=_six)"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# (7) in Duque et al. (2011): \"The p-Regions Problem\"\n",
"def _seven(model, i, j):\n",
" return model.t[i, j] - model.t[j, i] == 0\n",
"\n",
"model._seven_constr = Constraint(model.II, rule=_seven)"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def I_with_neighbors_init(model):\n",
" return ((i, ni) for i in model.I \n",
" for ni in model.N[i]) \n",
"\n",
"model.I_with_neighbors = Set(dimen=2,\n",
" initialize=I_with_neighbors_init)"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {
"collapsed": true,
"scrolled": false
},
"outputs": [],
"source": [
"# (8) in Duque et al. (2011): \"The p-Regions Problem\"\n",
"def _eight(model, i, j):\n",
" return model.x[i, j] <= model.t[i, j]\n",
"\n",
"model._eight_constr = Constraint(model.I_with_neighbors,\n",
" rule=_eight)"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# (9) in Duque et al. (2011): \"The p-Regions Problem\"\n",
"def _nine(model, i, j):\n",
" n, p, u, x = model.n, model.p, model.u, model.x\n",
" return u[i] - u[j] + (n-p) * x[i, j] \\\n",
" + (n-p-2) * x[j, i] <= model.n - model.p - 1 # typo in Duque et al.\n",
"\n",
"model._nine_constr = Constraint(model.I_with_neighbors,\n",
" rule=_nine)"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# (10) in Duque et al. (2011): \"The p-Regions Problem\"\n",
"def _ten(model, i):\n",
" return 1 <= model.u[i] <= model.n - model.p\n",
"\n",
"model._ten_constr = Constraint(model.I, rule=_ten)"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# (11) in Duque et al. (2011): \"The p-Regions Problem\"\n",
"# already in Var()-definition"
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# (12) in Duque et al. (2011): \"The p-Regions Problem\"\n",
"# already in Var()-definition"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Solve the optimization problem"
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {
"scrolled": false
},
"outputs": [],
"source": [
"from pyomo.opt import SolverFactory\n",
"opt = SolverFactory(\"cbc\")\n",
"results = opt.solve(model)"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([0, 0, 0, 1, 1, 0, 1, 1, 1], dtype=object)"
]
},
"execution_count": 30,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def find_index_of_sublist(el, lst):\n",
" for idx, sublst in enumerate(lst):\n",
" if el in sublst:\n",
" return idx\n",
" raise LookupError(\"{} not found any of the sublists of {}\".format(el, lst))\n",
"\n",
"# build a list of regions like [[0, 1, 2, 5], [3, 4, 6, 7, 8]]\n",
"idx_copy = model.I.data().copy()\n",
"num_regions = value(model.p)\n",
"regions = [[] for i in range(num_regions)]\n",
"for i in range(num_regions):\n",
" area = idx_copy.pop()\n",
" regions[i].append(area)\n",
" \n",
" for other_area in idx_copy:\n",
" if model.t[area, other_area].value == 1:\n",
" regions[i].append(other_area)\n",
" \n",
" idx_copy.difference_update(regions[i])\n",
"\n",
"# build a column with region information\n",
"region_col = np.arange(model.n)\n",
"for i in range(len(model.I)):\n",
" region_col[i] = find_index_of_sublist(i, regions)\n",
" \n",
"region_col"
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"area 0 is in region 0\n",
"area 1 is in region 0\n",
"area 2 is in region 0\n",
"area 3 is in region 1\n",
"area 4 is in region 1\n",
"area 5 is in region 0\n",
"area 6 is in region 1\n",
"area 7 is in region 1\n",
"area 8 is in region 1\n"
]
}
],
"source": [
"for i, region in enumerate(region_col):\n",
" print(\"area\", i, \"is in region\", region)"
]
},
{
"cell_type": "code",
"execution_count": 32,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"1222.8"
]
},
"execution_count": 32,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"value(model.Obj)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.5.2"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment