@@ -11,31 +11,71 @@
// See the License for the specific language governing permissions and
// limitations under the License.
# include <algorithm>
# include <string>
# include <vector>
# include "absl/flags/flag.h"
# include "absl/log/check.h"
# include "absl/strings/str_cat.h"
# include "absl/time/time.h"
# include "gtest/gtest.h"
# include "ortools/algorithms/set_cover.h"
# include "ortools/algorithms/set_cover_ledger.h"
# include "ortools/algorithms/set_cover_mip.h"
# include "ortools/algorithms/set_cover_model.h"
# include "ortools/algorithms/set_cover_reader.h"
# include "ortools/base/logging.h"
# include "ortools/base/path.h"
# include "ortools/base/timer.h"
# include "testing/base/public/googletest.h"
namespace operations_research {
void RunSolver ( operations_research : : SetCoverModel * model ,
double expected_cost ) {
operations_research : : SetCoverLedger ledger ( model ) ;
double RunSolver ( std : : string name , SetCoverModel * model ) {
SetCoverLedger ledger ( model ) ;
operations_research : : GreedySolutionGenerator greedy ( & ledger ) ;
GreedySolutionGenerator greedy ( & ledger ) ;
WallTimer global_timer ;
WallTimer timer ;
global_timer . Start ( ) ;
timer . Start ( ) ;
CHECK ( greedy . NextSolution ( ) ) ;
CHECK ( ledger . CheckSolution ( ) ) ;
LOG ( INFO ) < < " GreedySolutionGenerator cost: " < < ledger . cost ( ) ;
D CHECK( ledger . CheckSolution ( ) ) ;
LOG ( INFO ) < < name < < " _ GreedySolutionGenerator_ cost, " < < ledger . cost ( )
< < " , " < < absl : : ToInt64Microseconds ( timer . GetDuration ( ) ) < < " , us " ;
timer . Stop ( ) ;
timer . Reset ( ) ;
timer . Start ( ) ;
operations_research : : SteepestSearch steepest ( & ledger ) ;
steepest . NextSolution ( 100000 ) ;
LOG ( INFO ) < < " SteepestSearch cost: " < < ledger . cost ( ) ;
CHECK ( ledger . CheckSolution ( ) ) ;
CHECK_EQ ( ledger . cost ( ) , expected_cost ) ;
LOG ( INFO ) < < name < < " _ SteepestSearch_ cost, " < < ledger . cost ( ) < < " , "
< < absl : : ToInt64Microseconds ( timer . GetDuration ( ) ) < < " , us " ;
double best_cost = ledger . cost ( ) ;
D CHECK( ledger . CheckSolution ( ) ) ;
SubsetBoolVector best_choices = ledger . is_selected ( ) ;
std : : vector < SubsetIndex > focus = model - > all_subsets ( ) ;
timer . Stop ( ) ;
timer . Reset ( ) ;
timer . Start ( ) ;
for ( int i = 0 ; i < 10 ; + + i ) {
std : : vector < SubsetIndex > range =
ClearMostCoveredElements ( std : : min ( 100UL , focus . size ( ) ) , & ledger ) ;
SetCoverMip mip ( & ledger ) ;
mip . NextSolution ( range ) ;
if ( ledger . cost ( ) < best_cost ) {
best_cost = ledger . cost ( ) ;
best_choices = ledger . is_selected ( ) ;
}
}
timer . Stop ( ) ;
LOG ( INFO ) < < name < < " _MIP_cost, " < < best_cost < < " , "
< < absl : : ToInt64Microseconds ( timer . GetDuration ( ) ) < < " , us " ;
global_timer . Stop ( ) ;
LOG ( INFO ) < < name < < " _total_running_time, " < < best_cost < < " , "
< < absl : : ToInt64Microseconds ( global_timer . GetDuration ( ) )
< < " , us, total_time " ;
return best_cost ;
// TODO(user): add guided local search.
}
@@ -59,132 +99,168 @@ enum ProblemSize {
# define APPEND(x, y) x##y
# define APPEND_AND_EVAL(x, y) APPEND(x, y)
// This macro makes it possible to declare each test below with a one liner.
# define ORLIB_TEST(name, objective, size, function) \
TEST(SetCoverTest, APPEND_AND_EVAL(TestOnLine, __LINE__)) { \
auto filespec = \
file::JoinPathRespectAbsolute(absl::GetFlag(FLAGS_test_srcdir), \
"operations_research_data / " \
"o perations_r esearch_data / SET_COVERING", \
name); \
LOG(INFO) << "Reading " << name; \
operations_research:: SetCoverModel model = function(filespec); \
RunSolver(&model, objective); \
const char data_dir [ ] =
" operations_research_data/operations_research_data/ "
" SET_COVERING " ;
// In the following, the lower bounds are taken from:
// [1] Caprara, Alberto, Matteo Fischetti, and Paolo Toth. 1999. “A Heuristic
// Method for the Set Covering Problem.” O perations R esearch 47 (5): 730– 43.
// https://www.jstor.org/stable/223097 , and
// [2] Yagiura, Mutsunori, Masahiro Kishida, and Toshihide Ibaraki. 2006.
// “A 3-Flip Neighborhood Local Search for the Set Covering Problem.” European
// Journal of Operational Research 172 (2): 472– 99.
// https://www.sciencedirect.com/science/article/pii/S0377221704008264
// This macro makes it possible to declare each test below with a one-liner.
// 'best_objective' denotes the best objective costs found in literature.
// These are the proven optimal values. This can be achieved with MIP.
// For the rail instances, they are the best solution found in the literature
// [1] and [2]. They are not achievable though local search or MIP or a
// combination of the two.
// 'expected_objective' are the costs currently reached by the solver.
// TODO(user): find and add values for the unit cost (aka unicost) case.
# define ORLIB_TEST(name, best_objective, expected_objective, size, function) \
TEST(OrlibTest, APPEND_AND_EVAL(TestOnLine, __LINE__)) { \
auto filespec = file::JoinPathRespectAbsolute( \
absl::GetFlag(FLAGS_test_srcdir), data_dir, name); \
LOG(INFO) << "Reading " << name; \
operations_research::SetCoverModel model = function(filespec); \
double cost = RunSolver(name, &model); \
(void)cost; \
}
# define RAIL_TEST(name, objective, size) \
ORLIB_TEST(name, objective, size, \
operations_research::ReadRailSetCoverProblem)
# define ORLIB_UNICOST_TEST(name, best_objective, expected_ objective, size, \
function) \
TEST(OrlibUnicostTest, APPEND_AND_EVAL(TestOnLine, __LINE__)) { \
auto filespec = file::JoinPathRespectAbsolute( \
absl::GetFlag(FLAGS_test_srcdir), data_dir, name); \
LOG(INFO) << "Reading " << name; \
operations_research::SetCoverModel model = function(filespec); \
for (int i = 0; i < model.num_subsets(); ++i) { \
model.SetSubsetCost(i, 1.0); \
} \
double cost = RunSolver(absl::StrCat(name, "_unicost"), &model); \
(void)cost; \
}
# define SCP_TEST(name, objective, size) \
ORLIB_TEST(name, objective, size, \
operations_research::ReadBeasleySetCoverProblem)
# define SCP_TEST(name, best_objective, expected_ objective, size) \
ORLIB_TEST(name, best_objective, expected_ objective, size, \
operations_research::ReadBeasleySetCoverProblem) \
ORLIB_UNICOST_TEST(name, best_objective, expected_objective, size, \
operations_research::ReadBeasleySetCoverProblem)
// Costs mentioned are the cost currently reached by the solver.
// TODO(user): add the best costs from the literature and compare.
# define RAIL_TEST(name, best_objective, expected_objective, size) \
ORLIB_TEST(name, best_objective, expected_objective, size, \
operations_research::ReadRailSetCoverProblem) \
ORLIB_UNICOST_TEST(name, best_objective, expected_objective, size, \
operations_research::ReadRailSetCoverProblem)
RAIL _TEST( " rail2536 .txt" , 88 9, MANYSECOND S) ;
RAIL _TEST( " rail2586 .txt" , 1139 , MANYSECOND S) ;
RAIL _TEST( " rail4284 .txt" , 1362 , MANYSECOND S) ;
RAIL _TEST( " rail4872 .txt" , 1861 , MANYSECOND S) ;
RAIL _TEST( " rail507 .txt" , 218 , FEWTENTH S ) ;
RAIL _TEST( " rail51 6.txt" , 20 4, FEWTENTH S ) ;
RAIL _TEST( " rail582 .txt" , 250 , FEWTENTH S ) ;
SCP _TEST( " scp41 .txt" , 42 9, 442 , FEWMILLI S) ;
SCP _TEST( " scp42 .txt" , 512 , 555 , FEWMILLI S) ;
SCP _TEST( " scp43 .txt" , 516 , 557 , FEWMILLI S) ;
SCP _TEST( " scp44 .txt" , 494 , 516 , FEWMILLI S) ;
SCP _TEST( " scp45 .txt" , 512 , 530 , FEWMILLI S ) ;
SCP _TEST( " scp4 6.txt" , 560 , 59 4, FEWMILLI S ) ;
SCP _TEST( " scp47 .txt" , 430 , 451 , FEWMILLI S ) ;
SCP_TEST ( " scp48.txt " , 492 , 502 , FEWMILLIS ) ;
SCP_TEST ( " scp49.txt " , 641 , 693 , FEWMILLIS ) ;
SCP_TEST ( " scp410.txt " , 514 , 525 , FEWMILLIS ) ;
SCP_TEST ( " scp4 1.txt " , 442 , FEWMILLIS ) ;
SCP_TEST ( " scp4 2.txt " , 555 , FEWMILLIS ) ;
SCP_TEST ( " scp4 3.txt " , 557 , FEWMILLIS ) ;
SCP_TEST ( " scp4 4.txt " , 516 , FEWMILLIS ) ;
SCP_TEST ( " scp4 5.txt " , 530 , FEWMILLIS ) ;
SCP_TEST ( " scp4 6.txt " , 59 4, FEWMILLIS ) ;
SCP_TEST ( " scp4 7.txt " , 451 , FEWMILLIS ) ;
SCP_TEST ( " scp4 8.txt " , 502 , FEWMILLIS ) ;
SCP_TEST ( " scp4 9.txt " , 693 , FEWMILLIS ) ;
SCP_TEST ( " scp4 10.txt " , 525 , FEWMILLIS ) ;
SCP_TEST ( " scp5 1.txt " , 253 , 274 , FEWMILLIS ) ;
SCP_TEST ( " scp5 2.txt " , 302 , 329 , FEWMILLIS ) ;
SCP_TEST ( " scp5 3.txt " , 226 , 233 , FEWMILLIS ) ;
SCP_TEST ( " scp5 4.txt " , 242 , 255 , FEWMILLIS ) ;
SCP_TEST ( " scp5 5.txt " , 211 , 222 , FEWMILLIS ) ;
SCP_TEST ( " scp5 6.txt " , 213 , 23 4, FEWMILLIS ) ;
SCP_TEST ( " scp5 7.txt " , 293 , 313 , FEWMILLIS ) ;
SCP_TEST ( " scp5 8.txt " , 288 , 309 , FEWMILLIS ) ;
SCP_TEST ( " scp5 9.txt " , 279 , 292 , FEWMILLIS ) ;
SCP_TEST ( " scp5 10.txt " , 265 , 276 , FEWMILLIS ) ;
SCP_TEST ( " scp5 1.txt " , 274 , FEWMILLIS ) ;
SCP_TEST ( " scp5 2.txt " , 329 , FEWMILLIS ) ;
SCP_TEST ( " scp5 3.txt " , 233 , FEWMILLIS ) ;
SCP_TEST ( " scp5 4.txt " , 255 , FEWMILLIS ) ;
SCP_TEST ( " scp5 5.txt " , 222 , FEWMILLIS ) ;
SCP_TEST ( " scp56.txt " , 234 , FEWMILLIS ) ;
SCP_TEST ( " scp57.txt " , 313 , FEWMILLIS ) ;
SCP_TEST ( " scp58.txt " , 309 , FEWMILLIS ) ;
SCP_TEST ( " scp59.txt " , 292 , FEWMILLIS ) ;
SCP_TEST ( " scp510.txt " , 276 , FEWMILLIS ) ;
SCP_TEST ( " scp6 1.txt " , 138 , 151 , FEWMILLIS ) ;
SCP_TEST ( " scp6 2.txt " , 146 , 173 , FEWMILLIS ) ;
SCP_TEST ( " scp6 3.txt " , 145 , 154 , FEWMILLIS ) ;
SCP_TEST ( " scp6 4.txt " , 131 , 137 , FEWMILLIS ) ;
SCP_TEST ( " scp6 5.txt " , 161 , 181 , FEWMILLIS ) ;
SCP_TEST ( " scp6 1.txt " , 151 , FEWMILLI S) ;
SCP_TEST ( " scp6 2.txt " , 173 , FEWMILLI S) ;
SCP_TEST ( " scp6 3.txt " , 15 4, FEWMILLI S ) ;
SCP_TEST ( " scp6 4.txt " , 137 , FEWMILLI S) ;
SCP_TEST ( " scp6 5.txt " , 181 , FEWMILLI S) ;
SCP_TEST ( " scpa 1.txt " , 253 , 275 , FEWHUNDREDTH S) ;
SCP_TEST ( " scpa 2.txt " , 252 , 268 , FEWHUNDREDTH S) ;
SCP_TEST ( " scpa 3.txt " , 232 , 24 4, FEWHUNDREDTH S ) ;
SCP_TEST ( " scpa 4.txt " , 234 , 253 , FEWHUNDREDTH S) ;
SCP_TEST ( " scpa 5.txt " , 236 , 249 , FEWHUNDREDTH S) ;
SCP_TEST ( " scpa 1.txt " , 275 , FEWHUNDRED THS ) ;
SCP_TEST ( " scpa 2.txt " , 26 8, FEWHUNDRED THS ) ;
SCP_TEST ( " scpa 3.txt " , 244 , FEWHUNDRED THS ) ;
SCP_TEST ( " scpa 4.txt " , 253 , FEWHUNDRED THS ) ;
SCP_TEST ( " scpa 5.txt " , 249 , FEWHUNDRED THS ) ;
SCP_TEST ( " scpb 1.txt " , 69 , 74 , FEWTEN THS ) ;
SCP_TEST ( " scpb 2.txt " , 76 , 7 8, FEWTEN THS ) ;
SCP_TEST ( " scpb 3.txt " , 80 , 85 , FEWTEN THS ) ;
SCP_TEST ( " scpb 4.txt " , 79 , 85 , FEWTEN THS ) ;
SCP_TEST ( " scpb 5.txt " , 72 , 77 , FEWTEN THS ) ;
SCP_TEST ( " scpb 1.txt " , 74 , FEWTEN THS ) ;
SCP_TEST ( " scpb 2.txt " , 7 8, FEWTEN THS ) ;
SCP_TEST ( " scpb 3.txt " , 85 , FEWTEN THS ) ;
SCP_TEST ( " scpb 4.txt " , 85 , FEWTEN THS ) ;
SCP_TEST ( " scpb 5.txt " , 77 , FEWTEN THS ) ;
SCP_TEST ( " scpc 1.txt " , 227 , 251 , FEWHUNDRED THS ) ;
SCP_TEST ( " scpc 2.txt " , 219 , 23 8, FEWHUNDRED THS ) ;
SCP_TEST ( " scpc 3.txt " , 243 , 259 , FEWHUNDRED THS ) ;
SCP_TEST ( " scpc 4.txt " , 219 , 246 , FEWHUNDRED THS ) ;
SCP_TEST ( " scpc 5.txt " , 214 , 228 , FEWHUNDRED THS ) ;
SCP_TEST ( " scpc 1.txt " , 251 , FEWHUNDREDTHS ) ;
SCP_TEST ( " scpc 2.txt " , 238 , FEWHUNDREDTHS ) ;
SCP_TEST ( " scpc 3.txt " , 259 , FEWHUNDREDTHS ) ;
SCP_TEST ( " scpc 4.txt " , 246 , FEWHUNDREDTHS ) ;
SCP_TEST ( " scpc 5.txt " , 228 , FEWHUNDREDTHS ) ;
SCP_TEST ( " scpd 1.txt " , 60 , 68 , FEWHUNDREDTHS ) ;
SCP_TEST ( " scpd 2.txt " , 66 , 70 , FEWHUNDREDTHS ) ;
SCP_TEST ( " scpd 3.txt " , 72 , 78 , FEWHUNDREDTHS ) ;
SCP_TEST ( " scpd 4.txt " , 62 , 67 , FEWHUNDREDTHS ) ;
SCP_TEST ( " scpd 5.txt " , 61 , 72 , FEWHUNDREDTHS ) ;
SCP_TEST ( " scpclr10 .txt " , 32 , FEWMILLIS ) ;
SCP_TEST ( " scpclr11 .txt " , 30 , FEWMILLIS ) ;
SCP_TEST ( " scpclr12 .txt " , 31 , FEWMILLIS ) ;
SCP_TEST ( " scpclr13 .txt " , 33 , FEWMILLIS ) ;
SCP_TEST ( " scpe1 .txt " , 5 , 5 , FEWMILLIS ) ;
SCP_TEST ( " scpe2 .txt " , 5 , 6 , FEWMILLIS ) ;
SCP_TEST ( " scpe3 .txt " , 5 , 5 , FEWMILLIS ) ;
SCP_TEST ( " scpe4 .txt " , 5 , 6 , FEWMILLIS ) ;
SCP_TEST ( " scpe5.txt " , 5 , 5 , FEWMILLIS ) ;
SCP_TEST ( " scpcyc06 .txt " , 60 , FEWMILLIS ) ;
SCP_TEST ( " scpcyc07 .txt " , 144 , FEWMILLIS ) ;
SCP_TEST ( " scpcyc08 .txt " , 360 , FEWMILLIS ) ;
SCP_TEST ( " scpcyc09 .txt " , 816 , SUBHUNDRED TH ) ;
SCP_TEST ( " scpcyc10 .txt " , 1920 , FEWHUNDRED THS ) ;
SCP_TEST ( " scpcyc11.txt " , 4284 , SUBTENTH ) ;
SCP_TEST ( " scpnre1 .txt " , 29 , 31 , SUBTENTH ) ;
SCP_TEST ( " scpnre2 .txt " , 30 , 34 , SUBTENTH ) ;
SCP_TEST ( " scpnre3 .txt " , 27 , 32 , SUBTENTH ) ;
SCP_TEST ( " scpnre4 .txt " , 28 , 32 , SUBTEN TH ) ;
SCP_TEST ( " scpnre5 .txt " , 28 , 31 , SUBTEN TH) ;
SCP_TEST ( " scpd 1.txt " , 68 , FEWHUNDRED THS ) ;
SCP_TEST ( " scpd 2.txt " , 70 , FEWHUNDRED THS ) ;
SCP_TEST ( " scpd 3.txt " , 78 , FEWHUNDRED THS ) ;
SCP_TEST ( " scpd 4.txt " , 67 , FEWHUNDRED THS ) ;
SCP_TEST ( " scpd 5.txt " , 72 , FEWHUNDRED THS ) ;
SCP_TEST ( " scpnrf 1.txt " , 14 , 17 , SUBTEN TH) ;
SCP_TEST ( " scpnrf 2.txt " , 15 , 16 , SUBTEN TH) ;
SCP_TEST ( " scpnrf 3.txt " , 14 , 16 , SUBTEN TH) ;
SCP_TEST ( " scpnrf 4.txt " , 14 , 15 , SUBTEN TH) ;
SCP_TEST ( " scpnrf 5.txt " , 13 , 15 , SUBTEN TH) ;
SCP_TEST ( " scpe 1.txt " , 5 , FEWMILLIS ) ;
SCP_TEST ( " scpe 2.txt " , 6 , FEWMILLIS ) ;
SCP_TEST ( " scpe 3.txt " , 5 , FEWMILLIS ) ;
SCP_TEST ( " scpe 4.txt " , 6 , FEWMILLIS ) ;
SCP_TEST ( " scpe 5.txt " , 5 , FEWMILLIS ) ;
SCP_TEST ( " scpnrg 1.txt " , 176 , 196 , SUBTENTH ) ;
SCP_TEST ( " scpnrg 2.txt " , 154 , 171 , SUBTENTH ) ;
SCP_TEST ( " scpnrg 3.txt " , 166 , 182 , SUBTENTH ) ;
SCP_TEST ( " scpnrg 4.txt " , 168 , 187 , SUBTENTH ) ;
SCP_TEST ( " scpnrg 5.txt " , 168 , 183 , SUBTENTH ) ;
SCP_TEST ( " scpnre 1.txt " , 3 1, SUB TENTH) ;
SCP_TEST ( " scpnre 2.txt " , 34 , SUB TENTH) ;
SCP_TEST ( " scpnre 3.txt " , 32 , SUB TENTH) ;
SCP_TEST ( " scpnre 4.txt " , 32 , SUB TENTH) ;
SCP_TEST ( " scpnre 5.txt " , 31 , SUB TENTH) ;
SCP_TEST ( " scpnrh 1.txt " , 63 , 7 1, FEW TENTHS ) ;
SCP_TEST ( " scpnrh 2.txt " , 63 , 70 , FEW TENTHS ) ;
SCP_TEST ( " scpnrh 3.txt " , 59 , 65 , FEW TENTHS ) ;
SCP_TEST ( " scpnrh 4.txt " , 58 , 66 , FEW TENTHS ) ;
SCP_TEST ( " scpnrh 5.txt " , 55 , 62 , FEW TENTHS ) ;
SCP _TEST( " scpnrf1 .txt" , 17 , SUB TENTH) ;
SCP _TEST( " scpnrf2 .txt" , 16 , SUB TENTH) ;
SCP _TEST( " scpnrf3 .txt" , 16 , SUB TENTH) ;
SCP _TEST( " scpnrf4 .txt" , 15 , SUBTENTH ) ;
SCP _TEST( " scpnrf5 .txt" , 15 , SUBTENTH ) ;
RAIL _TEST( " rail507 .txt" , 174 , 218 , FEW TENTHS ) ;
RAIL _TEST( " rail516 .txt" , 182 , 204 , FEW TENTHS ) ;
RAIL _TEST( " rail582 .txt" , 211 , 250 , FEW TENTHS ) ;
RAIL _TEST( " rail2536 .txt" , 691 , 889 , MANYSECONDS ) ;
RAIL _TEST( " rail2586 .txt" , 952 , 1139 , MANYSECONDS ) ;
RAIL_TEST ( " rail4284.txt " , 1065 , 1362 , MANYSECONDS ) ;
RAIL_TEST ( " rail4872.txt " , 1527 , 1861 , MANYSECONDS ) ; // [2]
SCP_TEST ( " scpnrg1 .txt " , 196 , SUBTENTH ) ;
SCP_TEST ( " scpnrg2 .txt " , 171 , SUBTENTH ) ;
SCP_TEST ( " scpnrg3 .txt " , 182 , SUBTENTH ) ;
SCP_TEST ( " scpnrg4 .txt " , 187 , SUBTENTH ) ;
SCP_TEST ( " scpnrg5.txt " , 183 , SUBTENTH ) ;
SCP_TEST ( " scpclr10 .txt " , 0 , 32 , FEWMILLIS ) ;
SCP_TEST ( " scpclr11 .txt " , 0 , 30 , FEWMILLIS ) ;
SCP_TEST ( " scpclr12 .txt " , 0 , 31 , FEWMILLIS ) ;
SCP_TEST ( " scpclr13 .txt " , 0 , 33 , FEWMILLIS ) ;
SCP_TEST ( " scpnrh1 .txt " , 71 , FEWTENTH S ) ;
SCP_TEST ( " scpnrh2 .txt " , 7 0, FEWTENTH S) ;
SCP_TEST ( " scpnrh3 .txt " , 65 , FEWTENTH S ) ;
SCP_TEST ( " scpnrh4 .txt " , 66 , FEWTEN THS ) ;
SCP_TEST ( " scpnrh5 .txt " , 62 , FEWTEN THS ) ;
SCP_TEST ( " scpcyc06 .txt " , 0 , 60 , FEWMILLI S ) ;
SCP_TEST ( " scpcyc07 .txt " , 0 , 144 , FEWMILLI S) ;
SCP_TEST ( " scpcyc08 .txt " , 0 , 360 , FEWMILLI S ) ;
SCP_TEST ( " scpcyc09 .txt " , 0 , 816 , SUBHUNDRED TH) ;
SCP_TEST ( " scpcyc10 .txt " , 0 , 1920 , FEWHUNDRED THS ) ;
SCP_TEST ( " scpcyc11.txt " , 0 , 4284 , SUBTENTH ) ;
# undef ORLIB_TEST
# undef ORLIB_UNICOST_TEST
# undef APPEND
# undef APPEND_AND_EVAL
# undef SCP_TEST