Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
symmetry.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#ifndef ORTOOLS_SAT_SYMMETRY_H_
15#define ORTOOLS_SAT_SYMMETRY_H_
16
17#include <cstdint>
18#include <memory>
19#include <vector>
20
21#include "absl/types/span.h"
25#include "ortools/util/stats.h"
26
27namespace operations_research {
28namespace sat {
29
30// This class implements more or less the strategy described in the paper:
31// Devriendt J., Bogaerts B., De Cat B., Denecker M., Mears C. "Symmetry
32// propagation: Improved Dynamic Symmetry Breaking in SAT", 2012,
33// IEEE 24th International Conference on Tools with Artificial Intelligence.
34//
35// Basically, each time a literal is propagated, this class tries to detect
36// if another literal could also be propagated by symmetry. Note that this uses
37// a heuristic in order to be efficient and that it is not exhaustive in the
38// sense that it doesn't detect all possible propagations.
39//
40// Algorithm details:
41//
42// Given the current solver trail (i.e. the assigned literals and their
43// assignment order) the idea is to compute (as efficiently as possible) for
44// each permutation added to this class what is called the first (under the
45// trail assignment order) non-symmetric literal. A literal 'l' is said to be
46// non-symmetric under a given assignment and for a given permutation 'p' if
47// 'l' is assigned to true but not 'p(l)'.
48//
49// If a first non-symmetric literal 'l' for a permutation 'p' is not a decision,
50// then:
51// - Because it is not a decision, 'l' has been implied by a reason formed by
52// literals assigned to true at lower trail indices.
53// - Because this is the first non-symmetric literal for 'p', the permuted
54// reason only contains literal that are also assigned to true.
55// - Because of this, 'p(l)' is also implied by the current assignment.
56// Of course, this assume that p is a symmetry of the full problem.
57// Note that if it is already assigned to false, then we have a conflict.
58//
59// TODO(user): Implement the optimizations mentioned in the paper?
60// TODO(user): Instrument and see if the code can be optimized.
62 public:
64
65 // This type is neither copyable nor movable.
68
69 ~SymmetryPropagator() override;
70
71 bool Propagate(Trail* trail) final;
72 void Untrail(const Trail& trail, int trail_index) final;
73 absl::Span<const Literal> Reason(const Trail& trail, int trail_index,
74 int64_t conflict_id) const final;
75
76 // Adds a new permutation to this symmetry propagator. The ownership is
77 // transferred. This must be an integer permutation such that:
78 // - Its domain is [0, 2 * num_variables) and corresponds to the index
79 // representation of the literals over num_variables variables.
80 // - It must be compatible with the negation, for any literal l; not(p(l))
81 // must be the same as p(not(l)), where p(x) represents the image of x by
82 // the permutation.
83 //
84 // Remark: Any permutation which is a symmetry of the main SAT problem can be
85 // added here. However, since the number of permutations is usually not
86 // manageable, a good alternative is to only add the generators of the
87 // permutation group. It is also important to add permutations with a support
88 // as small as possible.
89 //
90 // TODO(user): Currently this can only be called before PropagateNext() is
91 // called (DCHECKed). Not sure if we need more incrementality though.
92 void AddSymmetry(std::unique_ptr<SparsePermutation> permutation);
93 int num_permutations() const { return permutations_.size(); }
94
95 // When the loader runs the symmetry detection code, it can only take into
96 // account the variables that existed at that time, even if some code paths
97 // will create new booleans later. Those new booleans might not be invariant
98 // under the symmetry that were detected, which might lead this propagator to
99 // propagate wrong clauses. This method allows to specify that all literals
100 // from 0 to `num_literals-1` were known when the symmetry detection code ran.
101 //
102 // For example, suppose we have a model with three literals: lit0, lit1 and
103 // lit2 and it's symmetrical with respect to swapping lit0 and lit1. Thus if
104 // (lit0, lit2) is a clause, we can deduce (lit1, lit2).
105 //
106 // Then, suppose we add a new literal: lit3 = lit0 & lit2.
107 //
108 // Now we can still deduce (lit1, lit2) from (lit0, lit2), but we cannot
109 // deduce (lit1, lit3) from (lit0, lit3). `num_literals` being set to 3 would
110 // allow the propagator to handle lit2 and lit3 differently, even if both are
111 // not part of any symmetry permutation.
112 void SetNumLiterals(int num_literals) {
113 num_literals_with_knonw_symmetry_ = num_literals;
114 }
115
116 // Visible for testing.
117 //
118 // Permutes a list of literals from input into output using the permutation
119 // with given index. This uses tmp_literal_mapping_ and has a complexity in
120 // O(permutation_support + input_size).
121 void Permute(int index, absl::Span<const Literal> input,
122 std::vector<Literal>* output) const;
123
124 private:
125 // Propagates the literal at propagation_trail_index_ from the trail.
126 bool PropagateNext(Trail* trail);
127
128 // The permutations.
129 // The index of a permutation is its position in this vector.
130 std::vector<std::unique_ptr<SparsePermutation>> permutations_;
131
132 // Reverse mapping (source literal) -> list of (permutation_index, image).
133 struct ImageInfo {
134 ImageInfo(int p, Literal i) : permutation_index(p), image(i) {}
135
136 int permutation_index;
137 Literal image;
138 };
140
141 // For each permutation p, we maintain the list of all assigned literals
142 // affected by p whose trail index is < propagation_trail_index_; sorted by
143 // trail index. Next to each such literal, we also store:
144 struct AssignedLiteralInfo {
145 AssignedLiteralInfo(Literal l, Literal i, int index)
146 : literal(l), image(i), first_non_symmetric_info_index_so_far(index) {}
147
148 // The literal in question (assigned to true and in the support of p).
149 Literal literal;
150
151 // The image by p of the literal above.
152 Literal image;
153
154 // Previous AssignedLiteralInfos are considered 'symmetric' iff both their
155 // 'literal' and 'image' were assigned to true at the time the current
156 // AssignedLiteralInfo's literal was assigned (i.e. earlier in the trail).
157 int first_non_symmetric_info_index_so_far;
158 };
159 std::vector<std::vector<AssignedLiteralInfo>> permutation_trails_;
160
161 // Adds an AssignedLiteralInfo to the given permutation trail.
162 // Returns false if there is a non-symmetric literal in this trail with its
163 // image not already assigned to true by the solver.
164 bool Enqueue(const Trail& trail, Literal literal, Literal image,
165 std::vector<AssignedLiteralInfo>* p_trail);
166
167 // The identity permutation over all the literals.
168 // This is temporary modified to encode a sparse permutation and then always
169 // restored to the identity.
170 mutable util_intops::StrongVector<LiteralIndex, Literal> tmp_literal_mapping_;
171
172 // Symmetry reason indexed by trail_index.
173 struct ReasonInfo {
174 int source_trail_index;
175 int symmetry_index;
176 };
177 std::vector<ReasonInfo> reasons_;
178
179 mutable StatsGroup stats_;
180 int num_propagations_;
181 int num_conflicts_;
182 int num_literals_with_knonw_symmetry_;
183};
184
185} // namespace sat
186} // namespace operations_research
187
188#endif // ORTOOLS_SAT_SYMMETRY_H_
SatPropagator(const std::string &name)
Definition sat_base.h:786
void AddSymmetry(std::unique_ptr< SparsePermutation > permutation)
Definition symmetry.cc:47
SymmetryPropagator(const SymmetryPropagator &)=delete
void Untrail(const Trail &trail, int trail_index) final
Definition symmetry.cc:149
absl::Span< const Literal > Reason(const Trail &trail, int trail_index, int64_t conflict_id) const final
Definition symmetry.cc:162
SymmetryPropagator & operator=(const SymmetryPropagator &)=delete
void Permute(int index, absl::Span< const Literal > input, std::vector< Literal > *output) const
Definition symmetry.cc:207
OR-Tools root namespace.
static int input(yyscan_t yyscanner)