1 // This example is the C++ implementation of ./examples/clingo/heuristic/. To
2 // run this example, use the instances provided with the other example.
3
4 #include <clingo.hh>
5 #include <unordered_map>
6 #include <limits>
7
8 namespace Detail {
9 template <typename H, typename I>
heap_rise_(H & h,I x)10 void heap_rise_(H &h, I x) {
11 if (x > 0) {
12 auto y = (x - 1) / 2;
13 if (h.priority(y) < h.priority(x)) {
14 h.swap(x, y);
15 heap_rise_(h, y);
16 }
17 }
18 }
19 template <typename H, typename I>
heap_sink_(H & h,I i)20 void heap_sink_(H &h, I i) {
21 auto j = i, l = 2 * j + 1, r = l + 1, s = h.size();
22 if (l < s && h.priority(j) < h.priority(l)) {
23 j = l;
24 }
25 if (r < s && h.priority(j) < h.priority(r)) {
26 j = r;
27 }
28 if (i != j) {
29 h.swap(i, j);
30 heap_sink_(h, j);
31 }
32 }
33 }
34
35 template <typename H>
heap_init(H & h)36 void heap_init(H &h) {
37 for (auto s = h.size(), i = s / 2; i > 0; --i) {
38 Detail::heap_sink_(h, i - 1);
39 }
40 }
41
42 template <typename H, typename I>
heap_remove(H & h,I x)43 void heap_remove(H &h, I x) {
44 auto y = h.size() - 1;
45 if (x != y) {
46 auto px = h.priority(x), py = h.priority(y);
47 h.swap(x, y);
48 h.pop();
49 if (px < py) { Detail::heap_rise_(h, x); }
50 else if (py < px) { Detail::heap_sink_(h, x); }
51 }
52 }
53
54 template <typename H, typename I, typename P>
heap_update(H & h,I x,P p)55 void heap_update(H &h, I x, P p) {
56 auto o = h.priority(x);
57 h.priority(x) = p;
58 if (o < p) { Detail::heap_rise_(h, x); }
59 else if (p < o) { Detail::heap_sink_(h, x); }
60 }
61
62 template <typename H, typename... V>
heap_insert(H & h,V &&...x)63 void heap_insert(H &h, V &&...x) {
64 h.push(std::forward<V>(x)...);
65 Detail::heap_rise_(h, h.size() - 1);
66 }
67
68 struct Entry {
69 int degree;
70 unsigned position;
71 };
72 using EntryMap = std::unordered_map<Clingo::Symbol, Entry>;
73
74 class Heap {
75 public:
priority(unsigned x)76 int &priority(unsigned x) {
77 assert(data[x]->second.position == x);
78 return data[x]->second.degree;
79 }
size() const80 unsigned size() const {
81 return data.size();
82 }
swap(unsigned x,unsigned y)83 void swap(unsigned x, unsigned y) {
84 using std::swap;
85 swap(data[x], data[y]);
86 data[x]->second.position = x;
87 data[y]->second.position = y;
88 }
pop()89 void pop() {
90 data.back()->second.position = std::numeric_limits<unsigned>::max();
91 data.pop_back();
92 }
push(EntryMap::iterator x)93 void push(EntryMap::iterator x) {
94 data.emplace_back(x);
95 data.back()->second.position = data.size() - 1;
96 }
97
front() const98 EntryMap::iterator front() const {
99 return data.front();
100 }
101
102 private:
103 std::vector<EntryMap::iterator> data;
104 };
105
106 class State {
107 public:
add(Clingo::Symbol sym,int x)108 void add(Clingo::Symbol sym, int x) {
109 auto ret = entries_.emplace(sym, Entry{0, 0});
110 if (ret.second) { heap_.push(ret.first); }
111 ret.first->second.degree += x;
112 }
init()113 void init() {
114 heap_init(heap_);
115 }
update(Clingo::Symbol sym,int x)116 void update(Clingo::Symbol sym, int x) {
117 auto &e = entries_.at(sym);
118 if (member(e)) { heap_update(heap_, e.position, e.degree + x); }
119 else { e.degree += x; }
120 }
remove(Clingo::Symbol sym)121 void remove(Clingo::Symbol sym) {
122 auto e = entries_.at(sym);
123 if (member(e)) { heap_remove(heap_, e.position); }
124 }
insert(Clingo::Symbol sym)125 void insert(Clingo::Symbol sym) {
126 auto e = entries_.find(sym);
127 if (!member(e->second)) { heap_insert(heap_, e); }
128 }
empty() const129 bool empty() const {
130 return heap_.size() == 0;
131 }
peak() const132 Clingo::Symbol peak() const {
133 return heap_.front()->first;
134 }
135
136 private:
member(Entry & e)137 bool member(Entry &e) {
138 return e.position < std::numeric_limits<unsigned>::max();
139 }
140
141 private:
142 EntryMap entries_;
143 Heap heap_;
144 };
145
146 class ColoringHeuristic : public Clingo::Heuristic {
147 public:
init(Clingo::PropagateInit & init)148 void init(Clingo::PropagateInit &init) override {
149 states_.resize(init.number_of_threads());
150 for (auto &&a : init.symbolic_atoms()) {
151 if (a.match("edge", 2)) {
152 auto u = a.symbol().arguments()[0];
153 auto v = a.symbol().arguments()[1];
154 graph_[u].second.emplace_back(v);
155 graph_[v].second.emplace_back(u);
156 for (auto &&state : states_) {
157 state.add(u, 1);
158 state.add(v, 1);
159 }
160 }
161 else if (a.match("assign", 2)) {
162 auto u = a.symbol().arguments()[0];
163 auto l = init.solver_literal(a.literal());
164 init.add_watch(l);
165 assign_[l].emplace_back(u);
166 graph_[u].first.emplace_back(l);
167 for (auto &&state : states_) {
168 state.add(u, 0);
169 }
170 }
171 }
172 for (auto &&state : states_) {
173 state.init();
174 }
175 }
176
propagate(Clingo::PropagateControl & ctl,Clingo::LiteralSpan changes)177 void propagate(Clingo::PropagateControl &ctl, Clingo::LiteralSpan changes) override {
178 auto &&state = states_[ctl.thread_id()];
179 for (auto &&l : changes) {
180 for (auto &&u : assign_[l]) {
181 state.remove(u);
182 for (auto &&v : graph_[u].second) {
183 state.update(v, -1);
184 }
185 }
186 }
187 }
188
undo(Clingo::PropagateControl const & ctl,Clingo::LiteralSpan changes)189 void undo(Clingo::PropagateControl const &ctl, Clingo::LiteralSpan changes) noexcept override {
190 auto &&state = states_[ctl.thread_id()];
191 for (auto &&l : changes) {
192 for (auto &&u : assign_[l]) {
193 state.insert(u);
194 for (auto &&v : graph_[u].second) {
195 state.update(v, 1);
196 }
197 }
198 }
199 }
200
decide(Clingo::id_t thread_id,Clingo::Assignment const & assign,Clingo::literal_t)201 Clingo::literal_t decide(Clingo::id_t thread_id, Clingo::Assignment const &assign, Clingo::literal_t) override {
202 auto &&state = states_[thread_id];
203 if (!state.empty()) {
204 auto sym = state.peak();
205 for (auto &&l : graph_[sym].first) {
206 if (assign.truth_value(l) == Clingo::TruthValue::Free) {
207 return l;
208 }
209 }
210 }
211 return 0;
212 }
213
214 private:
215 std::unordered_map<Clingo::Symbol, std::pair<std::vector<Clingo::literal_t>, Clingo::SymbolVector>> graph_;
216 std::unordered_map<Clingo::literal_t, Clingo::SymbolVector> assign_;
217 std::vector<State> states_;
218 };
219
220 class GraphColorizer : Clingo::Application {
221 public:
run(int argc,char const ** argv)222 static int run(int argc, char const **argv) {
223 GraphColorizer gc;
224 return Clingo::clingo_main(gc, {argv+1, argv+argc});
225 }
226 private:
program_name() const227 char const *program_name() const noexcept override {
228 return "colorizer";
229 };
version() const230 char const *version() const noexcept override { return "1.0"; }
main(Clingo::Control & ctl,Clingo::StringSpan files)231 void main(Clingo::Control &ctl, Clingo::StringSpan files) override {
232 for (auto &&file : files) {
233 ctl.load(file);
234 }
235 if (files.empty()) { ctl.load("-"); }
236 ctl.add("base", {},
237 "1 { assign(U,C) : color(C) } 1 :- vertex(U).\n"
238 " :- edge(U,V), assign(U,C), assign(V,C).\n");
239 ColoringHeuristic heu;
240 ctl.register_propagator(heu);
241 ctl.ground({{"base", {}}});
242 ctl.solve(Clingo::LiteralSpan{}, nullptr, false, false).get();
243 }
244 };
245
main(int argc,char const ** argv)246 int main(int argc, char const **argv) {
247 return GraphColorizer::run(argc, argv);
248 }
249