Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
gate_utils.h
Go to the documentation of this file.
1// Copyright 2010-2025 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14// Functions to manipulate a "small" truth table where
15// f(X0, X1, X2) is true iff bitmask[X0 + (X1 << 1) + (X2 << 2)] is true.
16#ifndef ORTOOLS_SAT_GATE_UTILS_H_
17#define ORTOOLS_SAT_GATE_UTILS_H_
18
19#include <cstdint>
20
21#include "absl/log/check.h"
22#include "absl/types/span.h"
24
26
27using SmallBitset = uint32_t;
28
29// This works for num_bits == 32 too.
30inline SmallBitset GetNumBitsAtOne(int num_bits) {
31 return ~SmallBitset(0) >> (32 - (1 << num_bits));
32}
33
34// Sort the key and modify the truth table accordingly.
35//
36// Note that we don't deal with identical key here, but the function
37// CanonicalizeFunctionTruthTable() does, and that is sufficient for our use
38// case.
39template <typename VarOrLiteral>
40void CanonicalizeTruthTable(absl::Span<VarOrLiteral> key,
41 SmallBitset& bitmask) {
42 const int num_bits = key.size();
43 for (int i = 0; i < num_bits; ++i) {
44 for (int j = i + 1; j < num_bits; ++j) {
45 if (key[i] <= key[j]) continue;
46
47 std::swap(key[i], key[j]);
48
49 // We need to swap bit positions i and j in bitmask.
50 SmallBitset new_bitmask = 0;
51 for (int p = 0; p < (1 << num_bits); ++p) {
52 const int value_i = (p >> i) & 1;
53 const int value_j = (p >> j) & 1;
54 int new_p = p;
55 new_p ^= (value_i << i) ^ (value_j << j); // Clear.
56 new_p ^= (value_i << j) ^ (value_j << i); // Swap.
57 new_bitmask |= ((bitmask >> p) & 1) << new_p;
58 }
59 bitmask = new_bitmask;
60 }
61 }
62 DCHECK(std::is_sorted(key.begin(), key.end()));
63}
64
65// Given a clause, return the truth table corresponding to it.
66// Namely, a single value should be excluded.
67inline void FillKeyAndBitmask(absl::Span<const Literal> clause,
68 absl::Span<BooleanVariable> key,
69 SmallBitset& bitmask) {
70 CHECK_EQ(clause.size(), key.size());
71 const int num_bits = clause.size();
72 SmallBitset bit_to_remove = 0;
73 for (int i = 0; i < num_bits; ++i) {
74 key[i] = clause[i].Variable();
75 bit_to_remove |= (clause[i].IsPositive() ? 0 : 1) << i;
76 }
77 CHECK_LT(bit_to_remove, (1 << num_bits));
78 bitmask = GetNumBitsAtOne(num_bits);
79 bitmask ^= SmallBitset(1) << bit_to_remove;
81}
82
83// Returns true iff the truth table encoded in bitmask encode a function
84// Xi = f(Xj, j != i);
85inline bool IsFunction(int i, int num_bits, SmallBitset truth_table) {
86 DCHECK_GE(i, 0);
87 DCHECK_LT(i, num_bits);
88
89 // We need to check that there is never two possibilities for Xi.
90 for (int p = 0; p < (1 << num_bits); ++p) {
91 if ((truth_table >> p) & (truth_table >> (p ^ (1 << i))) & 1) return false;
92 }
93
94 return true;
95}
96
97inline int AddHoleAtPosition(int i, int bitset) {
98 return (bitset & ((1 << i) - 1)) + ((bitset >> i) << (i + 1));
99}
100
101inline int RemoveFixedInput(int i, bool at_true,
102 absl::Span<LiteralIndex> inputs,
103 int& int_function_values) {
104 DCHECK_LT(i, inputs.size());
105 const int value = at_true ? 1 : 0;
106
107 // Re-compute the bitset.
108 SmallBitset values = int_function_values;
109 SmallBitset new_truth_table = 0;
110 const int new_size = inputs.size() - 1;
111 for (int p = 0; p < (1 << new_size); ++p) {
112 const int extended_p = AddHoleAtPosition(i, p) | (value << i);
113 new_truth_table |= ((values >> extended_p) & 1) << p;
114 }
115 int_function_values = new_truth_table;
116
117 for (int j = i + 1; j < inputs.size(); ++j) {
118 inputs[j - 1] = inputs[j];
119 }
120 return new_size;
121}
122
123// The function is target = function_values[inputs as bit position].
124//
125// TODO(user): This can be optimized with more bit twiddling if needed.
126inline int CanonicalizeFunctionTruthTable(LiteralIndex& target,
127 absl::Span<LiteralIndex> inputs,
128 int& int_function_values) {
129 // We want to fit on an int.
130 CHECK_LE(inputs.size(), 4);
131
132 // We assume smaller type.
133 SmallBitset function_values = int_function_values;
134
135 const int num_bits = inputs.size();
136 CHECK_LE(num_bits, 4); // Truth table must fit on an int.
137
138 // Make sure all inputs are positive.
139 for (int i = 0; i < num_bits; ++i) {
140 if (Literal(inputs[i]).IsPositive()) continue;
141
142 inputs[i] = Literal(inputs[i]).NegatedIndex();
143
144 // Position p go to position (p ^ (1 << i)).
145 SmallBitset new_truth_table = 0;
146 const SmallBitset to_xor = 1 << i;
147 for (int p = 0; p < (1 << num_bits); ++p) {
148 new_truth_table |= ((function_values >> p) & 1) << (p ^ to_xor);
149 }
150 function_values = new_truth_table;
151 CHECK_EQ(function_values >> (1 << num_bits), 0);
152 }
153
154 // Sort the inputs now.
155 CanonicalizeTruthTable<LiteralIndex>(inputs, function_values);
156 CHECK_EQ(function_values >> (1 << num_bits), 0);
157
158 // Merge identical variables.
159 for (int i = 0; i < inputs.size(); ++i) {
160 for (int j = i + 1; j < inputs.size();) {
161 if (inputs[i] == inputs[j]) {
162 // Lets remove input j.
163 for (int k = j; k + 1 < inputs.size(); ++k) inputs[k] = inputs[k + 1];
164 inputs.remove_suffix(1);
165
166 SmallBitset new_truth_table = 0;
167 for (int p = 0; p < (1 << inputs.size()); ++p) {
168 int extended_p = AddHoleAtPosition(j, p);
169 extended_p |= ((p >> i) & 1) << j; // fill it with bit i.
170 new_truth_table |= ((function_values >> extended_p) & 1) << p;
171 }
172 function_values = new_truth_table;
173 } else {
174 ++j;
175 }
176 }
177 }
178
179 // Lower arity?
180 // This can happen if the output do not depend on one of the inputs.
181 for (int i = 0; i < inputs.size();) {
182 bool remove = true;
183 for (int p = 0; p < (1 << inputs.size()); ++p) {
184 if (((function_values >> p) & 1) !=
185 ((function_values >> (p ^ (1 << i))) & 1)) {
186 remove = false;
187 break;
188 }
189 }
190 if (remove) {
191 // Lets remove input i.
192 for (int k = i; k + 1 < inputs.size(); ++k) inputs[k] = inputs[k + 1];
193 inputs.remove_suffix(1);
194
195 SmallBitset new_truth_table = 0;
196 for (int p = 0; p < (1 << inputs.size()); ++p) {
197 const int extended_p = AddHoleAtPosition(i, p);
198 new_truth_table |= ((function_values >> extended_p) & 1) << p;
199 }
200 function_values = new_truth_table;
201 } else {
202 ++i;
203 }
204 }
205
206 // If we have x = f(a,b,c) and not(y) = f(a,b,c) with the same f, we have an
207 // equivalence, so we need to canonicallize both f() and not(f()) to the same
208 // function. For that we just always choose to have the lowest bit at zero.
209 if (function_values & 1) {
210 target = Literal(target).Negated();
211 const SmallBitset all_one = GetNumBitsAtOne(inputs.size());
212 function_values = function_values ^ all_one;
213 DCHECK_EQ(function_values >> (1 << inputs.size()), 0);
214 }
215 DCHECK_EQ(function_values & 1, 0);
216
217 int_function_values = function_values;
218 return inputs.size();
219}
220
221} // namespace operations_research::sat
222
223#endif // ORTOOLS_SAT_GATE_UTILS_H_
LiteralIndex NegatedIndex() const
Definition sat_base.h:93
int RemoveFixedInput(int i, bool at_true, absl::Span< LiteralIndex > inputs, int &int_function_values)
Definition gate_utils.h:101
bool IsFunction(int i, int num_bits, SmallBitset truth_table)
Definition gate_utils.h:85
SmallBitset GetNumBitsAtOne(int num_bits)
Definition gate_utils.h:30
void CanonicalizeTruthTable(absl::Span< VarOrLiteral > key, SmallBitset &bitmask)
Definition gate_utils.h:40
int AddHoleAtPosition(int i, int bitset)
Definition gate_utils.h:97
int CanonicalizeFunctionTruthTable(LiteralIndex &target, absl::Span< LiteralIndex > inputs, int &int_function_values)
Definition gate_utils.h:126
void FillKeyAndBitmask(absl::Span< const Literal > clause, absl::Span< BooleanVariable > key, SmallBitset &bitmask)
Definition gate_utils.h:67