-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
150 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
add_subdirectory(branch-and-bound) | ||
add_subdirectory(branch-and-price) | ||
add_subdirectory(modeling) | ||
add_subdirectory(wrappers) | ||
add_subdirectory(wrappers) | ||
add_subdirectory(dualizer) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# Knapsack Problem | ||
foreach (OPTIMIZER ${AVAILABLE_MILP_SOLVERS}) | ||
|
||
set(target_name "test_dualizer_location_${OPTIMIZER}") | ||
|
||
add_executable(${target_name} | ||
location.cpp | ||
../../cartesian_product.h | ||
) | ||
|
||
target_compile_definitions(${target_name} PRIVATE OPTIMIZER=${OPTIMIZER}) | ||
target_link_libraries(${target_name} PRIVATE idol Catch2::Catch2WithMain) | ||
|
||
endforeach () |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
// | ||
// Created by henri on 22.10.23. | ||
// | ||
|
||
#include <catch2/catch_all.hpp> | ||
#include "../../cartesian_product.h" | ||
#include "idol/modeling.h" | ||
#include "idol/mixed-integer/problems/facility-location-problem/FLP_Instance.h" | ||
#include "idol/mixed-integer/optimizers/wrappers/GLPK/GLPK.h" | ||
#include "idol/mixed-integer/optimizers/wrappers/Mosek/Mosek.h" | ||
#include "idol/mixed-integer/optimizers/wrappers/Gurobi/Gurobi.h" | ||
#include "idol/mixed-integer/optimizers/wrappers/HiGHS/HiGHS.h" | ||
#include "idol/mixed-integer/modeling/models/Dualizer.h" | ||
|
||
using namespace Catch::literals; | ||
using namespace idol; | ||
|
||
enum Reformulation { | ||
Dual, | ||
StrongDuality, | ||
KKT | ||
}; | ||
|
||
std::string to_string(Reformulation reformulation) { | ||
switch (reformulation) { | ||
case Dual: return "dual"; | ||
case StrongDuality: return "strong duality"; | ||
case KKT: return "KKT"; | ||
default: throw std::runtime_error("Invalid reformulation."); | ||
} | ||
} | ||
|
||
TEST_CASE("Reformulate Facility Location Problem instances using optimality conditions", "") { | ||
|
||
Env env; | ||
|
||
using namespace Problems::FLP; | ||
|
||
// Generate parameters | ||
const auto [filename, objective_value] = GENERATE( | ||
std::make_pair<std::string, double>("instance_F10_C20__0.txt", 210.9670332387), | ||
std::make_pair<std::string, double>("instance_F10_C20__1.txt", 212.5001117517), | ||
std::make_pair<std::string, double>("instance_F10_C20__2.txt", 220.886214295), | ||
std::make_pair<std::string, double>("instance_F10_C20__3.txt", 308.7378641781), | ||
std::make_pair<std::string, double>("instance_F10_C20__4.txt", 194.9867989575) | ||
); | ||
const auto reformulation = GENERATE(Dual, StrongDuality); /* , Reformulation::KKT */ | ||
|
||
// Read instance | ||
const auto instance = read_instance_1991_Cornuejols_et_al("../../data/facility-location-problem/" + filename); | ||
const unsigned int n_customers = instance.n_customers(); | ||
const unsigned int n_facilities = instance.n_facilities(); | ||
|
||
// Make model | ||
auto x = Var::make_vector(env, Dim<1>(n_facilities), 0., 1., Continuous, 0., "x"); | ||
auto y = Var::make_vector(env, Dim<2>(n_facilities, n_customers), 0., 1., Continuous, 0., "y"); | ||
|
||
Model model(env); | ||
|
||
model.add_vector<Var, 1>(x); | ||
model.add_vector<Var, 2>(y); | ||
|
||
for (unsigned int i = 0 ; i < n_facilities ; ++i) { | ||
model.add(Ctr(env, idol_Sum(j, Range(n_customers), instance.demand(j) * y[i][j]) <= instance.capacity(i) * x[i])); | ||
} | ||
|
||
for (unsigned int j = 0 ; j < n_customers ; ++j) { | ||
model.add(Ctr(env, idol_Sum(i, Range(n_facilities), y[i][j]) == 1)); | ||
} | ||
|
||
model.set_obj_expr(idol_Sum(i, Range(n_facilities), instance.fixed_cost(i) * x[i] + idol_Sum(j, Range(n_customers), | ||
instance.per_unit_transportation_cost( | ||
i, j) * | ||
instance.demand(j) * | ||
y[i][j]))); | ||
|
||
Model reformulated(env); | ||
|
||
Dualizer dualizer(model, model.get_obj_expr()); | ||
|
||
switch (reformulation) { | ||
case Dual: | ||
dualizer.add_dual(reformulated); | ||
break; | ||
case StrongDuality: | ||
dualizer.add_strong_duality_reformulation(reformulated); | ||
break; | ||
case KKT: | ||
dualizer.add_kkt_reformulation(reformulated); | ||
break; | ||
default: throw std::runtime_error("Invalid reformulation."); | ||
} | ||
|
||
reformulated.use(OPTIMIZER()); | ||
|
||
WHEN("The instance \"" + filename + "\" is solved after reformulating using " + to_string(reformulation)) { | ||
|
||
reformulated.optimize(); | ||
|
||
THEN("The solution status is Optimal") { | ||
|
||
CHECK(reformulated.get_status() == Optimal); | ||
|
||
} | ||
|
||
AND_THEN("The objective value is " + std::to_string(objective_value)) { | ||
|
||
const auto primal_solution = save_primal(reformulated); | ||
double value = evaluate(dualizer.get_dual_obj_expr(), primal_solution); | ||
|
||
CHECK(value == Catch::Approx(objective_value)); | ||
|
||
if (reformulation != Reformulation::Dual) { | ||
value = evaluate(model.get_obj_expr(), primal_solution); | ||
CHECK(value == Catch::Approx(objective_value)); | ||
} | ||
|
||
} | ||
|
||
} | ||
|
||
} |