|
| 1 | +# Copyright 2017, Iain Dunning, Joey Huchette, Miles Lubin, and contributors #src |
| 2 | +# This Source Code Form is subject to the terms of the Mozilla Public License #src |
| 3 | +# v.2.0. If a copy of the MPL was not distributed with this file, You can #src |
| 4 | +# obtain one at https://mozilla.org/MPL/2.0/. #src |
| 5 | + |
| 6 | +# # Strategies for dealing with many indices |
| 7 | + |
| 8 | +# Common modeling patterns may lead to variables that are defined over many |
| 9 | +# indices. For example, a transshipment model may have `x[p, f, m, t]` to |
| 10 | +# represent the flow of product `p` from factory `f` to market `m` in time `t`. |
| 11 | +# If each set is represented by a range of integers, it can be easy to make |
| 12 | +# little errors like `x[t, p, f, m]` that are hard to catch and debug. The |
| 13 | +# purpose of this tutorial is to explain how to used typed indices to avoid such |
| 14 | +# bugs. |
| 15 | + |
| 16 | +# ## Required packages |
| 17 | + |
| 18 | +# This tutorial uses the following packages: |
| 19 | + |
| 20 | +using JuMP |
| 21 | + |
| 22 | +# ## The problem |
| 23 | + |
| 24 | +# Consider a transshipment model of a single product from factories to markets. |
| 25 | +# One way to model the flow variable is: |
| 26 | + |
| 27 | +F, M = 3, 3 |
| 28 | +model = Model() |
| 29 | +@variable(model, x[1:F, 1:M] >= 0) |
| 30 | + |
| 31 | +# Now the question is: what does `x[1, 2]` represent? Was it `x[f, m]` or |
| 32 | +# `x[m, f]`? In this simple example, it's probably easy to remember the flow |
| 33 | +# _from_ a factory _to_ a market, but it gets harder if there are more sets. Was |
| 34 | +# time the first index or the last? |
| 35 | + |
| 36 | +# Mis-typing the order of two sets is a common error, and it can result in bugs |
| 37 | +# that are surprisingly hard to find and debug. If you didn't already know, |
| 38 | +# would you be able to spot the difference in these two constraints, especially |
| 39 | +# if the constraint expression was large or you were looking through many |
| 40 | +# constraints trying to find out why the solution wasn't what you expected? |
| 41 | + |
| 42 | +@constraint(model, [f in 1:F], sum(x[f, m] for m in 1:M) == 1) |
| 43 | + |
| 44 | +#- |
| 45 | + |
| 46 | +@constraint(model, [f in 1:F], sum(x[m, f] for m in 1:M) == 1) |
| 47 | + |
| 48 | +# There are two approaches you can use to avoid bugs like `x[m, f]`: keyword |
| 49 | +# indexing and typed indices. |
| 50 | + |
| 51 | +# ## Keyword indexing |
| 52 | + |
| 53 | +# The first approach is to use [keyword indexing](@ref dense_keyword_indexing). |
| 54 | +# Because keyword indexing does not work for `Array` containers, we must |
| 55 | +# explicitly choose the `DenseAxisArray` container. |
| 56 | + |
| 57 | +F, M = 3, 3 |
| 58 | +model = Model() |
| 59 | +@variable(model, x[f in 1:F, m in 1:M] >= 0, container = DenseAxisArray) |
| 60 | + |
| 61 | +# Accessing `x` in the correct order works: |
| 62 | + |
| 63 | +x[f = 1, m = 2] |
| 64 | + |
| 65 | +# But the wrong order errors: |
| 66 | + |
| 67 | +try #hide |
| 68 | + x[m = 2, f = 1] |
| 69 | +catch err #hide |
| 70 | + showerror(stderr, err) #hide |
| 71 | +end #hide |
| 72 | + |
| 73 | +# Keyword indexing means we can write constraints like this: |
| 74 | + |
| 75 | +@constraint(model, [f in 1:F], sum(x[f = f, m = m] for m in 1:M) == 1) |
| 76 | + |
| 77 | +# ## Typed indices |
| 78 | + |
| 79 | +# A second approach is to define new types for each index: |
| 80 | + |
| 81 | +struct Factory |
| 82 | + x::Int |
| 83 | +end |
| 84 | + |
| 85 | +struct Market |
| 86 | + x::Int |
| 87 | +end |
| 88 | + |
| 89 | +# Then, we define each axis as a vector of these types: |
| 90 | + |
| 91 | +factories = Factory.(1:F) |
| 92 | +markets = Market.(1:M) |
| 93 | +model = Model() |
| 94 | +@variable(model, x[factories, markets] >= 0) |
| 95 | + |
| 96 | +# Accessing `x` in the correct order works: |
| 97 | + |
| 98 | +x[Factory(1), Market(2)] |
| 99 | + |
| 100 | +# But the wrong order errors: |
| 101 | + |
| 102 | +try #hide |
| 103 | + x[Market(2), Factory(1)] |
| 104 | +catch err #hide |
| 105 | + showerror(stderr, err) #hide |
| 106 | +end #hide |
| 107 | + |
| 108 | +# Typed indices means we can write constraints like this: |
| 109 | + |
| 110 | +@constraint(model, [f in 1:F], sum(x[Factory(f), Market(m)] for m in 1:M) == 1) |
| 111 | + |
| 112 | +# or like this: |
| 113 | + |
| 114 | +@constraint(model, [f in factories], sum(x[f, m] for m in markets) == 1) |
0 commit comments