23#include "absl/base/optimization.h"
24#include "absl/container/flat_hash_set.h"
25#include "absl/log/check.h"
26#include "absl/log/log.h"
27#include "absl/strings/str_cat.h"
28#include "absl/strings/str_format.h"
29#include "absl/strings/str_join.h"
30#include "absl/strings/string_view.h"
31#include "absl/types/span.h"
55 result.
values.push_back(value);
62 result.
values.push_back(included_min);
63 result.
values.push_back(included_max);
70 result.
values.push_back(0);
71 result.
values.push_back(1);
134 if (!domain.
values.empty()) {
144 const int64_t imin =
values[0];
145 const int64_t imax =
values[1];
160 if (interval_min > interval_max) {
166 values.push_back(interval_min);
167 values.push_back(interval_max);
170 if (
values[0] >= interval_min &&
values[1] <= interval_max)
return false;
185 std::vector<int64_t> new_values;
186 new_values.reserve(
values.size());
187 bool changed =
false;
188 for (
const int64_t val :
values) {
189 if (val > interval_max) {
193 if (val >= interval_min &&
194 (new_values.empty() || val != new_values.back())) {
195 new_values.push_back(val);
210 values.empty() ? std::numeric_limits<int64_t>::min() :
values[0];
212 values.empty() ? std::numeric_limits<int64_t>::max() :
values[1];
214 for (
const int64_t v : integers) {
215 if (v >= dmin && v <= dmax)
values.push_back(v);
223 const int64_t last =
values.back();
236 absl::flat_hash_set<int64_t> other_values(integers.begin(), integers.end());
237 std::vector<int64_t> new_values;
238 new_values.reserve(std::min(
values.size(), integers.size()));
239 bool changed =
false;
240 for (
const int64_t val :
values) {
241 if (other_values.contains(val)) {
242 if (new_values.empty() || val != new_values.back()) {
243 new_values.push_back(val);
279 bool changed =
false;
359 (
values.empty() || (
values[0] == std::numeric_limits<int64_t>::min() &&
360 values[1] == std::numeric_limits<int64_t>::max()));
376bool IntervalOverlapValues(int64_t lb, int64_t ub,
377 absl::Span<const int64_t> values) {
378 for (int64_t value : values) {
379 if (lb <= value && value <= ub) {
393 return IntervalOverlapValues(
values[0],
values[1], vec);
396 const std::vector<int64_t>& to_scan =
398 const absl::flat_hash_set<int64_t> container =
399 values.size() <= vec.size()
400 ? absl::flat_hash_set<int64_t>(vec.begin(), vec.end())
401 : absl::flat_hash_set<int64_t>(
values.begin(),
values.end());
402 for (int64_t value : to_scan) {
403 if (container.contains(value)) {
417 const int64_t dlb =
values[0];
418 const int64_t dub =
values[1];
419 return !(dub < lb || dlb > ub);
421 return IntervalOverlapValues(lb, ub,
values);
427 if (other.
values.empty()) {
449 const int64_t vmax =
values[1];
452 for (int64_t v =
values[0] + 1; v <= vmax; ++v) {
469 std::string prefix =
"";
471 prefix =
"fixed_set of ";
486 LOG(DFATAL) <<
"Error with float domain";
487 return "error_float";
492 return absl::StrCat(prefix,
"int");
494 return absl::StrCat(prefix,
"[",
values[0],
"..",
values[1],
"]");
496 }
else if (
values.size() == 1) {
497 return absl::StrCat(prefix,
values.back());
499 return absl::StrFormat(
"%s[%s]", prefix, absl::StrJoin(
values,
", "));
508 result.
values.push_back(value);
515 result.
values.push_back(imin);
516 result.
values.push_back(imax);
556 if (domain.
values.empty()) {
558 std::numeric_limits<int64_t>::max());
570 result.
floats.push_back(value);
577 result.
floats.push_back(lb);
578 result.
floats.push_back(ub);
592 return absl::StrFormat(
"%d",
values[0]);
594 return absl::StrFormat(
"[%d..%d]",
values[0],
values[1]);
596 return absl::StrFormat(
"[%s]", absl::StrJoin(
values,
", "));
602 std::string result =
"[";
603 for (
int i = 0; i <
variables.size(); ++i) {
605 result.append(i !=
variables.size() - 1 ?
", " :
"]");
610 return "VoidArgument";
612 return absl::StrCat(
floats[0]);
614 return absl::StrCat(
"[",
floats[0],
"..",
floats[1],
"]");
616 return absl::StrFormat(
"[%s]", absl::StrJoin(
floats,
", "));
618 LOG(FATAL) <<
"Unhandled case in DebugString " <<
static_cast<int>(
type);
631 DCHECK(
HasOneValue()) <<
"Value() called on unbound Argument: "
656 if (!domain.HasOneValue()) {
666 if (!var->domain.HasOneValue()) {
689 return value >=
values.front() && value <=
values.back();
691 return value ==
values.front();
703 CHECK_LT(pos,
values.size());
725 CHECK_LT(pos,
values.size());
730 return domains[pos].HasOneValue();
735 return variables[pos]->domain.HasOneValue();
775Variable::Variable(absl::string_view name_,
const Domain& domain_,
777 : name(name_), domain(domain_), temporary(temporary_), active(true) {
778 if (!domain.is_interval) {
779 gtl::STLSortAndRemoveDuplicates(&domain.values);
784 bool other_temporary) {
789 domain.IntersectWithDomain(other_domain);
795 return absl::StrFormat(
"% d",
domain.values.back());
797 return absl::StrFormat(
"%s(%s%s)%s",
name,
domain.DebugString(),
799 active ?
"" :
" [removed during presolve]");
807 const std::string presolve_status_str =
808 active ?
"" :
" [removed during presolve]";
809 const std::string symmetric_breaking_str =
811 const std::string redundant_str =
is_redundant ?
" redundant" :
"";
813 presolve_status_str, symmetric_breaking_str,
827 type =
"false_constraint";
860 std::vector<Annotation> args) {
895 LOG(INFO) <<
"Create INT_LIST";
931 ann.AppendAllVariables(vars);
951 return absl::StrFormat(
"[%s]", absl::StrJoin(
values,
", "));
955 std::string result =
"[";
956 for (
int i = 0; i <
variables.size(); ++i) {
958 result.append(i !=
variables.size() - 1 ?
", " :
"]");
965 LOG(FATAL) <<
"Unhandled case in DebugString " <<
static_cast<int>(
type);
985 absl::string_view
name, std::vector<Bounds>
bounds,
1005 return absl::StrFormat(
"output_var(%s)",
variable->name);
1007 return absl::StrFormat(
"output_array([%s] [%s])",
1021 bool defined,
bool set_is_fixed) {
1027 variables_.push_back(var);
1035 variables_.push_back(var);
1042 variables_.push_back(var);
1047 bool is_domain,
bool symmetry,
bool redundant) {
1049 new Constraint(
id, std::move(arguments), is_domain, symmetry, redundant);
1050 constraints_.push_back(constraint);
1054 std::vector<Argument> arguments) {
1055 AddConstraint(
id, std::move(arguments),
false,
false,
false);
1059 output_.push_back(std::move(
output));
1063 objective_ =
nullptr;
1082 std::string
output = absl::StrFormat(
"Model %s\nVariables\n", name_);
1083 for (
int i = 0; i < variables_.size(); ++i) {
1086 output.append(
"Constraints\n");
1087 for (
int i = 0; i < constraints_.size(); ++i) {
1088 if (constraints_[i] !=
nullptr) {
1092 if (objective_ !=
nullptr) {
1093 absl::StrAppendFormat(&
output,
"%s %s\n %s\n",
1094 maximize_ ?
"Maximize" :
"Minimize", objective_->name,
1096 }
else if (!float_objective_variables_.empty()) {
1097 absl::StrAppendFormat(&
output,
"%s [%s] * [%s] + %f\n %s\n",
1098 maximize_ ?
"Maximize" :
"Minimize",
1100 absl::StrJoin(float_objective_coefficients_,
", "),
1101 float_objective_offset_,
1105 absl::StrAppendFormat(&
output,
"Satisfy\n %s\n",
1108 output.append(
"Output\n");
1109 for (
int i = 0; i < output_.size(); ++i) {
1118 if (var->domain.empty()) {
1123 if (ct->type ==
"false_constraint") {
1134 SOLVER_LOG(logger_,
"Model ", model_.name());
1137 int num_bool_vars = 0;
1138 int num_int_vars = 0;
1139 int num_float_vars = 0;
1140 int num_set_vars = 0;
1141 for (
Variable* var : model_.variables()) {
1142 if (var->domain.is_float) {
1144 }
else if (var->domain.is_a_set) {
1146 }
else if (var->domain.display_as_boolean) {
1152 if (num_bool_vars > 0) {
1153 SOLVER_LOG(logger_,
" - boolean variables:", num_bool_vars);
1155 if (num_int_vars > 0) {
1156 SOLVER_LOG(logger_,
" - integer variables:", num_int_vars);
1158 if (num_float_vars > 0) {
1159 SOLVER_LOG(logger_,
" - float variables:", num_float_vars);
1161 if (num_set_vars > 0) {
1162 SOLVER_LOG(logger_,
" - set variables:", num_set_vars);
1167 int num_redundant_constraints = 0;
1168 int num_symmetry_breaking_constraints = 0;
1169 for (
Constraint*
const ct : model_.constraints()) {
1170 if (ct !=
nullptr && ct->active) {
1171 if (ct->is_redundant) {
1172 ++num_redundant_constraints;
1174 if (ct->is_symmetric_breaking) {
1175 ++num_symmetry_breaking_constraints;
1179 for (
const auto& it : constraints_per_type_) {
1180 SOLVER_LOG(logger_,
" - ", it.first,
": ", it.second.size());
1183 if (num_redundant_constraints > 0) {
1185 " - redundant constraints: ", num_redundant_constraints);
1187 if (num_symmetry_breaking_constraints > 0) {
1188 SOLVER_LOG(logger_,
" - symmetry breaking constraints: ",
1189 num_symmetry_breaking_constraints);
1191 if (model_.objective() ==
nullptr) {
1192 SOLVER_LOG(logger_,
" - Satisfaction problem");
1195 (model_.maximize() ?
"Maximization" :
"Minimization"),
1202 constraints_per_type_.clear();
1203 constraints_per_variables_.clear();
1204 for (
Constraint*
const ct : model_.constraints()) {
1205 if (ct !=
nullptr && ct->active) {
1206 constraints_per_type_[ct->type].push_back(ct);
1207 absl::flat_hash_set<const Variable*> marked;
1208 for (
const Argument& arg : ct->arguments) {
1213 for (
const Variable*
const var : marked) {
1214 constraints_per_variables_[var].push_back(ct);
1228 out->push_back(ann);
void PrintStatistics() const
void AddConstraint(absl::string_view id, std::vector< Argument > arguments, bool is_domain, bool symmetry, bool redundant)
const std::vector< SolutionOutputSpecs > & output() const
Variable * AddVariable(absl::string_view name, const Domain &domain, bool defined, bool set_is_fixed=false)
Variable * AddFloatConstant(double value)
std::string DebugString() const
void AddOutput(SolutionOutputSpecs output)
void Maximize(Variable *obj, std::vector< Annotation > search_annotations)
void Satisfy(std::vector< Annotation > search_annotations)
const std::string & name() const
const std::vector< Annotation > & search_annotations() const
bool IsInconsistent() const
void Minimize(Variable *obj, std::vector< Annotation > search_annotations)
Variable * AddConstant(int64_t value)
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
void STLDeleteElements(T *container)
void FlattenAnnotations(const Annotation &ann, std::vector< Annotation > *out)
std::string JoinDebugStringPtr(const std::vector< T > &v, absl::string_view separator)
std::string JoinNameFieldPtr(const std::vector< T > &v, absl::string_view separator)
std::string JoinDebugString(const std::vector< T > &v, absl::string_view separator)
static Annotation String(absl::string_view str)
static Annotation FunctionCall(absl::string_view id)
void AppendAllVariables(std::vector< Variable * > *vars) const
static Annotation FunctionCallWithArguments(absl::string_view id, std::vector< Annotation > args)
static Annotation Empty()
static Annotation AnnotationList(std::vector< Annotation > list)
static Annotation IntegerList(const std::vector< int64_t > &values)
static Annotation VarRefArray(std::vector< Variable * > variables)
static Annotation Identifier(absl::string_view id)
std::vector< Variable * > variables
std::vector< int64_t > values
bool IsFunctionCallWithIdentifier(absl::string_view identifier) const
static Annotation VarRef(Variable *var)
static Annotation IntegerValue(int64_t value)
static Annotation Interval(int64_t interval_min, int64_t interval_max)
std::vector< Annotation > annotations
std::string DebugString() const
std::vector< Variable * > variables
static Argument FloatList(std::vector< double > floats)
static Argument FloatValue(double value)
std::vector< Domain > domains
bool HasOneValueAt(int pos) const
Variable * VarAt(int pos) const
std::string DebugString() const
bool IsArrayOfValues() const
static Argument VoidArgument()
static Argument IntegerValue(int64_t value)
int64_t ValueAt(int pos) const
static Argument Interval(int64_t imin, int64_t imax)
static Argument FromDomain(const Domain &domain)
static Argument IntegerList(std::vector< int64_t > values)
static Argument VarRef(Variable *var)
static Argument FloatInterval(double lb, double ub)
std::vector< double > floats
bool Contains(int64_t value) const
static Argument DomainList(std::vector< Domain > domains)
static Argument VarRefArray(std::vector< Variable * > vars)
std::vector< int64_t > values
bool is_symmetric_breaking
std::string DebugString() const
void RemoveArg(int arg_pos)
std::vector< Argument > arguments
static Domain AllFloats()
bool IntersectWithSingleton(int64_t value)
static Domain IntegerList(std::vector< int64_t > values)
bool OverlapsDomain(const Domain &other) const
static Domain IntegerValue(int64_t value)
std::string DebugString() const
static Domain FloatValue(double value)
bool IntersectWithInterval(int64_t interval_min, int64_t interval_max)
bool IntersectWithDomain(const Domain &domain)
bool SetEmptyFloatDomain()
static Domain EmptyDomain()
bool IntersectWithFloatDomain(const Domain &domain)
bool RemoveValue(int64_t value)
static Domain FloatInterval(double lb, double ub)
std::vector< int64_t > values
static Domain SetOfBoolean()
static Domain Interval(int64_t included_min, int64_t included_max)
static Domain SetOfAllInt64()
static Domain SetOfInterval(int64_t included_min, int64_t included_max)
bool Contains(int64_t value) const
bool OverlapsIntInterval(int64_t lb, int64_t ub) const
std::vector< double > float_values
static Domain SetOfIntegerList(std::vector< int64_t > values)
static Domain SetOfIntegerValue(int64_t value)
bool IntersectWithListOfIntegers(absl::Span< const int64_t > integers)
bool OverlapsIntList(const std::vector< int64_t > &vec) const
std::string DebugString() const
std::vector< Variable * > flat_variables
static SolutionOutputSpecs MultiDimensionalArray(absl::string_view name, std::vector< Bounds > bounds, std::vector< Variable * > flat_variables, bool display_as_boolean)
std::string DebugString() const
static SolutionOutputSpecs VoidOutput()
static SolutionOutputSpecs SingleVariable(absl::string_view name, Variable *variable, bool display_as_boolean)
std::vector< Bounds > bounds
bool Merge(absl::string_view other_name, const Domain &other_domain, bool other_temporary)
std::string DebugString() const
#define SOLVER_LOG(logger,...)