Line | Branch | Decision | Exec | Source |
---|---|---|---|---|
1 | // Copyright 2019-2022 Cambridge Quantum Computing | |||
2 | // | |||
3 | // Licensed under the Apache License, Version 2.0 (the "License"); | |||
4 | // you may not use this file except in compliance with the License. | |||
5 | // You may obtain a copy of the License at | |||
6 | // | |||
7 | // http://www.apache.org/licenses/LICENSE-2.0 | |||
8 | // | |||
9 | // Unless required by applicable law or agreed to in writing, software | |||
10 | // distributed under the License is distributed on an "AS IS" BASIS, | |||
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
12 | // See the License for the specific language governing permissions and | |||
13 | // limitations under the License. | |||
14 | ||||
15 | #include "BruteForceColouring.hpp" | |||
16 | ||||
17 | #include <set> | |||
18 | #include <sstream> | |||
19 | #include <stdexcept> | |||
20 | #include <tkassert/Assert.hpp> | |||
21 | ||||
22 | #include "ColouringPriority.hpp" | |||
23 | ||||
24 | using std::map; | |||
25 | using std::set; | |||
26 | using std::size_t; | |||
27 | using std::vector; | |||
28 | ||||
29 | namespace tket { | |||
30 | namespace graphs { | |||
31 | ||||
32 | struct BruteForceColouring::Impl { | |||
33 | struct NodeColouringData { | |||
34 | vector<size_t> allowed_colours; | |||
35 | ||||
36 | // The index in "allowed_colours", not the actual colour | |||
37 | size_t current_colour_index = 0; | |||
38 | ||||
39 | 1220269 | bool is_valid_colour() const { | ||
40 | 1220269 | return current_colour_index < allowed_colours.size(); | ||
41 | } | |||
42 | ||||
43 | 2893890 | size_t get_colour() const { return allowed_colours[current_colour_index]; } | ||
44 | }; | |||
45 | ||||
46 | // This exactly mirrors the nodes in the ColouringPriority object; | |||
47 | // it is just extra data specifically related to colouring. | |||
48 | vector<NodeColouringData> colouring_data; | |||
49 | ||||
50 | // KEY: is the vertex, VALUE: the colour | |||
51 | map<size_t, size_t> colours; | |||
52 | ||||
53 | // Fills in the colour possibilities, | |||
54 | // possibly increasing suggested_number_of_colours. | |||
55 | // Returns false if it failed. | |||
56 | 14647 | bool initial_colouring_setup( | ||
57 | const ColouringPriority& priority, size_t& suggested_number_of_colours) { | |||
58 |
1/2✓ Branch 1 taken 14647 times.
✗ Branch 2 not taken.
|
14647 | const auto& initial_clique = priority.get_initial_clique(); | |
59 | ||||
60 | 14647 | suggested_number_of_colours = | ||
61 | 14647 | std::max(suggested_number_of_colours, initial_clique.size()); | ||
62 | ||||
63 |
1/2✓ Branch 1 taken 14647 times.
✗ Branch 2 not taken.
|
14647 | const auto& nodes = priority.get_nodes(); | |
64 | ||||
65 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 14647 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 14647 times.
|
14647 | if (suggested_number_of_colours > nodes.size()) { |
66 | ✗ | return false; | ||
67 | } | |||
68 |
1/2✓ Branch 2 taken 14647 times.
✗ Branch 3 not taken.
|
14647 | colouring_data.resize(nodes.size()); | |
69 | ||||
70 |
2/2✓ Branch 1 taken 41088 times.
✓ Branch 2 taken 14647 times.
|
2/2✓ Decision 'true' taken 41088 times.
✓ Decision 'false' taken 14647 times.
|
55735 | for (size_t i = 0; i < initial_clique.size(); ++i) { |
71 |
1/2✓ Branch 2 taken 41088 times.
✗ Branch 3 not taken.
|
41088 | colouring_data[i].allowed_colours.assign(1, i); | |
72 |
1/2✓ Branch 2 taken 41088 times.
✗ Branch 3 not taken.
|
41088 | colours[nodes[i].vertex] = i; | |
73 | } | |||
74 | ||||
75 | // Now, recursively keep increasing the number of colours until we at least | |||
76 | // have nonempty colour possibility lists. (It may still be impossible, of | |||
77 | // course, due to the detailed graph structure). | |||
78 | ||||
79 | 14647 | set<size_t> forbidden_colours; | ||
80 | ||||
81 |
2/2✓ Branch 2 taken 148143 times.
✓ Branch 3 taken 14647 times.
|
2/2✓ Decision 'true' taken 148143 times.
✓ Decision 'false' taken 14647 times.
|
162790 | for (size_t i = initial_clique.size(); i < nodes.size(); ++i) { |
82 | 148143 | forbidden_colours.clear(); | ||
83 | ||||
84 |
2/2✓ Branch 6 taken 322820 times.
✓ Branch 7 taken 148143 times.
|
2/2✓ Decision 'true' taken 322820 times.
✓ Decision 'false' taken 148143 times.
|
470963 | for (size_t node_index : nodes[i].earlier_neighbour_node_indices) { |
85 | const auto& earlier_colours = | |||
86 | 322820 | colouring_data[node_index].allowed_colours; | ||
87 | ||||
88 | // It is only the initial CLIQUE vertices which have fixed colours; | |||
89 | // as the number of possible colours increases, EVERY other vertex | |||
90 | // has more colour possibilities; so we must NOT think that, | |||
91 | // just because CURRENTLY a vertex has only one colour, | |||
92 | // that it will ALWAYS be that way! | |||
93 |
3/4✓ Branch 2 taken 322820 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 136833 times.
✓ Branch 5 taken 185987 times.
|
2/2✓ Decision 'true' taken 136833 times.
✓ Decision 'false' taken 185987 times.
|
322820 | if (initial_clique.count(nodes[node_index].vertex) != 0) { |
94 | TKET_ASSERT(earlier_colours.size() == 1); | |||
95 |
1/2✓ Branch 2 taken 136833 times.
✗ Branch 3 not taken.
|
136833 | forbidden_colours.insert(earlier_colours[0]); | |
96 | } | |||
97 | } | |||
98 | 148143 | auto& possible_colours = colouring_data[i].allowed_colours; | ||
99 | 148143 | possible_colours.clear(); | ||
100 |
2/2✓ Branch 0 taken 469845 times.
✓ Branch 1 taken 148143 times.
|
2/2✓ Decision 'true' taken 469845 times.
✓ Decision 'false' taken 148143 times.
|
617988 | for (size_t col = 0; col < suggested_number_of_colours; ++col) { |
101 |
3/4✓ Branch 1 taken 469845 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 333012 times.
✓ Branch 4 taken 136833 times.
|
2/2✓ Decision 'true' taken 333012 times.
✓ Decision 'false' taken 136833 times.
|
469845 | if (forbidden_colours.count(col) == 0) { |
102 |
1/2✓ Branch 1 taken 333012 times.
✗ Branch 2 not taken.
|
333012 | possible_colours.push_back(col); | |
103 | } | |||
104 | } | |||
105 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 148143 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 148143 times.
|
148143 | if (possible_colours.empty()) { |
106 | ✗ | ++suggested_number_of_colours; | ||
107 | ✗ | return initial_colouring_setup(priority, suggested_number_of_colours); | ||
108 | } | |||
109 | } | |||
110 | 14647 | return true; | ||
111 | 14647 | } | ||
112 | ||||
113 | 14647 | void fill_colour_map(const ColouringPriority& priority) { | ||
114 | 14647 | const auto& nodes = priority.get_nodes(); | ||
115 |
2/2✓ Branch 1 taken 189231 times.
✓ Branch 2 taken 14647 times.
|
2/2✓ Decision 'true' taken 189231 times.
✓ Decision 'false' taken 14647 times.
|
203878 | for (size_t i = 0; i < nodes.size(); ++i) { |
116 | 189231 | colours[nodes[i].vertex] = colouring_data[i].get_colour(); | ||
117 | } | |||
118 | 14647 | } | ||
119 | ||||
120 | 15078 | bool attempt_brute_force_colouring(const ColouringPriority& priority) { | ||
121 |
2/2✓ Branch 4 taken 194173 times.
✓ Branch 5 taken 15078 times.
|
2/2✓ Decision 'true' taken 194173 times.
✓ Decision 'false' taken 15078 times.
|
209251 | for (auto& data : colouring_data) { |
122 | 194173 | data.current_colour_index = 0; | ||
123 | } | |||
124 | 15078 | const auto& nodes = priority.get_nodes(); | ||
125 | 15078 | const size_t number_of_nodes = nodes.size(); | ||
126 | ||||
127 | 15078 | for (size_t current_node_index = 0;;) { | ||
128 | 1220269 | const auto& current_node = nodes[current_node_index]; | ||
129 | 1220269 | auto& current_colouring_node = colouring_data[current_node_index]; | ||
130 | ||||
131 |
2/2✓ Branch 1 taken 949390 times.
✓ Branch 2 taken 270879 times.
|
2/2✓ Decision 'true' taken 949390 times.
✓ Decision 'false' taken 270879 times.
|
1220269 | if (current_colouring_node.is_valid_colour()) { |
132 | // We have a candidate colour, now test it for consistency. | |||
133 | 949390 | const auto current_col = current_colouring_node.get_colour(); | ||
134 | 949390 | bool colour_is_impossible = false; | ||
135 | ||||
136 | // TODO: would a set of colours be faster here? | |||
137 | 949390 | for (size_t earlier_node_index : | ||
138 |
2/2✓ Branch 5 taken 1755269 times.
✓ Branch 6 taken 459679 times.
|
3164338 | current_node.earlier_neighbour_node_indices) { | |
139 |
2/2✓ Branch 2 taken 489711 times.
✓ Branch 3 taken 1265558 times.
|
2/2✓ Decision 'true' taken 489711 times.
✓ Decision 'false' taken 1265558 times.
|
1755269 | if (colouring_data[earlier_node_index].get_colour() == current_col) { |
140 | 489711 | colour_is_impossible = true; | ||
141 | 489711 | break; | ||
142 | } | |||
143 | } | |||
144 |
2/2✓ Branch 0 taken 459679 times.
✓ Branch 1 taken 489711 times.
|
2/2✓ Decision 'true' taken 459679 times.
✓ Decision 'false' taken 489711 times.
|
949390 | if (!colour_is_impossible) { |
145 | // Advance to the next node to colour. | |||
146 | 459679 | ++current_node_index; | ||
147 |
2/2✓ Branch 0 taken 445032 times.
✓ Branch 1 taken 14647 times.
|
2/2✓ Decision 'true' taken 445032 times.
✓ Decision 'false' taken 14647 times.
|
459679 | if (current_node_index < number_of_nodes) { |
148 | 445032 | colouring_data[current_node_index].current_colour_index = 0; | ||
149 | 445032 | continue; | ||
150 | } | |||
151 | // We've hit the end! We are finished. | |||
152 | 14647 | return true; | ||
153 | } | |||
154 | 489711 | ++current_colouring_node.current_colour_index; | ||
155 | 489711 | continue; | ||
156 | 489711 | } | ||
157 | ||||
158 | // We must backtrack. | |||
159 |
2/2✓ Branch 0 taken 431 times.
✓ Branch 1 taken 270448 times.
|
2/2✓ Decision 'true' taken 431 times.
✓ Decision 'false' taken 270448 times.
|
270879 | if (current_node_index == 0) { |
160 | 431 | return false; | ||
161 | } | |||
162 | 270448 | --current_node_index; | ||
163 | ||||
164 | // Advance the colour. | |||
165 | 270448 | ++colouring_data[current_node_index].current_colour_index; | ||
166 | 1205191 | } | ||
167 | // We cannot actually reach here, the outer loop only returns, never breaks. | |||
168 | } | |||
169 | }; | |||
170 | ||||
171 | 302476 | BruteForceColouring::~BruteForceColouring() {} | ||
172 | ||||
173 | 302476 | BruteForceColouring::BruteForceColouring( | ||
174 | 302476 | const ColouringPriority& priority, size_t suggested_number_of_colours) | ||
175 | 302476 | : m_pimpl(std::make_unique<BruteForceColouring::Impl>()) { | ||
176 |
1/2✓ Branch 1 taken 302476 times.
✗ Branch 2 not taken.
|
302476 | const auto number_of_nodes = priority.get_nodes().size(); | |
177 |
2/2✓ Branch 0 taken 287829 times.
✓ Branch 1 taken 14647 times.
|
0/1? Decision couldn't be analyzed.
|
302476 | if (suggested_number_of_colours >= number_of_nodes) { |
178 | // We've been given permission to use many colours; | |||
179 | // so just use them all! | |||
180 |
2/2✓ Branch 0 taken 295700 times.
✓ Branch 1 taken 287829 times.
|
2/2✓ Decision 'true' taken 295700 times.
✓ Decision 'false' taken 287829 times.
|
583529 | for (size_t i = 0; i < number_of_nodes; ++i) { |
181 |
2/4✓ Branch 2 taken 295700 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 295700 times.
✗ Branch 7 not taken.
|
295700 | m_pimpl->colours[priority.get_nodes()[i].vertex] = i; | |
182 | } | |||
183 | 302476 | return; | ||
184 | } | |||
185 | ||||
186 | 14647 | const auto initial_suggested_number_of_colours = suggested_number_of_colours; | ||
187 | ||||
188 | try { | |||
189 |
2/4✓ Branch 2 taken 14647 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 14647 times.
|
14647 | if (!m_pimpl->initial_colouring_setup( | |
190 | priority, suggested_number_of_colours)) { | |||
191 | ✗ | throw std::runtime_error("initial_colouring_setup failed"); | ||
192 | } | |||
193 | ||||
194 | // From now on, every time we fail to colour with the specified number, | |||
195 | // we simply have to add the extra colour onto | |||
196 | // each list of possible colours; | |||
197 | // no need to redo "initial_colouring_setup". | |||
198 | ||||
199 |
1/2✓ Branch 0 taken 15078 times.
✗ Branch 1 not taken.
|
15078 | for (; suggested_number_of_colours <= number_of_nodes; | |
200 | ++suggested_number_of_colours) { | |||
201 |
3/4✓ Branch 2 taken 15078 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 14647 times.
✓ Branch 5 taken 431 times.
|
15078 | if (m_pimpl->attempt_brute_force_colouring(priority)) { | |
202 | // We've succeeded! | |||
203 |
1/2✓ Branch 2 taken 14647 times.
✗ Branch 3 not taken.
|
14647 | m_pimpl->fill_colour_map(priority); | |
204 | 14647 | return; | ||
205 | } | |||
206 | // It's impossible with this number of colours, | |||
207 | // so try again with one more. | |||
208 | // If we were really fancy we might consider | |||
209 | // a binary search on the number of colours. | |||
210 |
3/4✓ Branch 1 taken 431 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3678 times.
✓ Branch 5 taken 431 times.
|
4109 | for (size_t i = priority.get_initial_clique().size(); i < number_of_nodes; | |
211 | ++i) { | |||
212 |
1/2✓ Branch 3 taken 3678 times.
✗ Branch 4 not taken.
|
3678 | m_pimpl->colouring_data[i].allowed_colours.push_back( | |
213 | suggested_number_of_colours); | |||
214 | } | |||
215 | } | |||
216 | ✗ | throw std::runtime_error("suggested_number_of_colours hit number_of_nodes"); | ||
217 | ✗ | } catch (const std::exception& e) { | ||
218 | // GCOVR_EXCL_START | |||
219 | TKET_ASSERT( | |||
220 | AssertMessage() << "initial_suggested_number_of_colours = " | |||
221 | << initial_suggested_number_of_colours | |||
222 | << ", reached suggested_number_of_colours = " | |||
223 | << suggested_number_of_colours << ", had " | |||
224 | << number_of_nodes << " nodes. Error: " << e.what() | |||
225 | << priority.print_raw_data()); | |||
226 | // GCOVR_EXCL_STOP | |||
227 | } | |||
228 | } | |||
229 | ||||
230 | 302476 | const map<size_t, size_t>& BruteForceColouring::get_colours() const { | ||
231 | 302476 | return m_pimpl->colours; | ||
232 | } | |||
233 | ||||
234 | } // namespace graphs | |||
235 | } // namespace tket | |||
236 |