Skip to content

Commit 8938765

Browse files
authored
Merge pull request #11 from Robbybp/matrix
Add `incidence_matrix` function
2 parents 8342177 + 8a06fec commit 8938765

File tree

7 files changed

+191
-0
lines changed

7 files changed

+191
-0
lines changed

Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ BipartiteMatching = "79040ab4-24c8-4c92-950c-d48b5991a0f6"
88
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
99
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
1010
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
11+
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
1112

1213
[compat]
1314
julia = "1"

docs/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ makedocs(
3232
"reference/identify_variables.md",
3333
"reference/incidence_graph.md",
3434
"reference/interface.md",
35+
"reference/incidence_matrix.md",
3536
],
3637
],
3738
)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Incidence matrix
2+
3+
```@meta
4+
CurrentModule = JuMPIn
5+
```
6+
7+
```@docs
8+
incidence_matrix
9+
```

src/JuMPIn.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,8 @@ include("maximum_matching.jl")
3333
include("dulmage_mendelsohn.jl")
3434
include("interface.jl")
3535

36+
# Methods to get incidence matrices (as SparseMatrixCSC) from
37+
# JuMP models or incidence graphs
38+
include("incidence_matrix.jl")
39+
3640
end

src/incidence_matrix.jl

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# ___________________________________________________________________________
2+
#
3+
# JuMPIn.jl: JuMP Incidence Graph Analysis
4+
# Copyright (c) 2023. Triad National Security, LLC. All rights reserved.
5+
#
6+
# This program was produced under U.S. Government contract 89233218CNA000001
7+
# for Los Alamos National Laboratory (LANL), which is operated by Triad
8+
# National Security, LLC for the U.S. Department of Energy/National Nuclear
9+
# Security Administration. All rights in the program are reserved by Triad
10+
# National Security, LLC, and the U.S. Department of Energy/National Nuclear
11+
# Security Administration. The Government is granted for itself and others
12+
# acting on its behalf a nonexclusive, paid-up, irrevocable worldwide license
13+
# in this material to reproduce, prepare derivative works, distribute copies
14+
# to the public, perform publicly and display publicly, and to permit others
15+
# to do so.
16+
#
17+
# This software is distributed under the 3-clause BSD license.
18+
# ___________________________________________________________________________
19+
20+
"""
21+
Utility functions for getting the incidence graph of JuMP constraints and
22+
variables.
23+
24+
"""
25+
26+
import JuMP
27+
import SparseArrays
28+
import Graphs
29+
30+
import JuMPIn: get_bipartite_incidence_graph, IncidenceGraphInterface
31+
32+
"""
33+
incidence_matrix(constraints, variables)::SparseMatrixCSC
34+
35+
Return a `SparseMatrixCSC` with entries corresponding to incidence
36+
between the provided constraints and variables.
37+
38+
Rows correspond to constraints, columns correspond to variables, and
39+
matrix entries correspond to edges in the incidence graph. All matrix
40+
entries have values of `1.0`.
41+
42+
"""
43+
function incidence_matrix(
44+
constraints::Vector{<:JuMP.ConstraintRef},
45+
variables::Vector{JuMP.VariableRef},
46+
)::SparseArrays.SparseMatrixCSC
47+
graph, _, _ = get_bipartite_incidence_graph(constraints, variables)
48+
A, B, E = graph
49+
M = length(constraints)
50+
row = Vector{Int64}()
51+
col = Vector{Int64}()
52+
val = Vector{Float64}()
53+
for (i, j) in E
54+
# NOTE: Here we exploit the graph's convention. This will need to
55+
# change if we change how the graph is constructed.
56+
push!(row, i)
57+
push!(col, j - M)
58+
push!(val, 1.0)
59+
end
60+
return SparseArrays.sparse(row, col, val)
61+
end
62+
63+
"""
64+
incidence_matrix(igraph::IncidenceGraphInterface)::SparseMatrixCSC
65+
66+
Return the incidence matrix associated with the provided
67+
`IncidenceGraphInterface`.
68+
69+
"""
70+
function incidence_matrix(
71+
igraph::IncidenceGraphInterface
72+
)::SparseArrays.SparseMatrixCSC
73+
row = Vector{Int64}()
74+
col = Vector{Int64}()
75+
val = Vector{Float64}()
76+
M = length(igraph._con_node_map)
77+
for e in Graphs.edges(igraph._graph)
78+
i = Graphs.src(e)
79+
j = Graphs.dst(e)
80+
if typeof(igraph._nodes[i]) <: JuMP.VariableRef
81+
i, j = j, i
82+
end
83+
# Now we know that j is a variable node
84+
push!(row, i)
85+
# NOTE: Here we exploit the graph's convention. This will need to
86+
# change if we change how the graph is constructed, e.g. we add
87+
# a node for the objective.
88+
push!(col, j - M)
89+
push!(val, 1.0)
90+
end
91+
return SparseArrays.sparse(row, col, val)
92+
end

test/incidence_matrix.jl

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# ___________________________________________________________________________
2+
#
3+
# JuMPIn.jl: JuMP Incidence Graph Analysis
4+
# Copyright (c) 2023. Triad National Security, LLC. All rights reserved.
5+
#
6+
# This program was produced under U.S. Government contract 89233218CNA000001
7+
# for Los Alamos National Laboratory (LANL), which is operated by Triad
8+
# National Security, LLC for the U.S. Department of Energy/National Nuclear
9+
# Security Administration. All rights in the program are reserved by Triad
10+
# National Security, LLC, and the U.S. Department of Energy/National Nuclear
11+
# Security Administration. The Government is granted for itself and others
12+
# acting on its behalf a nonexclusive, paid-up, irrevocable worldwide license
13+
# in this material to reproduce, prepare derivative works, distribute copies
14+
# to the public, perform publicly and display publicly, and to permit others
15+
# to do so.
16+
#
17+
# This software is distributed under the 3-clause BSD license.
18+
# ___________________________________________________________________________
19+
20+
module TestIncidenceMatrix
21+
22+
import JuMP as jmp
23+
import MathOptInterface as moi
24+
import SparseArrays
25+
using Test: @test, @test_throws
26+
27+
import JuMPIn as ji
28+
29+
include("models.jl") # Models
30+
using .Models: make_degenerate_flow_model
31+
32+
33+
function test_incidence_matrix_from_constraints_and_variables()
34+
m = jmp.Model()
35+
@jmp.variable(m, x[1:3])
36+
@jmp.constraint(m, eq1, 2*x[1] + 3*x[2] == 4)
37+
@jmp.NLconstraint(m, eq2, 2*x[3]^1.5*x[2] == 1)
38+
constraints = [eq1, eq2]
39+
variables = [x[1], x[2], x[3]]
40+
imat = ji.incidence_matrix(constraints, variables)
41+
pred_row = [1, 1, 2, 2]
42+
pred_col = [1, 2, 2, 3]
43+
pred_val = [1.0, 1.0, 1.0, 1.0]
44+
m = 2
45+
n = 3
46+
pred_mat = SparseArrays.sparse(pred_row, pred_col, pred_val, m, n)
47+
@test imat == pred_mat
48+
end
49+
50+
function test_incidence_matrix_from_incidence_graph()
51+
m = jmp.Model()
52+
@jmp.variable(m, x[1:3])
53+
@jmp.constraint(m, eq1, 2*x[1] + 3*x[2] == 4)
54+
@jmp.NLconstraint(m, eq2, 2*x[3]^1.5*x[2] == 1)
55+
constraints = [eq1, eq2]
56+
variables = [x[3], x[2], x[1]]
57+
igraph = ji.IncidenceGraphInterface(constraints, variables)
58+
imat = ji.incidence_matrix(igraph)
59+
pred_row = [1, 1, 2, 2]
60+
pred_col = [3, 2, 2, 1]
61+
pred_val = [1.0, 1.0, 1.0, 1.0]
62+
m = 2
63+
n = 3
64+
pred_mat = SparseArrays.sparse(pred_row, pred_col, pred_val, m, n)
65+
@test imat == pred_mat
66+
end
67+
68+
69+
function runtests()
70+
test_incidence_matrix_from_constraints_and_variables()
71+
test_incidence_matrix_from_incidence_graph()
72+
return
73+
end
74+
75+
end # module TestIncidenceGraph
76+
77+
if abspath(PROGRAM_FILE) == @__FILE__
78+
TestIncidenceMatrix.runtests()
79+
end

test/runtests.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,9 @@ using Test
4242
TestInterface.runtests()
4343
end
4444

45+
@testset "IncidenceMatrix" begin
46+
include("incidence_matrix.jl")
47+
TestIncidenceMatrix.runtests()
48+
end
49+
4550
end

0 commit comments

Comments
 (0)