/
stack.jl
130 lines (115 loc) · 4.27 KB
/
stack.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import Base.vcat, Base.hcat, Base.hvcat
struct HcatAtom <: AbstractExpr
head::Symbol
id_hash::UInt64
children::Tuple
size::Tuple{Int, Int}
function HcatAtom(args::AbstractExpr...)
num_rows = args[1].size[1]
num_cols = 0
for arg in args
if arg.size[1] != num_rows
error("Cannot horizontally stack expressions of varying number of rows")
end
num_cols += arg.size[2]
end
children = tuple(args...)
return new(:hcat, hash(children), children, (num_rows, num_cols))
end
end
function sign(x::HcatAtom)
return sum(map(sign, x.children))
end
function monotonicity(x::HcatAtom)
return [Nondecreasing() for c in x.children]
end
function curvature(x::HcatAtom)
return ConstVexity()
end
function evaluate(x::HcatAtom)
return hcat(map(evaluate, x.children)...)
end
function conic_form!(x::HcatAtom, unique_conic_forms::UniqueConicForms)
if !has_conic_form(unique_conic_forms, x)
# build a list of child conic objectives and constraints
objectives = ConicObj[]
for child in x.children
push!(objectives, conic_form!(child, unique_conic_forms))
end
# build a dict from variable ids to sizes
variable_to_sizes = Dict{UInt64, Int}()
for objective in objectives
for id in keys(objective)
if !(id in keys(variable_to_sizes))
if id == objectid(:constant)
variable_to_sizes[id] = 1
else
variable_to_sizes[id] = length(unique_conic_forms.id_to_variables[id])
end
end
end
end
# Suppose the child objectives for two children e1 (2 x 1) and e2 (2 x 2) look something like
# e1: x => 1 2 3
# 4 5 6
# y => 2 4
# 7 8
# e2: x => 1 1 1
# 2 2 2
# 3 3 3
# 4 4 4
# The objective of [e1 e2] will look like
# x => 1 2 3
# 4 5 6
# 1 1 1
# 2 2 2
# 3 3 3
# 4 4 4
# y => 2 4
# 7 8
# 0 0
# 0 0
# 0 0
# 0 0
# builds the objective by aggregating a list of coefficients for each variable
# from each child objective, and then vertically concatenating them
objective = ConicObj()
for (id, col_size) in variable_to_sizes
#temp_tuple = Tuple{Value,Value}
x1_value_list = Value[]
x2_value_list = Value[]
for i in 1:length(objectives)
row_size = length(x.children[i])
if haskey(objectives[i], id)
push!(x1_value_list, objectives[i][id][1])
push!(x2_value_list, objectives[i][id][2])
else
push!(x1_value_list, spzeros(row_size, col_size))
push!(x2_value_list, spzeros(row_size, col_size))
end
end
x1 = vcat(x1_value_list...)
x2 = vcat(x2_value_list...)
objective[id] = (x1,x2)
end
cache_conic_form!(unique_conic_forms, x, objective)
end
return get_conic_form(unique_conic_forms, x)
end
hcat(args::AbstractExpr...) = HcatAtom(args...)
hcat(args::AbstractExprOrValue...) = HcatAtom(map(arg -> convert(AbstractExpr, arg), args)...)
hcat(args::Value...) = Base.cat(args..., dims=Val(2))
# TODO: implement vertical concatenation in a more efficient way
vcat(args::AbstractExpr...) = transpose(HcatAtom(map(transpose, args)...))
vcat(args::AbstractExprOrValue...) = transpose(HcatAtom(map(arg -> transpose(convert(AbstractExpr, arg)), args)...))
vcat(args::Value...) = Base.cat(args..., dims=Val(1)) # Note: this makes general vcat slower for anyone using Convex...
function hvcat(rows::Tuple{Vararg{Int}}, args::AbstractExprOrValue...)
nbr = length(rows)
rs = Vector{Any}(undef, nbr)
a = 1
for i = 1:nbr
rs[i] = hcat(args[a:a-1+rows[i]]...)
a += rows[i]
end
return vcat(rs...)
end