Skip to content

Commit 28bc985

Browse files
committed
2 parents 758f418 + ad9b19b commit 28bc985

File tree

5 files changed

+63
-32
lines changed

5 files changed

+63
-32
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "DisjunctiveProgramming"
22
uuid = "0d27d021-0159-4c7d-b4a7-9ccb5d9366cf"
33
authors = ["hdavid16 <[email protected]>"]
4-
version = "0.2.1"
4+
version = "0.3.0"
55

66
[deps]
77
IntervalArithmetic = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253"

src/DisjunctiveProgramming.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ using JuMP, IntervalArithmetic, Symbolics, Suppressor
44

55
export add_disjunction!, add_proposition!, reformulate_disjunction
66
export @disjunction, @proposition
7+
export choose!
78

89
include("constraint.jl")
910
include("logic.jl")

src/logic.jl

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,39 @@
1+
"""
2+
choose!(m::Model, n::Int, vars::VariableRef...; mode)
3+
4+
Add constraint to select n elements from the list of variables. Options for mode
5+
are `:at_least`, `:at_most`, `:exactly`.
6+
"""
7+
function choose!(m::Model, n::Int, vars::VariableRef...; mode=:exactly)
8+
@assert length(vars) >= n "Not enough variables passed."
9+
@assert all(is_valid.(m, vars)) "Invalid VariableRefs passed."
10+
add_selection!(m, n, vars...; mode)
11+
end
12+
function choose!(m::Model, vars::VariableRef...; mode=:exactly)
13+
@assert all(is_valid.(m, vars)) "Invalid VariableRefs passed."
14+
n = vars[1] #first variable is the n
15+
add_selection!(m, n, vars...; mode)
16+
end
17+
function add_selection!(m::Model, n, vars::VariableRef...; mode::Symbol)
18+
display(n)
19+
if mode == :exactly
20+
display(@constraint(m, sum(vars) == n))
21+
elseif mode == :at_least
22+
@constraint(m, sum(vars) n)
23+
elseif mode == :at_most
24+
@constraint(m, sum(vars) n)
25+
end
26+
end
27+
128
"""
229
to_cnf!(m::Model, expr::Expr)
330
431
Convert logical proposition expression into conjunctive normal form.
532
"""
633
function to_cnf!(m::Model, expr::Expr)
7-
expr_name = Symbol(expr) #get name to register reformulated logical proposition
34+
expr_name = Symbol("{$expr}") #get name to register reformulated logical proposition
835
replace_Symvars!(expr, m; logical_proposition = true) #replace all JuMP variables with Symbolic variables
9-
check_logical_proposition(expr) #check that valid boolean symbols and variables are used in the logical proposition
10-
eliminate_equivalence!(expr) #eliminate ⇔
11-
eliminate_implication!(expr) #eliminmate ⇒
12-
move_negations_inwards!(expr) #expand ¬
13-
clause_list = distribute_and_over_or_recursively!(expr) #distribute ∧ over ∨ recursively
14-
@assert !isempty(clause_list) "Conversion to CNF failed."
36+
clause_list = to_cnf!(expr)
1537
#replace symbolic variables with JuMP variables and boolean operators with their algebraic counterparts
1638
for clause in clause_list
1739
replace_JuMPvars!(clause, m)
@@ -29,6 +51,22 @@ function to_cnf!(m::Model, expr::Expr)
2951
end
3052
end
3153

54+
"""
55+
to_cnf!(expr::Expr)
56+
57+
Convert an expression of symbolic Boolean variables and operators to CNF.
58+
"""
59+
function to_cnf!(expr::Expr)
60+
check_logical_proposition(expr) #check that valid boolean symbols and variables are used in the logical proposition
61+
eliminate_equivalence!(expr) #eliminate ⇔
62+
eliminate_implication!(expr) #eliminmate ⇒
63+
move_negations_inwards!(expr) #expand ¬
64+
clause_list = distribute_and_over_or_recursively!(expr) #distribute ∧ over ∨ recursively
65+
@assert !isempty(clause_list) "Conversion to CNF failed."
66+
67+
return clause_list
68+
end
69+
3270
"""
3371
check_logical_proposition(expr::Expr)
3472
@@ -53,11 +91,13 @@ function eliminate_equivalence!(expr)
5391
if expr isa Expr
5492
if expr.args[1] == :
5593
@assert length(expr.args) == 3 "Double implication cannot have more than two clauses."
56-
A = expr.args[2]
57-
B = expr.args[3]
94+
A1 = expr.args[2]
95+
B1 = expr.args[3]
96+
A2 = A1 isa Expr ? copy(A1) : A1
97+
B2 = B1 isa Expr ? copy(B1) : B1
5898
expr.args[1] = :
59-
expr.args[2] = :($A $B)
60-
expr.args[3] = :($B $A)
99+
expr.args[2] = :($A1 $B1)
100+
expr.args[3] = :($B2 $A2)
61101
end
62102
for i in eachindex(expr.args)
63103
expr.args[i] = eliminate_equivalence!(expr.args[i])

src/macros.jl

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,14 @@ macro disjunction(m, args...)
5050
end
5151
end
5252

53-
#XOR constraint name
54-
xor_con = Symbol("XOR($disj_name)")
53+
# #XOR constraint name
54+
# xor_con = Symbol("XOR($disj_name)")
5555

5656
#build disjunction
5757
code = quote
5858
@assert !in($name, keys(object_dictionary($m))) "The disjunction name $name is already registered in the model. Specify new name."
5959
$m[$name] = @variable($m, [eachindex($disjunction)], Bin, base_name = string($name), lower_bound = 0, upper_bound = 1)
60-
@constraint($m, $xor_con, sum($m[$name]) == 1)
60+
# @constraint($m, $xor_con, sum($m[$name]) == 1)
6161
reformulate_disjunction($m, $(disjunction...); bin_var = $name, reformulation = $reformulation, param = $param)
6262
end
6363

@@ -114,15 +114,15 @@ function add_disjunction!(m::Model,disj...;reformulation::Symbol,M=missing,ϵ=1e
114114
bin_var = ismissing(name) ? Symbol("disj",gensym()) : name
115115
disj_name = ismissing(name) ? bin_var : Symbol("disj_",name)
116116

117-
#XOR constraint name
118-
xor_con = "XOR($disj_name)"
117+
# #XOR constraint name
118+
# xor_con = "XOR($disj_name)"
119119

120120
#apply reformulation
121121
@assert !in(bin_var, keys(object_dictionary(m))) "The disjunction name $bin_var is already registered in the model. Specify new name."
122122
#create indicator variable
123123
m[bin_var] = @variable(m, [eachindex(disj)], Bin, base_name = string(bin_var), lower_bound = 0, upper_bound = 1)
124-
#add xor constraint on binary variable
125-
m[Symbol(xor_con)] = @constraint(m, sum(m[bin_var]) == 1, base_name = xor_con)
124+
# #add xor constraint on binary variable
125+
# m[Symbol(xor_con)] = @constraint(m, sum(m[bin_var]) == 1, base_name = xor_con)
126126
#reformulate disjunction
127127
reformulate_disjunction(m, disj...; bin_var, reformulation, param)
128128
end
@@ -135,17 +135,7 @@ Add logical proposition macro.
135135
macro proposition(m, expr)
136136
#get args
137137
expr = QuoteNode(expr)
138-
code = :(add_proposition!($m, $expr))
139-
138+
code = :(DisjunctiveProgramming.to_cnf!($m, $expr))
139+
140140
return esc(code)
141141
end
142-
143-
"""
144-
add_proposition!(m::Model, expr::Expr)
145-
146-
Add logical proposition expression to a JuMP model.
147-
"""
148-
function add_proposition!(m::Model, expr::Expr)
149-
@assert m isa Model "A valid JuMP Model must be provided."
150-
to_cnf!(m, expr)
151-
end

src/reformulate.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ function reformulate_disjunction(m::Model, disj...; bin_var, reformulation, para
3030
new_constraints = Dict{Symbol,Any}(
3131
Symbol(bin_var,"[$i]") => disj[i] for i in eachindex(disj)
3232
)
33-
new_constraints[Symbol(bin_var,"_XOR")] = constraint_by_name(m, "XOR(disj_$bin_var)")
33+
# new_constraints[Symbol(bin_var,"_XOR")] = constraint_by_name(m, "XOR(disj_$bin_var)")
3434
if reformulation == :hull
3535
for var in m[:gdp_variable_refs]
3636
agg_con_name = "$(var)_$(bin_var)_aggregation"

0 commit comments

Comments
 (0)