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