Skip to content

Commit a325eb6

Browse files
odowlongemen3000
andauthored
Add DimensionalData extension (#3413)
Co-authored-by: longemen3000 <[email protected]> Co-authored-by: Andrés Riedemann <[email protected]>
1 parent 0ba286e commit a325eb6

File tree

8 files changed

+227
-4
lines changed

8 files changed

+227
-4
lines changed

Project.toml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,23 @@ Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
1212
SnoopPrecompile = "66db9d55-30c0-4569-8b51-7e840670fc0c"
1313
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
1414

15+
[weakdeps]
16+
DimensionalData = "0703355e-b756-11e9-17c0-8b28908087d0"
17+
18+
[extensions]
19+
JuMPDimensionalDataExt = "DimensionalData"
20+
1521
[compat]
22+
DimensionalData = "0.24"
1623
MathOptInterface = "1.18"
1724
MutableArithmetics = "1"
1825
OrderedCollections = "1"
1926
SnoopPrecompile = "1"
2027
julia = "1.6"
2128

2229
[extras]
30+
DimensionalData = "0703355e-b756-11e9-17c0-8b28908087d0"
2331
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
2432

2533
[targets]
26-
test = ["Test"]
34+
test = ["DimensionalData", "Test"]

docs/Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ CDDLib = "3391f64e-dcde-5f30-b752-e11513730f60"
33
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
44
Clarabel = "61c947e1-3e6d-4ee4-985a-eec8c727bd6e"
55
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
6+
DimensionalData = "0703355e-b756-11e9-17c0-8b28908087d0"
67
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
78
Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6"
89
Dualization = "191a621a-6537-11e9-281d-650236a99e60"
@@ -34,6 +35,7 @@ CDDLib = "=0.9.2"
3435
CSV = "0.10"
3536
Clarabel = "=0.5.1"
3637
DataFrames = "1"
38+
DimensionalData = "0.24"
3739
Documenter = "0.27.9, 0.28"
3840
Dualization = "0.5"
3941
GLPK = "=1.1.2"

docs/make.jl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,11 +187,16 @@ for (solver, data) in TOML.parsefile(joinpath(@__DIR__, "packages.toml"))
187187
end
188188
end
189189
push!(_LIST_OF_SOLVERS, "JuliaOpt/NLopt.jl" => "packages/NLopt.md")
190+
push!(
191+
_LIST_OF_EXTENSIONS,
192+
"rafaqz/DimensionalData.jl" => "extensions/DimensionalData.md",
193+
)
194+
190195
# Sort, with jump-dev repos at the start.
191196
sort!(_LIST_OF_SOLVERS; by = x -> (!startswith(x[1], "jump-dev/"), x[1]))
192197
sort!(_LIST_OF_EXTENSIONS; by = x -> (!startswith(x[1], "jump-dev/"), x[1]))
193198
pushfirst!(_LIST_OF_SOLVERS, "Introduction" => "packages/solvers.md")
194-
pushfirst!(_LIST_OF_EXTENSIONS, "Introduction" => "packages/extensions.md")
199+
pushfirst!(_LIST_OF_EXTENSIONS, "Introduction" => "extensions/introduction.md")
195200

196201
# ==============================================================================
197202
# JuMP API
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# DimensionalData.jl
2+
3+
[DimensionalData.jl](https://github.com/rafaqz/DimensionalData.jl) provides
4+
tools and abstractions for working with rectangular arrays that have named
5+
dimensions.
6+
7+
!!! compat
8+
Using the DimensionalData extension with JuMP requires Julia v1.9 or later.
9+
10+
The DimensionalData extension in JuMP lets you construct a `DimensionalData.DimArray`
11+
as an alternative to [`Containers.DenseAxisArray`](@ref) in the JuMP macros.
12+
13+
## License
14+
15+
DimensionalData.jl is licensed under the [MIT license](https://github.com/rafaqz/DimensionalData.jl/blob/main/LICENSE).
16+
17+
## Installation
18+
19+
Install DimensionalData using `Pkg.add`:
20+
21+
```julia
22+
import Pkg
23+
Pkg.add("DimensionalData")
24+
```
25+
26+
## Use with JuMP
27+
28+
Activate the extension by loading both JuMP and DimensionalData:
29+
30+
```jldoctest ext_dimensional_data
31+
julia> using JuMP, DimensionalData
32+
```
33+
34+
Then, pass `container = DimensionalData.DimArray` in the [`@variable`](@ref),
35+
[`@constraint`](@ref), or [`@expression`](@ref) macros:
36+
```jldoctest ext_dimensional_data
37+
julia> model = Model();
38+
39+
julia> @variable(
40+
model,
41+
x[i = 2:4, j = ["a", "b"]] >= i,
42+
container = DimensionalData.DimArray,
43+
)
44+
3×2 DimArray{VariableRef,2} with dimensions:
45+
Dim{:i} Sampled{Int64} 2:4 ForwardOrdered Regular Points,
46+
Dim{:j} Categorical{String} String["a", "b"] ForwardOrdered
47+
"a" "b"
48+
2 x[2,a] x[2,b]
49+
3 x[3,a] x[3,b]
50+
4 x[4,a] x[4,b]
51+
```
52+
53+
Here `x` is a `DimensionalData.Dim` array object, so indexing uses the
54+
DimensionalData syntax:
55+
```jldoctest ext_dimensional_data
56+
julia> x[At(2), At("a")]
57+
x[2,a]
58+
59+
julia> x[2, 2]
60+
x[3,b]
61+
```
62+
63+
You can use `container = DimensionalData.DimArray` in the [`@expression`](@ref)
64+
macro:
65+
```jldoctest ext_dimensional_data
66+
julia> @expression(
67+
model,
68+
expr[j = ["a", "b"]],
69+
sum(x[At(i), At(j)] for i in 2:4),
70+
container = DimensionalData.DimArray,
71+
)
72+
2-element DimArray{AffExpr,1} with dimensions:
73+
Dim{:j} Categorical{String} String["a", "b"] ForwardOrdered
74+
"a" x[2,a] + x[3,a] + x[4,a]
75+
"b" x[2,b] + x[3,b] + x[4,b]
76+
```
77+
and in [`@constraint`](@ref):
78+
```jldoctest ext_dimensional_data
79+
julia> @constraint(
80+
model,
81+
[j = ["a", "b"]],
82+
expr[At(j)] <= 1,
83+
container = DimensionalData.DimArray,
84+
)
85+
2-element DimArray{ConstraintRef{Model, MathOptInterface.ConstraintIndex{MathOptInterface.ScalarAffineFunction{Float64}, MathOptInterface.LessThan{Float64}}, ScalarShape},1} with dimensions:
86+
Dim{:j} Categorical{String} String["a", "b"] ForwardOrdered
87+
"a" x[2,a] + x[3,a] + x[4,a] ≤ 1
88+
"b" x[2,b] + x[3,b] + x[4,b] ≤ 1
89+
```
90+
91+
## Documentation
92+
93+
See the [DimensionalData.jl documentation](https://rafaqz.github.io/DimensionalData.jl/stable/)
94+
for more details on the syntax and features of `DimensionalData.DimArray`.

docs/src/packages/extensions.md renamed to docs/src/extensions/introduction.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,13 @@ README contents in the JuMP documentation for the benefit of users.
1919
Written an extension? Add it to this section of the JuMP documentation by making
2020
a pull request to the [`docs/packages.toml`](https://github.com/jump-dev/JuMP.jl/blob/master/docs/packages.toml)
2121
file.
22+
23+
## Weak dependencies
24+
25+
Some extensions listed in this section are implemented using the [weak dependency](https://pkgdocs.julialang.org/v1/creating-packages/#Weak-dependencies)
26+
feature added to Julia in v1.9. These extensions are activated if and only if
27+
you have `JuMP` and the other package loaded into your current scope with
28+
`using` or `import`.
29+
30+
!!! compat
31+
Using a weak dependency requires Julia v1.9 or later.

ext/JuMPDimensionalDataExt.jl

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Copyright 2017, Iain Dunning, Joey Huchette, Miles Lubin, and contributors
2+
# This Source Code Form is subject to the terms of the Mozilla Public
3+
# License, v. 2.0. If a copy of the MPL was not distributed with this
4+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
5+
6+
module JuMPDimensionalDataExt
7+
8+
import DimensionalData
9+
import JuMP
10+
11+
function JuMP.Containers.container(
12+
f::F,
13+
indices::JuMP.Containers.VectorizedProductIterator,
14+
c::Type{DimensionalData.DimArray},
15+
names::AbstractVector,
16+
) where {F<:Function}
17+
dims = NamedTuple(i => j for (i, j) in zip(names, indices.prod.iterators))
18+
return DimensionalData.DimArray(map(i -> f(i...), indices), dims)
19+
end
20+
21+
function JuMP.Containers.container(
22+
::Function,
23+
::JuMP.Containers.NestedIterator,
24+
::Type{DimensionalData.DimArray},
25+
::AbstractVector,
26+
)
27+
return error(
28+
"Unable to create a `DimArray` because the container does not form " *
29+
"a dense rectangular array",
30+
)
31+
end
32+
33+
end #module

ext/test_DimensionalData.jl

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Copyright 2017, Iain Dunning, Joey Huchette, Miles Lubin, and contributors
2+
# This Source Code Form is subject to the terms of the Mozilla Public
3+
# License, v. 2.0. If a copy of the MPL was not distributed with this
4+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
5+
6+
module TestContainersDimensionalData
7+
8+
using Test
9+
10+
using DimensionalData
11+
using JuMP
12+
13+
function test_dimension_data_variable()
14+
model = Model()
15+
@variable(model, x[i = 2:4, j = ["a", "b"]], container = DimArray)
16+
@test x isa DimArray
17+
@test x[At(2), At("a")] isa VariableRef
18+
@test JuMP.name(x[At(4), At("b")]) == "x[4,b]"
19+
@test @expression(model, sum(x[At(i), At("a")] for i in 2:4)) isa AffExpr
20+
@constraint(model, c, sum(x[At(i), At("a")] for i in 2:4) <= 1)
21+
@test c isa ConstraintRef
22+
return
23+
end
24+
25+
function test_dimension_data_expression()
26+
model = Model()
27+
B = ["a", "b"]
28+
@variable(model, x[i = 2:4, j = B], container = DimArray)
29+
@expression(
30+
model,
31+
expr[j = B],
32+
sum(x[At(i), At(j)] for i in 2:4),
33+
container = DimArray,
34+
)
35+
@test expr isa DimArray
36+
@test expr[At("a")] isa AffExpr
37+
return
38+
end
39+
40+
function test_dimensional_data_missing_names()
41+
model = Model()
42+
@test @variable(model, [1:3, 1:2], container = DimArray) isa DimArray
43+
@test @variable(model, [i = 1:3, 1:2], container = DimArray) isa DimArray
44+
@test @variable(model, [1:3, j = 1:2], container = DimArray) isa DimArray
45+
return
46+
end
47+
48+
function test_dimensional_data_sparse()
49+
model = Model()
50+
@test_throws(
51+
ErrorException(
52+
"Unable to create a `DimArray` because the container does not form " *
53+
"a dense rectangular array",
54+
),
55+
@variable(model, [i = 1:3, i:2], container = DimArray),
56+
)
57+
@test_throws(
58+
ErrorException(
59+
"Unable to create a `DimArray` because the container does not form " *
60+
"a dense rectangular array",
61+
),
62+
@variable(model, [i = 1:3; isodd(i)], container = DimArray),
63+
)
64+
return
65+
end
66+
67+
end

test/Kokako.jl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,12 @@ Calls `include_modules_to_test(dir)` where `dir` is the `/test` directory of the
144144
package `package`.
145145
"""
146146
function include_modules_to_test(package::Module)
147-
dir = joinpath(dirname(dirname(pathof(package))), "test")
148-
return include_modules_to_test(dir)
147+
root = dirname(dirname(pathof(package)))
148+
modules = include_modules_to_test(joinpath(root, "test"))
149+
if VERSION >= v"1.9"
150+
append!(modules, include_modules_to_test(joinpath(root, "ext")))
151+
end
152+
return modules
149153
end
150154

151155
end # module

0 commit comments

Comments
 (0)