Skip to content

Commit 7e3e605

Browse files
authored
[docs] add a tutorial on typed indices (#4022)
1 parent c0b7ded commit 7e3e605

File tree

3 files changed

+117
-1
lines changed

3 files changed

+117
-1
lines changed

docs/make.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ if !_FAST
134134
joinpath("getting_started", "debugging.md"),
135135
joinpath("getting_started", "performance_tips.md"),
136136
joinpath("linear", "tips_and_tricks.md"),
137+
joinpath("linear", "typed_indices.md"),
137138
]
138139
filename = joinpath(@__DIR__, "src", "tutorials", file)
139140
content = read(filename, String)
@@ -352,6 +353,7 @@ const _PAGES = [
352353
"tutorials/linear/knapsack.md",
353354
"tutorials/linear/diet.md",
354355
"tutorials/linear/cannery.md",
356+
"tutorials/linear/typed_indices.md",
355357
"tutorials/linear/factory_schedule.md",
356358
"tutorials/linear/multi.md",
357359
"tutorials/linear/multi_commodity_network.md",

docs/src/manual/containers.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ julia> DataFrames.DataFrame(table)
259259
4 │ 2 B (2, :B)
260260
```
261261

262-
### Keyword indexing
262+
### [Keyword indexing](@id dense_keyword_indexing)
263263

264264
If all axes are named, you can use keyword indexing:
265265

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
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

Comments
 (0)