Skip to content

Commit 2ebe759

Browse files
committed
[FZ] support array_var_set_element
1 parent ba26576 commit 2ebe759

File tree

2 files changed

+1742
-1748
lines changed

2 files changed

+1742
-1748
lines changed

ortools/flatzinc/cp_model_fz_solver.cc

Lines changed: 62 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <utility>
2424
#include <vector>
2525

26+
#include "absl/container/btree_map.h"
2627
#include "absl/container/flat_hash_map.h"
2728
#include "absl/container/flat_hash_set.h"
2829
#include "absl/flags/flag.h"
@@ -98,6 +99,14 @@ struct CpModelProtoWithMapping {
9899
// Returns a constant CpModelProto variable created on-demand.
99100
int LookupConstant(int64_t value);
100101

102+
void AddImplication(absl::Span<const int> e, int l) {
103+
ConstraintProto* ct = proto.add_constraints();
104+
for (int e_lit : e) {
105+
ct->add_enforcement_literal(e_lit);
106+
}
107+
ct->mutable_bool_and()->add_literals(l);
108+
}
109+
101110
// Convert a flatzinc argument to a variable or a list of variable.
102111
// Note that we always encode a constant argument with a constant variable.
103112
int LookupVar(const fz::Argument& argument);
@@ -1382,7 +1391,7 @@ void CpModelProtoWithMapping::PutSetBooleansInCommonDomain(
13821391
}
13831392
}
13841393
}
1385-
// array_var_set_element, set_le, set_le_reif, set_lt, set_lt_reif.
1394+
// set_le, set_le_reif, set_lt, set_lt_reif.
13861395
void CpModelProtoWithMapping::ExtractSetConstraint(
13871396
const fz::Constraint& fz_ct) {
13881397
if (fz_ct.type == "array_set_element") {
@@ -1452,6 +1461,52 @@ void CpModelProtoWithMapping::ExtractSetConstraint(
14521461
false_range->mutable_linear());
14531462
}
14541463
}
1464+
} else if (fz_ct.type == "array_var_set_element") {
1465+
const int index = LookupVar(fz_ct.arguments[0]);
1466+
const Domain index_domain = ReadDomainFromProto(proto.variables(index));
1467+
const int64_t min_index = index_domain.Min();
1468+
1469+
const std::shared_ptr<SetVariable> target_var =
1470+
LookupSetVar(fz_ct.arguments[2]);
1471+
absl::btree_map<int64_t, int> target_values_to_literals;
1472+
for (int i = 0; i < target_var->sorted_values.size(); ++i) {
1473+
target_values_to_literals[target_var->sorted_values[i]] =
1474+
TrueLiteral(target_var->var_indices[i]);
1475+
}
1476+
1477+
BoolArgumentProto* exactly_one =
1478+
proto.add_constraints()->mutable_exactly_one();
1479+
for (const int64_t value : index_domain.Values()) {
1480+
const int index_literal = GetOrCreateLiteralForVarEqValue(index, value);
1481+
exactly_one->add_literals(index_literal);
1482+
1483+
std::shared_ptr<SetVariable> set_var =
1484+
LookupSetVarAt(fz_ct.arguments[1], value - min_index);
1485+
absl::btree_map<int64_t, int> set_values_to_literals;
1486+
for (int i = 0; i < set_var->sorted_values.size(); ++i) {
1487+
set_values_to_literals[set_var->sorted_values[i]] =
1488+
TrueLiteral(set_var->var_indices[i]);
1489+
}
1490+
1491+
for (const auto& [value, set_literal] : set_values_to_literals) {
1492+
const auto it = target_values_to_literals.find(value);
1493+
if (it == target_values_to_literals.end()) {
1494+
// index is selected => set_literal[value] is false.
1495+
AddImplication({index_literal}, FalseLiteral(set_literal));
1496+
} else {
1497+
// index is selected => set_literal[value] == target_literal[value].
1498+
AddImplication({index_literal, set_literal}, it->second);
1499+
AddImplication({index_literal, it->second}, set_literal);
1500+
}
1501+
}
1502+
1503+
// Properly handle the case where not all target literals are reached.
1504+
for (const auto& [value, target_literal] : target_values_to_literals) {
1505+
if (!set_values_to_literals.contains(value)) {
1506+
AddImplication({index_literal}, FalseLiteral(target_literal));
1507+
}
1508+
}
1509+
}
14551510
} else if (fz_ct.type == "set_card") {
14561511
std::shared_ptr<SetVariable> set_var = LookupSetVar(fz_ct.arguments[0]);
14571512
VarOrValue var_or_value = LookupVarOrValue(fz_ct.arguments[1]);
@@ -1553,8 +1608,8 @@ void CpModelProtoWithMapping::ExtractSetConstraint(
15531608
FillDomainInProto(set_domain, domain_reduction_ct->mutable_linear());
15541609

15551610
// Then propagate any remove from the set domain to the variable domain.
1556-
// Note that the reif part is an equivalence. We do not want false implies
1557-
// var in set (which contains the value of the var).
1611+
// Note that the reif part is an equivalence. We do not want false
1612+
// implies var in set (which contains the value of the var).
15581613
for (int i = 0; i < set_var->sorted_values.size(); ++i) {
15591614
const int64_t value = set_var->sorted_values[i];
15601615
const int set_value_literal = set_var->var_indices[i];
@@ -1652,25 +1707,17 @@ void CpModelProtoWithMapping::ExtractSetConstraint(
16521707
{LookupSetVar(fz_ct.arguments[0]), LookupSetVar(fz_ct.arguments[1])},
16531708
{&x_literals, &y_literals});
16541709

1655-
const auto add_implication = [&](absl::Span<const int> e, int l) {
1656-
ConstraintProto* ct = proto.add_constraints();
1657-
for (int e_lit : e) {
1658-
ct->add_enforcement_literal(e_lit);
1659-
}
1660-
ct->mutable_bool_and()->add_literals(l);
1661-
};
1662-
16631710
// Implement the comparison logic.
16641711
for (int i = 0; i < x_literals.size(); ++i) {
16651712
const int x_lit = x_literals[i];
16661713
const int y_lit = y_literals[i];
16671714
if (fz_ct.type == "set_subset") {
1668-
add_implication({x_lit}, y_lit);
1715+
AddImplication({x_lit}, y_lit);
16691716
} else if (fz_ct.type == "set_superset") {
1670-
add_implication({y_lit}, x_lit);
1717+
AddImplication({y_lit}, x_lit);
16711718
} else if (fz_ct.type == "set_eq") {
1672-
add_implication({x_lit}, y_lit);
1673-
add_implication({y_lit}, x_lit);
1719+
AddImplication({x_lit}, y_lit);
1720+
AddImplication({y_lit}, x_lit);
16741721
} else {
16751722
LOG(FATAL) << "Unsupported " << fz_ct.type;
16761723
}

0 commit comments

Comments
 (0)