-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathprocedures.jl
255 lines (195 loc) · 7.68 KB
/
procedures.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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
#import Pkg;
Pkg.add("GraphRecipes");
Pkg.add("Plots");
using GraphRecipes, Plots #only used to visualize the search tree at the end of the branch-and-bound
Pkg.add("Clp");
Pkg.add("JuMP");
using JuMP, Clp #to comment for the lab class
function readKnaptxtInstance(filename)
price=Int64[]
weight=Int64[]
KnapCap=Int64[]
open(filename) do f
for i in 1:3
tok = split(readline(f))
if(tok[1] == "ListPrices=")
for i in 2:(length(tok)-1)
push!(price,parse(Int64, tok[i]))
end
elseif(tok[1] == "ListWeights=")
for i in 2:(length(tok)-1)
push!(weight,parse(Int64, tok[i]))
end
elseif(tok[1] == "Capacity=")
push!(KnapCap, parse(Int64, tok[2]))
else
println("Unknown read :", tok)
end
end
end
capacity=KnapCap[1]
return price, weight, capacity
end
function testSondability_LP(model2, x, BestProfit, Bestsol)
TA, TO, TR = false, false, false
if(termination_status(model2) == MOI.INFEASIBLE)#Test de faisabilite
TA=true
println("TA")
elseif(objective_value(model2) <= BestProfit) #Test d'optimalite
TO=true
println("TO")
elseif( prod(abs.([round.(v, digits=0) for v in value.(x)]-value.(x)) .<= fill(10^-5, size(x)))
) #Test de resolution
TR=true
println("TR")
#if (value(benef) >= BestProfit)
if (objective_value(model2) >= BestProfit)
Bestsol = value.(x)
#BestProfit=value(benef)
BestProfit=objective_value(model2)
println("\nNew Solution memorized ", Bestsol, " with bestprofit ", BestProfit, "\n")
end
else
println("non sondable")
end
TA, TO, TR, Bestsol, BestProfit
end
function separateNodeThenchooseNext_lexicographic_depthfirst!(listobjs, listvals, n)
# this node is non-sondable. Apply the branching criterion to separate it into two subnodes
# and choose the child-node at the left
# lexicographic branching criterion: branch on the 1st object not yet fixed
i, obj = 1, 0
while((i <= n) && (obj==0))
if(!(i in listobjs))
obj=i
end
i+=1
end
println("\nbranch on object ", obj, "\n")
# depthfirst exploration strategy: the node selected will be the most left of the child-nodes just created
push!(listobjs,obj) #save the identity of the object selected for branching
push!(listvals,1.0) #save the node selected, identified by the value assigned to the variable/object chosen
end
function exploreNextNode_depthfirst!(listobjs, listvals, listnodes)
#this node is sondable, go back to parent node then right child if possible
stop=false
#check if we are not at the root node
if (length(listobjs)>= 1)
#go back to parent node
obj=pop!(listobjs)
theval=pop!(listvals)
tmp=pop!(listnodes)
#go to right child if possible, otherwise go back to parent
while( (theval==0.0) && (length(listobjs)>= 1))
obj=pop!(listobjs)
theval=pop!(listvals)
tmp=pop!(listnodes)
end
if theval==1.0
push!(listobjs,obj)
push!(listvals,0.0)
else
println("\nFINISHED")
stop=true
end
else
#the root node was sondable
println("\nFINISHED")
stop=true
end
return stop
end
function updateModele_LP!(model2, x, listobjs, listvals)
for i in 1:length(listobjs)
set_lower_bound(x[listobjs[i]],listvals[i])
set_upper_bound(x[listobjs[i]],listvals[i])
end
end
function reset_LP!(model2, x, listobjs)
for i in 1:length(listobjs)
set_lower_bound(x[listobjs[i]],0.0)
set_upper_bound(x[listobjs[i]],1.0)
end
end
function resetAll_LP!(model2, x)
for i in 1:length(x)
set_lower_bound(x[i],0.0)
set_upper_bound(x[i],1.0)
end
end
function createModel_LP(price, weight, capacity)
# ROOT NODE
n=length(price)
model2 = Model(Clp.Optimizer) # set optimizer
set_optimizer_attribute(model2, "LogLevel", 0) #don't display anything during solve
set_optimizer_attribute(model2, "Algorithm", 4) #LP solver chosen is simplex
# define x variables as CONTINUOUS (recall that it is not possible to define binary variables in Clp)
@variable(model2, 0 <= x[i in 1:n] <= 1)
# define objective function
@objective(model2, Max, sum(price[i]*x[i] for i in 1:n))
# define the capacity constraint
@constraint(model2, sum(weight[i]*x[i] for i in 1:n) <= capacity)
println(model2)
return model2, x
end
function solveKnapInstance(filename)
price, weight, capacity = readKnaptxtInstance(filename)
model2, x = createModel_LP(price, weight, capacity)
#create the structure to memorize the search tree for visualization at the end
trParentnodes=Int64[] #will store orig node of arc in search tree
trChildnodes=Int64[] #will store destination node of arc in search tree
trNamenodes=Int64[] #will store names of nodes in search tree
#intermediate structure to navigate in the search tree
listobjs=Int64[]
listvals=Float64[]
listnodes=Int64[]
BestProfit::Float64=-1.0
Bestsol=Float64[]
current_node_number::Int64=0
stop = false
while(!stop)
println("\nNode number ", current_node_number, ": \n---------------\n")
#Update the graphical tree
push!(trNamenodes,current_node_number+1)
if(length(trNamenodes)>=2)
push!(trParentnodes,listnodes[end]+1) # +1 because the 1st node is "node 0"
push!(trChildnodes, current_node_number+1) # +1 because the 1st node is "node 0"
end
push!(listnodes, current_node_number)
#create LP of current node
updateModele_LP!(model2, x, listobjs, listvals)
println(model2)
print("Solve the LP model of the current node to compute its bound: start ... ")
status = optimize!(model2)
println("... end");
print(": Solution LP")
if(termination_status(model2) == MOI.INFEASIBLE)#(has_values(model2))
print(" : NOT AVAILABLE (probably infeasible or ressources limit reached)")
else
print(" ", objective_value(model2))
[print("\t", name(v),"=",value(v)) for v in all_variables(model2)]
end
println(" ");
println("\nPrevious Solution memorized ", Bestsol, " with bestprofit ", BestProfit, "\n")
TA, TO, TR, Bestsol, BestProfit = testSondability_LP(model2, x, BestProfit, Bestsol)
is_node_sondable = TA || TO || TR
#reset_LP!(model2, x, listobjs)
if(!is_node_sondable)
separateNodeThenchooseNext_lexicographic_depthfirst!(listobjs, listvals, length(price))
else
stop = exploreNextNode_depthfirst!(listobjs, listvals, listnodes)
end
resetAll_LP!(model2, x)
current_node_number = current_node_number + 1
end
println("\n******\n\nOptimal value = ", BestProfit, "\n\nOptimal x=", Bestsol)
return BestProfit, Bestsol, trParentnodes, trChildnodes, trNamenodes
end
function solveNdisplayKnap(filename)
println("\n Branch-and-Bound for solving a knapsack problem. \n\n Solving instance '" * filename * "'\n")
BestProfit, Bestsol, trParentnodes, trChildnodes, trNamenodes = solveKnapInstance(filename)
println("\n******\n\nOptimal value = ", BestProfit, "\n\nOptimal x=", Bestsol)
println("\n Branch-and-bound tree visualization : start display ...")
display(graphplot(trParentnodes, trChildnodes, names=trNamenodes, method=:tree))
println("... end display. \n\n")
end