@@ -125,20 +125,6 @@ function delete_vars_by_pricing!(model, lb :: Float64)
125125end
126126=#
127127
128- # Execute Furini's 2016 Final Pricing. The returned value is a boolean list,
129- # with each index corresponding to a variable in all_variables(model),
130- # and with a true value if the variable should be kept, and false if it should
131- # be removed. The model parameter is expected to be a solved continuous
132- # relaxation of the problem (so the objective_value and the reduced_cost may
133- # be extracted); if this is not the case, pass the correct `ub` by means
134- # of the optional parameter.
135- function which_vars_to_keep (
136- vars, lb :: Float64 , ub :: Float64 = objective_value (model)
137- )
138- # We keep the ones equal to lb (not strictly necessary) to guarantee the
139- # warm start will work (the variables needed for it should be there).
140- end
141-
142128using JuMP
143129using TimerOutputs
144130import MathOptInterface
@@ -150,7 +136,7 @@ const MOI = MathOptInterface
150136# original plate; otherwise (2) a CutPattern of the original plate containing
151137# a CutPattern representing the piece.
152138function extraction_pattern (
153- bmr :: ByproductPPG2KP{D, S, P} , e_idx :: Int
139+ bmr :: ByproductPPG2KP{D, S, P} , e_idx
154140) :: CutPattern{D, S} where {D, S, P}
155141 pli, pii = bmr. np[e_idx] # the plate index and the piece index
156142 L, W = bmr. pli_lwb[pli] # the plate dimensions
@@ -160,22 +146,18 @@ function extraction_pattern(
160146end
161147
162148# INTERNAL METHOD USED ONLY IN get_cut_pattern
163- # Given a vector of JuMP variables, return a vector of their indexes in the
164- # original vector and another vector storing the rounded non-zero variable
165- # values. (The kwargs are passed to the `isapprox` method, to define what is
166- # considered to be "non-zero" in a floating-point world.)
167- # NOTE: takes the model only to be able to check if the variable is valid.
168- function gather_nonzero (
169- model :: JuMP.Model , vars :: Vector{JuMP.VariableRef} ; kwargs...
170- ) :: NTuple{2, Vector{Int}}
171- idxs = Vector {Int} ()
172- vals = Vector {Int} ()
173- for (idx, var) in enumerate (vars)
174- ! is_valid (model, var) && continue
175- val = value (var)
176- isapprox (val, 0.0 ; kwargs... ) && continue
149+ # Given a list of variables, returns two lists. The two lists have the same
150+ # length (that is itself smaller than the given list). The first list is
151+ # of indexes of the first list that have non-zero values (after aplying
152+ # RoundNearest to them) and the second is the rounded (to nearest) values.
153+ function gather_nonzero (vars, :: Type{D} ) where {D}
154+ idxs = Vector {eltype(keys(vars))} ()
155+ vals = Vector {D} ()
156+ for (idx, var) in pairs (vars)
157+ val = round (D, value (var), RoundNearest)
158+ iszero (val) && continue
177159 push! (idxs, idx)
178- push! (vals, round (Int, val, RoundNearest) )
160+ push! (vals, val)
179161 end
180162 idxs, vals
181163end
@@ -218,12 +200,12 @@ end
218200# cut indexes in topological ordering (any non-root cut over some plate only
219201# appears if a previous cut has made a copy of that plate type available).
220202function build_cut_idx_stack (
221- nz_cuts :: Vector{NTuple{3, P}} , qt_cuts :: Vector{D} , root_cut_idx :: Int
222- ) :: Vector{Int } where {D, P}
223- cut_idx_stack = Vector {Int } ()
203+ nz_cuts :: Vector{NTuple{3, P}} , qt_cuts :: Vector{D} , root_cut_idx :: P
204+ ) :: Vector{P } where {D, P}
205+ cut_idx_stack = Vector {P } ()
224206 push! (cut_idx_stack, root_cut_idx)
225207 cuts_available = deepcopy (qt_cuts)
226- next_cut = 1
208+ next_cut = one (P)
227209 while next_cut <= length (cut_idx_stack)
228210 _, fc, sc = nz_cuts[cut_idx_stack[next_cut]]
229211 @assert ! iszero (fc)
@@ -233,7 +215,7 @@ function build_cut_idx_stack(
233215 sc_idx = consume_cut (nz_cuts, cuts_available, sc)
234216 sc_idx != = nothing && push! (cut_idx_stack, sc_idx)
235217 end
236- next_cut += 1
218+ next_cut += one (P)
237219 end
238220
239221 return cut_idx_stack
@@ -248,16 +230,14 @@ end
248230# that are just piece extractions).
249231function add_used_extractions! (
250232 patterns :: Dict{Int64, Vector{CutPattern{D, S}}} ,
251- nzpe_idxs :: Vector{Int} ,
252- nzpe_vals :: Vector{Int} ,
253- bmr :: ByproductPPG2KP{D, S, P}
233+ nzpe_idxs, nzpe_vals, bmr :: ByproductPPG2KP{D, S, P}
254234) :: Dict{Int64, Vector{CutPattern{D, S}}} where {D, S, P}
255- for (i, np_idx) in enumerate (nzpe_idxs)
235+ for (i, np_idx) in pairs (nzpe_idxs)
256236 pli, pii = bmr. np[np_idx]
257- # @show np_idx
258- # @show pli, pii
259- # @show nzpe_vals[i]
260- # @show bmr.l[pii], bmr.w[pii]
237+ @show np_idx
238+ @show pli, pii
239+ @show nzpe_vals[i]
240+ @show bmr. l[pii], bmr. w[pii]
261241 extractions = extraction_pattern .(bmr, repeat ([np_idx], nzpe_vals[i]))
262242 if haskey (patterns, pli)
263243 append! (patterns[pli], extractions)
@@ -275,15 +255,15 @@ end
275255# (tree) bottom-up.
276256function bottom_up_tree_build! (
277257 patterns :: Dict{Int64, Vector{CutPattern{D, S}}} ,
278- nz_cut_idx_stack :: Vector{Int} ,
258+ nz_cut_idx_stack,
279259 nz_cuts :: Vector{NTuple{3, P}} ,
280260 nz_cuts_ori :: BitArray{1} ,
281261 bmr :: ByproductPPG2KP{D, S, P}
282262) :: Dict{Int64, Vector{CutPattern{D, S}}} where {D, S, P}
283263 for cut_idx in reverse (nz_cut_idx_stack)
284- # @show cut_idx
264+ @show cut_idx
285265 pp, fc, sc = nz_cuts[cut_idx]
286- # @show pp, fc, sc
266+ @show pp, fc, sc
287267 child_patts = Vector {CutPattern{D, S}} ()
288268 if ! iszero (fc) && haskey (patterns, fc) && ! isempty (patterns[fc])
289269 push! (child_patts, pop! (patterns[fc]))
@@ -294,7 +274,7 @@ function bottom_up_tree_build!(
294274 isempty (patterns[sc]) && delete! (patterns, sc)
295275 end
296276 ppl, ppw = bmr. pli_lwb[pp][1 ], bmr. pli_lwb[pp][2 ]
297- # @show ppl, ppw
277+ @show ppl, ppw
298278 ! haskey (patterns, pp) && (patterns[pp] = Vector {CutPattern{D, S}} ())
299279 push! (patterns[pp], CutPattern (
300280 ppl, ppw, nz_cuts_ori[cut_idx], child_patts
@@ -309,6 +289,8 @@ function get_cut_pattern(
309289 model_type :: Val{:PPG2KP} , model :: JuMP.Model , :: Type{D} , :: Type{S} ,
310290 build_model_return :: ByproductPPG2KP{D, S}
311291) :: CutPattern{D, S} where {D, S}
292+ # local constant to alternate debug mode (method will not take a debug flag)
293+ debug = false
312294 # 1. Check if there can be an extraction from the original plate to a
313295 # single piece. If it may and it happens, then just return this single
314296 # piece solution; otherwise the first cut is in `cuts_made`, find it
@@ -326,28 +308,23 @@ function get_cut_pattern(
326308 bmr = build_model_return
327309 pe = model[:picuts ] # Piece Extractions
328310 cm = model[:cuts_made ] # Cuts Made
329- # We need to define an absolute tolerance, as solvers often return values
330- # very similar to zero (when they should return zero) and this depends on
331- # the instance problem coefficients.
332- # MOI.get(backend(model), MOI.RelativeGap()) was initially used, but
333- # it is too much of a headache, GLPK sometimes does not believe it is
334- # available because we solved a linear model before.
335- atol = eps (objective_value (model)) # hope this is reasonable
336311
337312 # non-zero {piece extractions, cuts made} {indexes,values}
338- nzpe_idxs, nzpe_vals = gather_nonzero (model, pe; atol = atol)
339- nzcm_idxs, nzcm_vals = gather_nonzero (model, cm; atol = atol)
340- # @show nzpe_idxs
341- # @show nzpe_vals
342- # @show nzcm_idxs
343- # @show nzcm_vals
313+ nzpe_idxs, nzpe_vals = gather_nonzero (pe, D)
314+ nzcm_idxs, nzcm_vals = gather_nonzero (cm, D)
315+ if debug
316+ @show nzpe_idxs
317+ @show nzpe_vals
318+ @show nzcm_idxs
319+ @show nzcm_vals
320+ end
344321
345322 sps_idx = check_if_single_piece_solution (bmr. np, nzpe_idxs)
346323 ! iszero (sps_idx) && return extraction_pattern (bmr, sps_idx)
347324
348325 # The cuts actually used in the solution.
349326 sel_cuts = bmr. cuts[nzcm_idxs]
350- # @show sel_cuts
327+ debug && @show sel_cuts
351328 # If the cut in `sel_cuts` is vertical or not.
352329 ori_cuts = nzcm_idxs .>= bmr. first_vertical_cut_idx
353330 # The index of the root cut (cut over the original plate) in `sel_cuts`.
@@ -977,9 +954,16 @@ function reduced_profit(
977954 cut :: Tuple{P, P, P} , constraints :: Vector{T}
978955) :: Float64 where {P, T}
979956 pp, fc, sc = cut
957+ pp_dual = dual (constraints[pp])
958+ fc_dual = dual (constraints[fc])
980959 sc_dual = iszero (sc) ? 0.0 : dual (constraints[sc])
960+ # @show pp_dual
961+ # @show fc_dual
962+ # @show sc_dual
981963 # The reduced profit computation shown in the paper is done here.
982- return dual (constraints[pp]) - dual (constraints[fc]) - sc_dual
964+ rp = fc_dual + sc_dual - pp_dual
965+ # @show rp
966+ return rp
983967end
984968
985969function restricted_final_pricing (
@@ -1012,17 +996,14 @@ function restricted_final_pricing(
1012996 # Fix all unrestricted cuts (pool vars).
1013997 uc_vars = cm[uc_idxs]
1014998 fix .(uc_vars, 0.0 ; force = true )
1015- # Solve the relaxed restricted model.
1016- @timeit " relaxed_restricted" optimize! (model)
1017- # @show JuMP.dual_status(model)
1018- # @show JuMP.primal_status(model)
1019999 # Get the solution of the heuristic.
10201000 bkv, _, shelves = Heuristic. iterated_greedy (
10211001 d, p, bp. l, bp. w, bp. L, bp. W, rng
10221002 )
10231003 sol = Heuristic. shelves2cutpattern (shelves, bp. l, bp. w, bp. L, bp. W)
10241004 LB = convert (Float64, bkv)
1025- # @show LB
1005+ # Solve the relaxed restricted model.
1006+ @timeit " relaxed_restricted" optimize! (model)
10261007 # Check if everything seems ok with the values obtained.
10271008 if termination_status (model) == MOI. OPTIMAL
10281009 # @show objective_bound(model)
@@ -1043,13 +1024,24 @@ function restricted_final_pricing(
10431024 )
10441025 exit (1 )
10451026 end
1027+ # TODO : if the assert below fails, then the solver used reaches an optimal
1028+ # point of a LP but do not has duals for it, what should not be possible,
1029+ # some problem has happened (for an example, the solver does not recognize
1030+ # the model as a LP but think it is a MIP for example, CPLEX does this).
1031+ @assert has_duals (model)
10461032 rc_cuts = bp. cuts[rc_idxs]
10471033 plate_cons = all_constraints (
10481034 model, GenericAffExpr{Float64, VariableRef}, MOI. LessThan{Float64}
10491035 )
1036+ debug && begin
1037+ restricted_LB = LB
1038+ restricted_UB = UB
1039+ @show restricted_LB
1040+ @show restricted_UB
1041+ end
1042+ # Do the final pricing, get which variables should be removed.
10501043 # TODO : check if a tolerance is needed in the comparison. Query it from the
10511044 # solver/model if possible (or use eps?).
1052- # Do the final pricing, get which variables should be removed.
10531045 rc_fix_bitstr = @. floor (UB - reduced_profit (rc_cuts, (plate_cons,))) <= LB
10541046 debug && begin
10551047 restricted_vars_removed = sum (rc_fix_bitstr)
0 commit comments