diff --git a/src/model.jl b/src/model.jl index b7d5801..ceb4b16 100644 --- a/src/model.jl +++ b/src/model.jl @@ -7,7 +7,7 @@ mutable struct ReverseManufacturingModel mip::JuMP.Model vars::DotDict arcs - decision_nodes + shipping_nodes process_nodes end @@ -49,11 +49,14 @@ end mutable struct Arc # Origin of the arc source::Node + # Destination of the arc dest::Node + # Costs dictionary. Each value in this dictionary is multiplied by the arc flow variable # and added to the objective function. costs::Dict + # Values dictionary. This dictionary is used to store extra information about the # arc. They are not used automatically by the model. values::Dict @@ -69,16 +72,16 @@ function build_model(instance::ReverseManufacturingInstance, println("Building optimization model...") mip = Model(optimizer) - decision_nodes, process_nodes, arcs = create_nodes_and_arcs(instance) + shipping_nodes, process_nodes, arcs = create_nodes_and_arcs(instance) - println(" $(length(decision_nodes)) decision nodes") + println(" $(length(shipping_nodes)) shipping nodes") println(" $(length(process_nodes)) process nodes") println(" $(length(arcs)) arcs") vars = DotDict() vars.flow = Dict(a => @variable(mip, lower_bound=0) for a in arcs) vars.node = Dict(n => @variable(mip, binary=true) for n in values(process_nodes)) - create_decision_node_constraints!(mip, decision_nodes, vars) + create_shipping_node_constraints!(mip, shipping_nodes, vars) create_process_node_constraints!(mip, process_nodes, vars) println(" Creating objective function...") @@ -96,12 +99,12 @@ function build_model(instance::ReverseManufacturingInstance, return ReverseManufacturingModel(mip, vars, arcs, - decision_nodes, + shipping_nodes, process_nodes) end -function create_decision_node_constraints!(mip, nodes, vars) - println(" Creating decision-node constraints...") +function create_shipping_node_constraints!(mip, nodes, vars) + println(" Creating shipping-node constraints...") for (id, n) in tqdm(nodes) @constraint(mip, sum(vars.flow[a] for a in n.incoming_arcs) + n.balance == @@ -125,18 +128,18 @@ end function create_nodes_and_arcs(instance) println(" Creating nodes and arcs...") arcs = Arc[] - decision_nodes = Dict() + shipping_nodes = Dict() process_nodes = Dict() # Create all nodes for (product_name, product) in instance.products - # Decision nodes for initial amounts + # Shipping nodes for initial amounts if haskey(product, "initial amounts") for location_name in keys(product["initial amounts"]) amount = product["initial amounts"][location_name]["amount"] n = Node(product_name, "Origin", location_name, balance=amount) - decision_nodes[n.product_name, n.plant_name, n.location_name] = n + shipping_nodes[n.product_name, n.plant_name, n.location_name] = n end end @@ -149,11 +152,11 @@ function create_nodes_and_arcs(instance) end end - # Decision nodes for each plant + # Shipping nodes for each plant for plant in product["output plants"] for location_name in keys(plant["locations"]) n = Node(product_name, plant["name"], location_name) - decision_nodes[n.product_name, n.plant_name, n.location_name] = n + shipping_nodes[n.product_name, n.plant_name, n.location_name] = n end end end @@ -168,7 +171,7 @@ function create_nodes_and_arcs(instance) for dest_plant in product["input plants"] for dest_location_name in keys(dest_plant["locations"]) dest_location = dest_plant["locations"][dest_location_name] - source = decision_nodes[product_name, "Origin", source_location_name] + source = shipping_nodes[product_name, "Origin", source_location_name] dest = process_nodes[product_name, dest_plant["name"], dest_location_name] distance = calculate_distance(source_location["latitude"], source_location["longitude"], @@ -193,7 +196,7 @@ function create_nodes_and_arcs(instance) # Process arcs (conversions within a plant) source = process_nodes[source_plant["input"], source_plant["name"], source_location_name] - dest = decision_nodes[product_name, source_plant["name"], source_location_name] + dest = shipping_nodes[product_name, source_plant["name"], source_location_name] costs = Dict() values = Dict("weight" => source_plant["outputs"][product_name]) a = Arc(source, dest, costs, values) @@ -205,7 +208,7 @@ function create_nodes_and_arcs(instance) for dest_plant in product["input plants"] for dest_location_name in keys(dest_plant["locations"]) dest_location = dest_plant["locations"][dest_location_name] - source = decision_nodes[product_name, source_plant["name"], source_location_name] + source = shipping_nodes[product_name, source_plant["name"], source_location_name] dest = process_nodes[product_name, dest_plant["name"], dest_location_name] distance = calculate_distance(source_location["latitude"], source_location["longitude"], @@ -223,7 +226,7 @@ function create_nodes_and_arcs(instance) end end end - return decision_nodes, process_nodes, arcs + return shipping_nodes, process_nodes, arcs end function calculate_distance(source_lat, source_lon, dest_lat, dest_lon)::Float64 @@ -336,8 +339,8 @@ function get_solution(instance::ReverseManufacturingInstance, for output_product_name in keys(plant["outputs"]) plant_loc_dict["total output"][output_product_name] = 0.0 plant_loc_dict["output"][output_product_name] = product_dict = Dict() - decision_node = model.decision_nodes[output_product_name, plant_name, location_name] - for a in decision_node.outgoing_arcs + shipping_node = model.shipping_nodes[output_product_name, plant_name, location_name] + for a in shipping_node.outgoing_arcs if vals[a] <= 0 continue end diff --git a/test/model_test.jl b/test/model_test.jl index d3354e5..e8e554d 100644 --- a/test/model_test.jl +++ b/test/model_test.jl @@ -8,17 +8,17 @@ using ReverseManufacturing, Cbc, JuMP, Printf model = ReverseManufacturing.build_model(instance, Cbc.Optimizer) # Verify nodes - @test ("P1", "Origin", "C1") in keys(model.decision_nodes) - @test ("P1", "Origin", "C3") in keys(model.decision_nodes) - @test ("P1", "Origin", "C8") in keys(model.decision_nodes) - @test ("P2", "F1", "L1") in keys(model.decision_nodes) - @test ("P2", "F1", "L2") in keys(model.decision_nodes) - @test ("P3", "F1", "L1") in keys(model.decision_nodes) - @test ("P3", "F1", "L2") in keys(model.decision_nodes) - @test ("P3", "F2", "L3") in keys(model.decision_nodes) - @test ("P3", "F2", "L4") in keys(model.decision_nodes) - @test ("P4", "F2", "L3") in keys(model.decision_nodes) - @test ("P4", "F2", "L4") in keys(model.decision_nodes) + @test ("P1", "Origin", "C1") in keys(model.shipping_nodes) + @test ("P1", "Origin", "C3") in keys(model.shipping_nodes) + @test ("P1", "Origin", "C8") in keys(model.shipping_nodes) + @test ("P2", "F1", "L1") in keys(model.shipping_nodes) + @test ("P2", "F1", "L2") in keys(model.shipping_nodes) + @test ("P3", "F1", "L1") in keys(model.shipping_nodes) + @test ("P3", "F1", "L2") in keys(model.shipping_nodes) + @test ("P3", "F2", "L3") in keys(model.shipping_nodes) + @test ("P3", "F2", "L4") in keys(model.shipping_nodes) + @test ("P4", "F2", "L3") in keys(model.shipping_nodes) + @test ("P4", "F2", "L4") in keys(model.shipping_nodes) @test ("P1", "F1", "L1") in keys(model.process_nodes) @test ("P1", "F1", "L2") in keys(model.process_nodes) @test ("P2", "F2", "L3") in keys(model.process_nodes) @@ -27,7 +27,7 @@ using ReverseManufacturing, Cbc, JuMP, Printf @test ("P4", "F3", "L5") in keys(model.process_nodes) # Verify some arcs - p1_orig_c1 = model.decision_nodes["P1", "Origin", "C1"] + p1_orig_c1 = model.shipping_nodes["P1", "Origin", "C1"] p1_f1_l1 = model.process_nodes["P1", "F1", "L1"] @test length(p1_orig_c1.outgoing_arcs) == 2 @test length(p1_f1_l1.incoming_arcs) == 10 @@ -38,7 +38,7 @@ using ReverseManufacturing, Cbc, JuMP, Printf @test round(arc.costs["transportation"], digits=2) == 1643.43 @test arc.costs["variable"] == 70.0 - p2_f1_l1 = model.decision_nodes["P2", "F1", "L1"] + p2_f1_l1 = model.shipping_nodes["P2", "F1", "L1"] p2_f2_l3 = model.process_nodes["P2", "F2", "L3"] @test length(p2_f1_l1.incoming_arcs) == 1 @test length(p2_f1_l1.outgoing_arcs) == 2