00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include <cstdio>
00017 #include <cstdlib>
00018
00019 #include "base/commandlineflags.h"
00020 #include "base/commandlineflags.h"
00021 #include "base/integral_types.h"
00022 #include "base/logging.h"
00023 #include "base/stringprintf.h"
00024 #include "base/strtoint.h"
00025 #include "base/file.h"
00026 #include "base/filelinereader.h"
00027 #include "base/split.h"
00028 #include "constraint_solver/constraint_solver.h"
00029
00030 DEFINE_string(
00031 data_file,
00032 "",
00033 "Required: input file description the muldi-dimensional knapsack problem\n "
00034 "to solve. It supports two file format as described in:\n"
00035 " - http://elib.zib.de/pub/Packages/mp-testdata/ip/sac94-suite/readme\n"
00036 " - http://hces.bus.olemiss.edu/tools.html\n");
00037 DEFINE_int32(time_limit_in_ms, 0, "Time limit in ms, <= 0 means no limit.");
00038 DEFINE_int32(simplex_frequency, 0, "Number of nodes explored between each"
00039 " call to the simplex optimizer.");
00040 DEFINE_bool(display_search_log, true, "Display search log.");
00041
00042 namespace operations_research {
00043
00044
00045
00046 class MultiDimKnapsackData {
00047 public:
00048 MultiDimKnapsackData()
00049 : name_(""), line_read_(0), mode_(0), num_dims_(-1), num_items_(-1),
00050 current_bin_(0), current_item_(0), optimal_value_(0),
00051 problem_type_(-1) {}
00052
00053
00054 void Load(const string& filename) {
00055 FileLineReader reader(filename.c_str());
00056 reader.set_line_callback(NewPermanentCallback(
00057 this,
00058 &MultiDimKnapsackData::ProcessNewLine));
00059 reader.Reload();
00060 if (!reader.loaded_successfully()) {
00061 LOG(ERROR) << "Could not open multi dimensional knapsack file";
00062 }
00063 if (optimal_value_ == 0) {
00064 LOG(INFO) << "Successfully loaded problem " << name_ << " with "
00065 << items() << " items, " << dims() << " dimensions";
00066 } else {
00067 LOG(INFO) << "Successfully loaded problem " << name_ << " with "
00068 << items() << " items, " << dims()
00069 << " dimensions and an optimal value of " << optimal_value_;
00070 }
00071 }
00072
00073
00074 int items() const { return num_items_; }
00075
00076
00077 int dims() const { return num_dims_; }
00078
00079
00080 const string& name() const { return name_; }
00081
00082 int capacity(int i) const { return dims_[i]; }
00083 int profit(int j) const { return profit_[j]; }
00084 int weight(int i, int j) const { return weight_[i][j]; }
00085 int optimal_value() const { return optimal_value_; }
00086
00087
00088 void ProcessNewLine(char* const line) {
00089 const char* const kWordDelimiters(" ");
00090 std::vector<string> words;
00091 SplitStringUsing(line, kWordDelimiters, &words);
00092 line_read_++;
00093 if (problem_type_ == -1) {
00094 if (words.size() == 1) {
00095 LOG(INFO) << "New data format";
00096 problem_type_ = 1;
00097 } else if (words.size() == 2) {
00098 LOG(INFO) << "Original data format";
00099 problem_type_ = 0;
00100 }
00101 }
00102 if (problem_type_ == 0) {
00103
00104
00105
00106
00107
00108
00109
00110 switch (mode_) {
00111 case 0: {
00112 if (words.size() != 0) {
00113 CHECK_EQ(2, words.size());
00114 num_dims_ = atoi32(words[0]);
00115 num_items_ = atoi32(words[1]);
00116 weight_.resize(num_dims_);
00117 mode_ = 1;
00118 }
00119 break;
00120 }
00121 case 1: {
00122 for (int i = 0; i < words.size(); ++i) {
00123 const int val = atoi32(words[i]);
00124 profit_.push_back(val);
00125 }
00126 CHECK_LE(profit_.size(), num_items_);
00127 if (profit_.size() == num_items_) {
00128 mode_ = 2;
00129 }
00130 break;
00131 }
00132 case 2: {
00133 for (int i = 0; i < words.size(); ++i) {
00134 const int val = atoi32(words[i]);
00135 dims_.push_back(val);
00136 }
00137 CHECK_LE(dims_.size(), num_dims_);
00138 if (dims_.size() == num_dims_) {
00139 mode_ = 3;
00140 }
00141 break;
00142 }
00143 case 3: {
00144 for (int i = 0; i < words.size(); ++i) {
00145 const int val = atoi32(words[i]);
00146 weight_[current_bin_].push_back(val);
00147 if (weight_[current_bin_].size() == num_items_) {
00148 current_bin_++;
00149 }
00150 }
00151 if (current_bin_ == num_dims_) {
00152 mode_ = 4;
00153 }
00154 break;
00155 }
00156 case 4: {
00157 if (words.size() != 0) {
00158 CHECK_EQ(1, words.size());
00159 optimal_value_ = atoi32(words[0]);
00160 mode_ = 5;
00161 }
00162 break;
00163 }
00164 case 5: {
00165 if (words.size() != 0) {
00166 name_ = line;
00167 mode_ = 6;
00168 }
00169 break;
00170 }
00171 case 6: {
00172 break;
00173 }
00174 }
00175 } else {
00176
00177
00178
00179
00180
00181 switch (mode_) {
00182 case 0: {
00183 name_ = words[0];
00184 mode_ = 1;
00185 current_bin_ = -1;
00186 break;
00187 }
00188 case 1: {
00189 if (words.size() != 0) {
00190 CHECK_EQ(2, words.size());
00191 num_items_ = atoi32(words[0]);
00192 num_dims_ = atoi32(words[1]);
00193 weight_.resize(num_dims_);
00194 mode_ = 2;
00195 }
00196 break;
00197 }
00198 case 2: {
00199 for (int i = 0; i < words.size(); ++i) {
00200 const int val = atoi32(words[i]);
00201 if (current_bin_ == -1) {
00202 profit_.push_back(val);
00203 } else {
00204 weight_[current_bin_].push_back(val);
00205 CHECK_EQ(current_item_, weight_[current_bin_].size() - 1);
00206 }
00207 current_bin_++;
00208 if (current_bin_ == num_dims_) {
00209 current_bin_ = -1;
00210 current_item_++;
00211 }
00212 if (current_item_ == num_items_) {
00213 mode_ = 3;
00214 }
00215 }
00216 break;
00217 }
00218 case 3: {
00219 for (int i = 0; i < words.size(); ++i) {
00220 const int val = atoi32(words[i]);
00221 dims_.push_back(val);
00222 }
00223 CHECK_LE(dims_.size(), num_dims_);
00224 if (dims_.size() == num_dims_) {
00225 mode_ = 4;
00226 }
00227 break;
00228 }
00229 case 4:
00230 break;
00231 }
00232 }
00233 }
00234
00235 private:
00236 string name_;
00237 std::vector<int> dims_;
00238 std::vector<int> profit_;
00239 std::vector<std::vector<int> > weight_;
00240 int line_read_;
00241 int mode_;
00242 int num_dims_;
00243 int num_items_;
00244 int current_bin_;
00245 int current_item_;
00246 int optimal_value_;
00247 int problem_type_;
00248 };
00249
00250 int64 EvaluateItem(MultiDimKnapsackData* const data, int64 var, int64 val) {
00251 if (val == 0) {
00252 return 0LL;
00253 }
00254 const int profit = data->profit(var);
00255 int max_weight = 0;
00256 for (int i = 0; i < data->dims(); ++i) {
00257 const int weight = data->weight(i, var);
00258 if (weight > max_weight) {
00259 max_weight = weight;
00260 }
00261 }
00262 return -(profit * 100 / max_weight);
00263 }
00264
00265 void SolveKnapsack(MultiDimKnapsackData* const data) {
00266 Solver solver("MultiDim Knapsack");
00267 std::vector<IntVar*> assign;
00268 solver.MakeBoolVarArray(data->items(), "assign", &assign);
00269 for (int i = 0; i < data->dims(); ++i) {
00270 const int capacity = data->capacity(i);
00271 std::vector<int64> coefs;
00272 for (int j = 0; j < data->items(); ++j) {
00273 coefs.push_back(data->weight(i, j));
00274 }
00275 solver.AddConstraint(
00276 solver.MakeScalProdLessOrEqual(assign, coefs, capacity));
00277 }
00278
00279
00280 std::vector<int64> profits;
00281 for (int i = 0; i < data->items(); ++i) {
00282 profits.push_back(data->profit(i));
00283 }
00284
00285 IntVar* const objective = solver.MakeScalProd(assign, profits)->Var();
00286
00287 std::vector<SearchMonitor*> monitors;
00288 OptimizeVar* const objective_monitor = solver.MakeMaximize(objective, 1);
00289 monitors.push_back(objective_monitor);
00290 if (FLAGS_display_search_log) {
00291 SearchMonitor* const search_log = solver.MakeSearchLog(1000000, objective);
00292 monitors.push_back(search_log);
00293 }
00294 DecisionBuilder* const db =
00295 solver.MakePhase(assign,
00296 NewPermanentCallback(&EvaluateItem, data),
00297 Solver::CHOOSE_STATIC_GLOBAL_BEST);
00298 if (FLAGS_time_limit_in_ms != 0) {
00299 LOG(INFO) << "adding time limit of " << FLAGS_time_limit_in_ms << " ms";
00300 SearchLimit* const limit = solver.MakeLimit(FLAGS_time_limit_in_ms,
00301 kint64max,
00302 kint64max,
00303 kint64max);
00304 monitors.push_back(limit);
00305 }
00306
00307 if (FLAGS_simplex_frequency > 0) {
00308 SearchMonitor* const simplex =
00309 solver.MakeSimplexConstraint(FLAGS_simplex_frequency);
00310 monitors.push_back(simplex);
00311 }
00312
00313 if (solver.Solve(db, monitors)) {
00314 LOG(INFO) << "Best solution found = " << objective_monitor->best();
00315 }
00316 }
00317 }
00318
00319 static const char kUsage[] =
00320 "Usage: see flags.\n"
00321 "This program runs a multi-dimensional knapsack problem.";
00322
00323 int main(int argc, char **argv) {
00324 google::SetUsageMessage(kUsage);
00325 google::ParseCommandLineFlags(&argc, &argv, true);
00326 if (FLAGS_data_file.empty()) {
00327 LOG(FATAL) << "Please supply a data file with --datafile=";
00328 }
00329 operations_research::MultiDimKnapsackData data;
00330 data.Load(FLAGS_data_file);
00331 operations_research::SolveKnapsack(&data);
00332 return 0;
00333 }