00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include <time.h>
00024 #include <set>
00025 #include <utility>
00026 #include "base/callback.h"
00027 #include "base/commandlineflags.h"
00028 #include "base/commandlineflags.h"
00029 #include "base/integral_types.h"
00030 #include "base/logging.h"
00031 #include "base/stringprintf.h"
00032 #include "constraint_solver/constraint_solver.h"
00033 #include "constraint_solver/constraint_solveri.h"
00034 #include "base/random.h"
00035
00036 DEFINE_int32(minsize, 0, "Minimum degree of Costas matrix.");
00037 DEFINE_int32(maxsize, 0, "Maximum degree of Costas matrix.");
00038 DEFINE_int32(freevar, 5, "Number of free variables.");
00039 DEFINE_int32(freeorderedvar, 4, "Number of variables in ordered subset.");
00040 DEFINE_int32(sublimit, 20, "Number of attempts per subtree.");
00041 DEFINE_int32(timelimit, 120000, "Time limit for local search.");
00042 DEFINE_bool(soft_constraints, false, "Use soft constraints.");
00043 DEFINE_string(export_profile, "", "filename to save the profile overview");
00044
00045 namespace operations_research {
00046
00047
00048 void CheckConstraintViolators(const std::vector<int64>& vars,
00049 std::vector<int>* const violators) {
00050 int dim = vars.size();
00051
00052
00053 for (int i = 0; i < dim; ++i) {
00054 for (int k = i + 1; k < dim; ++k) {
00055 if (vars[i] == vars[k]) {
00056 violators->push_back(i);
00057 violators->push_back(k);
00058 }
00059 }
00060 }
00061
00062
00063 for (int level = 1; level < dim; ++level) {
00064 for (int i = 0; i < dim - level; ++i) {
00065 const int difference = vars[i + level] - vars[i];
00066
00067 for (int k = i + 1; k < dim - level; ++k) {
00068 if (difference == vars[k + level] - vars[k]) {
00069 violators->push_back(k + level);
00070 violators->push_back(k);
00071 violators->push_back(i + level);
00072 violators->push_back(i);
00073 }
00074 }
00075 }
00076 }
00077 }
00078
00079
00080 bool CheckCostas(const std::vector<int64>& vars) {
00081 std::vector<int> violators;
00082
00083 CheckConstraintViolators(vars, &violators);
00084
00085 return violators.empty();
00086 }
00087
00088
00089 class OrderedLNS: public BaseLNS {
00090 public:
00091 OrderedLNS(const std::vector<IntVar*>& vars, int free_elements) :
00092 BaseLNS(vars.data(), vars.size()),
00093 free_elements_(free_elements) {
00094 index_ = 0;
00095
00096
00097 for (int i = 0; i < free_elements; ++i) {
00098 index_ += i * pow(static_cast<double>(vars.size()),
00099 free_elements - i - 1);
00100 }
00101 }
00102
00103 virtual bool NextFragment(std::vector<int>* const fragment) {
00104 int dim = Size();
00105 std::set<int> fragment_set;
00106
00107 do {
00108 int work_index = index_;
00109
00110 fragment->clear();
00111 fragment_set.clear();
00112
00113 for (int i = 0; i < free_elements_; ++i) {
00114 int current_index = work_index % dim;
00115 work_index = work_index / dim;
00116
00117 std::pair<std::set<int>::iterator, bool> ret =
00118 fragment_set.insert(current_index);
00119
00120
00121 if (ret.second) {
00122 fragment->push_back(current_index);
00123 } else {
00124 break;
00125 }
00126 }
00127
00128
00129 ++index_;
00130
00131 } while (fragment_set.size() < free_elements_);
00132
00133 return true;
00134 }
00135
00136 private:
00137 int index_;
00138 const int free_elements_;
00139 };
00140
00141
00142
00143 class RandomLNS: public BaseLNS {
00144 public:
00145 RandomLNS(const std::vector<IntVar*>& vars, int free_elements) :
00146 BaseLNS(vars.data(), vars.size()),
00147 free_elements_(free_elements),
00148 rand_(ACMRandom::HostnamePidTimeSeed()) {
00149 }
00150
00151 virtual bool NextFragment(std::vector<int>* const fragment) {
00152 std::vector<int> weighted_elements;
00153 std::vector<int64> values;
00154
00155
00156
00157
00158 for (int i = 0; i < Size(); ++i) {
00159 values.push_back(Value(i));
00160
00161
00162 weighted_elements.push_back(i);
00163 }
00164
00165 CheckConstraintViolators(values, &weighted_elements);
00166 int size = weighted_elements.size();
00167
00168
00169 while (fragment->size() < std::min(free_elements_, size)) {
00170 const int index = weighted_elements[rand_.Next() % size];
00171 fragment->push_back(index);
00172
00173
00174 for (std::vector<int>::iterator pos = weighted_elements.begin();
00175 pos != weighted_elements.end(); ) {
00176 if (*pos == index) {
00177
00178 std::vector<int>::iterator end = pos;
00179
00180 while ((end + 1) != weighted_elements.end() && *(end + 1) == *pos) {
00181 ++end;
00182 }
00183
00184 pos = weighted_elements.erase(pos, end);
00185 } else {
00186 ++pos;
00187 }
00188 }
00189
00190 size = weighted_elements.size();
00191 }
00192
00193 return true;
00194 }
00195
00196 private:
00197 const int free_elements_;
00198 ACMRandom rand_;
00199 };
00200
00201 class Evaluator {
00202 public:
00203 explicit Evaluator(const std::vector<IntVar*>& vars) : vars_(vars) {}
00204
00205
00206 int64 VarEvaluator(int64 index) {
00207 return vars_[index]->Size();
00208 }
00209
00210
00211
00212 int64 ValueEvaluator(int64 id, int64 value) {
00213 int appearance = 0;
00214
00215 for (int i = 0; i < vars_.size(); ++i) {
00216 if (i != id && vars_[i]->Contains(value)) {
00217 ++appearance;
00218 }
00219 }
00220
00221 return appearance;
00222 }
00223 private:
00224 std::vector<IntVar*> vars_;
00225 };
00226
00227
00228
00229
00230 void CostasSoft(const int dim) {
00231 Solver solver("Costas");
00232
00233
00234
00235 const int num_elements = dim + (2 * dim + 1) * (dim);
00236
00237
00238 std::vector<IntVar*> vars;
00239 solver.MakeIntVarArray(num_elements, -dim, dim, "var_", &vars);
00240
00241
00242 std::vector<IntVar*> matrix(dim);
00243
00244 std::vector<IntVar*> occurences;
00245
00246
00247
00248 std::vector<int64> possible_values(2 * dim + 1);
00249
00250 for (int64 i = -dim; i <= dim; ++i) {
00251 possible_values[i + dim] = i;
00252 }
00253
00254 int index = 0;
00255
00256
00257 for (; index < dim; ++index) {
00258 matrix[index] = vars[index];
00259 vars[index]->SetMin(1);
00260 }
00261
00262
00263
00264 std::vector<IntVar*> matrix_count;
00265 solver.MakeIntVarArray(2 * dim + 1, 0, dim, "matrix_count_", &matrix_count);
00266 solver.AddConstraint(solver.MakeDistribute(matrix, possible_values,
00267 matrix_count));
00268
00269
00270 for (int64 j = dim + 1; j <= 2 * dim; ++j) {
00271
00272 vars[index]
00273 = solver.MakeSemiContinuousExpr(solver.MakeSum(matrix_count[j], -1),
00274 0, 1)->Var();
00275
00276 occurences.push_back(vars[index++]);
00277 }
00278
00279
00280 for (int i = 1; i < dim; ++i) {
00281 std::vector<IntVar*> subset(dim - i);
00282
00283
00284 for (int j = 0; j < dim - i; ++j) {
00285 IntVar* const diff = solver.MakeDifference(vars[j + i], vars[j])->Var();
00286 subset[j] = diff;
00287 }
00288
00289
00290 std::vector<IntVar*> domain_count;
00291 solver.MakeIntVarArray(2 * dim + 1, 0, dim, "domain_count_", &domain_count);
00292 solver.AddConstraint(solver.MakeDistribute(subset,
00293 possible_values,
00294 domain_count));
00295
00296
00297 for (int64 j = 0; j <= 2 * dim; ++j) {
00298 vars[index] =
00299 solver.MakeSemiContinuousExpr(solver.MakeSum(domain_count[j], -1),
00300 0, dim - i)->Var();
00301
00302 occurences.push_back(vars[index++]);
00303 }
00304 }
00305
00306
00307 IntVar* const objective_var = solver.MakeSum(occurences)->Var();
00308 OptimizeVar* const total_duplicates = solver.MakeMinimize(objective_var, 1);
00309
00310 SearchMonitor* const log = solver.MakeSearchLog(1000, objective_var);
00311
00312
00313 SolutionCollector* const collector = solver.MakeLastSolutionCollector();
00314 collector->Add(vars);
00315
00316
00317 Evaluator evaluator(matrix);
00318 DecisionBuilder* const first_solution =
00319 solver.MakePhase(matrix,
00320 NewPermanentCallback(&evaluator,
00321 &Evaluator::VarEvaluator),
00322 NewPermanentCallback(&evaluator,
00323 &Evaluator::ValueEvaluator));
00324
00325 SearchLimit* const search_time_limit =
00326 solver.MakeLimit(FLAGS_timelimit, kint64max, kint64max, kint64max);
00327
00328
00329 SearchLimit* const fail_limit = solver.MakeLimit(kint64max, kint64max,
00330 FLAGS_sublimit, kint64max);
00331
00332 DecisionBuilder* const subdecision_builder =
00333 solver.MakeSolveOnce(first_solution, fail_limit);
00334
00335 std::vector<LocalSearchOperator*> localSearchOperators;
00336
00337
00338 localSearchOperators.push_back(
00339 solver.RevAlloc(new OrderedLNS(matrix, FLAGS_freevar)));
00340
00341
00342 localSearchOperators.push_back(
00343 solver.RevAlloc(new OrderedLNS(matrix, FLAGS_freeorderedvar)));
00344
00345 LocalSearchPhaseParameters* const ls_params =
00346 solver.MakeLocalSearchPhaseParameters(
00347 solver.ConcatenateOperators(localSearchOperators),
00348 subdecision_builder);
00349
00350 DecisionBuilder* const second_phase =
00351 solver.MakeLocalSearchPhase(matrix.data(),
00352 matrix.size(),
00353 first_solution,
00354 ls_params);
00355
00356
00357 solver.Solve(second_phase,
00358 collector,
00359 log,
00360 total_duplicates,
00361 search_time_limit);
00362
00363 if (collector->solution_count() > 0) {
00364 std::vector<int64> costas_matrix;
00365 string output;
00366
00367 for (int n = 0; n < dim; ++n) {
00368 const int64 v = collector->Value(0, vars[n]);
00369 costas_matrix.push_back(v);
00370 StringAppendF(&output, "%3lld", v);
00371 }
00372
00373 if (!CheckCostas(costas_matrix)) {
00374 LOG(INFO) << "No Costas Matrix found, closest solution displayed.";
00375 }
00376
00377 LOG(INFO) << output;
00378 } else {
00379 LOG(INFO) << "No solution found";
00380 }
00381 }
00382
00383
00384 void CostasHard(const int dim) {
00385 SolverParameters parameters;
00386 if (!FLAGS_export_profile.empty()) {
00387 parameters.profile_level = SolverParameters::NORMAL_PROFILING;
00388 }
00389 Solver solver("costas", parameters);
00390
00391
00392 std::vector<IntVar*> vars;
00393 solver.MakeIntVarArray(dim, -dim, dim, "var", &vars);
00394
00395
00396 for (int m = 0; m < dim; ++m) {
00397 vars[m]->SetMin(1);
00398 }
00399
00400 solver.AddConstraint(solver.MakeAllDifferent(vars));
00401
00402
00403 for (int i = 1; i < dim; ++i) {
00404 std::vector<IntVar*> subset(dim - i);
00405
00406 for (int j = 0; j < dim - i; ++j) {
00407 subset[j] = solver.MakeDifference(vars[j + i], vars[j])->Var();
00408 }
00409
00410 solver.AddConstraint(solver.MakeAllDifferent(subset));
00411 }
00412
00413 DecisionBuilder* const db = solver.MakePhase(vars,
00414 Solver::CHOOSE_FIRST_UNBOUND,
00415 Solver::ASSIGN_MIN_VALUE);
00416 solver.NewSearch(db);
00417
00418 if (solver.NextSolution()) {
00419 std::vector<int64> costas_matrix;
00420 string output;
00421
00422 for (int n = 0; n < dim; ++n) {
00423 const int64 v = vars[n]->Value();
00424 costas_matrix.push_back(v);
00425 StringAppendF(&output, "%3lld", v);
00426 }
00427
00428 LOG(INFO) << output << " (" << solver.wall_time() << "ms)";
00429
00430 CHECK(CheckCostas(costas_matrix)) <<
00431 ": Solution is not a valid Costas Matrix.";
00432 } else {
00433 LOG(INFO) << "No solution found.";
00434 }
00435 if (!FLAGS_export_profile.empty()) {
00436 solver.ExportProfilingOverview(FLAGS_export_profile);
00437 }
00438 }
00439 }
00440
00441 int main(int argc, char **argv) {
00442 google::ParseCommandLineFlags(&argc, &argv, true);
00443 int min = 1;
00444 int max = 10;
00445
00446 if (FLAGS_minsize != 0) {
00447 min = FLAGS_minsize;
00448
00449 if (FLAGS_maxsize != 0) {
00450 max = FLAGS_maxsize;
00451 } else {
00452 max = min;
00453 }
00454 }
00455
00456 for (int i = min; i <= max; ++i) {
00457 LOG(INFO) << "Computing Costas Array for dim = " << i;
00458 if (FLAGS_soft_constraints) {
00459 operations_research::CostasSoft(i);
00460 } else {
00461 operations_research::CostasHard(i);
00462 }
00463 }
00464
00465 return 0;
00466 }