Molassembler  1.0.0
Molecule graph and conformer library
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
OrderDiscoveryHelper.h
Go to the documentation of this file.
1 
20 #ifndef INCLUDE_MOLASSEMBLER_ORDER_DISCORVERY_HELPER
21 #define INCLUDE_MOLASSEMBLER_ORDER_DISCORVERY_HELPER
22 
23 #include "boost/graph/adjacency_list.hpp"
24 #include "boost/graph/graphviz.hpp"
27 
28 namespace Scine {
29 
30 namespace Molassembler {
31 
36 template<typename T>
38 public:
39  struct VertexData {
40  T data;
41  };
42 
43  using DependencyGraphType = boost::adjacency_list<
44  /* OutEdgeListS = Type of Container for edges of a vertex
45  * Options: vector, list, slist, set, multiset, unordered_set
46  * Choice: setS
47  */
48  boost::setS,
49  /* VertexListS = Type of Container for vertices
50  * Options: vector, list, slist, set, multiset, unordered_set
51  * Choice: vecS, removing vertices does not occur
52  */
53  boost::vecS,
54  /* DirectedS = Is the graph directed or not?
55  * Choice: Directed, do not need bidirectional
56  */
57  boost::directedS,
58  /* VertexProperty = What information is stored about vertices?
59  * Choice: Atom, containing an index and an element type
60  */
62  >;
63 
64  using VertexIndexType = typename DependencyGraphType::vertex_descriptor;
65 
66 private:
67  std::map<T, VertexIndexType> sourceMap_;
68  DependencyGraphType graph_;
69 
75  std::vector<
76  std::vector<T>
77  > getSetsByDegree_() const {
78  /* Group all vertices's values by vertex out_degree
79  *
80  * The more out edges a vertex has, the "smaller" it is, since a less-than
81  * relationship is represented by a directed edge from the smaller object
82  * to the bigger object.
83  */
84 
85  // Keep a mapping of out_degree to index in sets to avoid empty sets
86  std::map<
87  unsigned,
88  std::vector<VertexIndexType>
89  > degreeToSetMap;
90 
91  typename DependencyGraphType::vertex_iterator iter, end;
92  std::tie(iter, end) = boost::vertices(graph_);
93 
94  while(iter != end) {
95  auto outDegree = boost::out_degree(*iter, graph_);
96 
97  if(degreeToSetMap.count(outDegree) == 0) {
98  degreeToSetMap[outDegree] = {*iter};
99  } else {
100  degreeToSetMap.at(outDegree).push_back(*iter);
101  }
102 
103  ++iter;
104  }
105 
106  /* The map is ordered by key (outDegree ASC), so the result of mapValues
107  * should is ordered too, so we can just map the vertex descriptors for
108  * every degree into the stored underlying data
109  */
110  return Temple::map(
111  degreeToSetMap,
112  [&](const auto& mapIterPair) -> std::vector<T> {
113  return Temple::map_stl(
114  mapIterPair.second,
115  [&](const auto& index) -> T {
116  return graph_[index].data;
117  }
118  );
119  }
120  );
121  }
122 
123  VertexIndexType addItem_(const T& item) {
124  auto newIndex = boost::add_vertex(graph_);
125 
126  graph_[newIndex].data = item;
127  sourceMap_.emplace(
128  item,
129  newIndex
130  );
131 
132  return newIndex;
133  }
134 
135 
137  private:
138  const OrderDiscoveryHelper& baseRef_;
139 
140  public:
141  GraphvizWriter(const OrderDiscoveryHelper& base) : baseRef_(base) {}
142 
143  void operator() (std::ostream& os) const {
144  os << "graph [fontname = \"Arial\", layout = \"dot\"];\n"
145  << "node [fontname = \"Arial\", shape = circle, style = filled];\n"
146  << "edge [fontname = \"Arial\"];\n";
147  }
148 
149  void operator() (std::ostream& os, const VertexIndexType& vertexIndex) const {
150  os << R"([label=")" << baseRef_.graph_[vertexIndex].data << R"("])";
151  }
152 
153  void operator() (std::ostream& /* os */, const typename DependencyGraphType::edge_descriptor& /* edge */) const {
154  // do nothing particular
155  }
156  };
157 
158 public:
159  OrderDiscoveryHelper() = default;
160 
168  template<typename Container>
169  explicit OrderDiscoveryHelper(const Container& container) {
170  setUnorderedValues(
171  std::begin(container),
172  std::end(container)
173  );
174  }
175 
186  // Build a mapping of vertices
187  std::map<VertexIndexType, VertexIndexType> vertexMapping;
188 
189  for(VertexIndexType i = 0; i < boost::num_vertices(other.graph_); ++i) {
190  // Does this vertex exist in this graph already?
191  const auto& vertexData = other.graph_[i].data;
192  if(sourceMap_.count(vertexData) > 0) {
193  vertexMapping.emplace(
194  i,
195  sourceMap_.at(vertexData)
196  );
197  }
198  }
199 
200  // Add non-contradicting edges from the other graph
201  for(
202  auto edgeIterPair = boost::edges(other.graph_);
203  edgeIterPair.first != edgeIterPair.second;
204  ++edgeIterPair.first
205  ) {
206  auto otherEdgeSource = boost::source(*edgeIterPair.first, other.graph_);
207  auto otherEdgeTarget = boost::target(*edgeIterPair.first, other.graph_);
208 
209  if( // Both endpoints must have a counterpart in this graph
210  vertexMapping.count(otherEdgeSource) > 0
211  && vertexMapping.count(otherEdgeTarget) > 0
212  ) {
213  auto thisEdgeSource = vertexMapping.at(otherEdgeSource);
214  auto thisEdgeTarget = vertexMapping.at(otherEdgeTarget);
215 
216  // Check if that edge already exists (or its inverse)
217  auto thisGraphEdge = boost::edge(
218  thisEdgeSource,
219  thisEdgeTarget,
220  graph_
221  );
222 
223  // In case the graph edge is found, go to the next edge
224  if(thisGraphEdge.second) {
225  continue;
226  }
227 
228  // If not, check if the inverse is present in the graph
229  auto inverseGraphEdge = boost::edge(
230  thisEdgeTarget,
231  thisEdgeSource,
232  graph_
233  );
234 
235  if(inverseGraphEdge.second) {
236  throw "Contradicting information in other OrderDiscoveryHelper graph!";
237  }
238 
239  // Add the edge to this graph
240  boost::add_edge(thisEdgeSource, thisEdgeTarget, graph_);
241 
242  }
243  }
244 
245  addTransferabilityEdges();
246  }
247 
259  // Left is the other graph, right is this graph
260  std::map<VertexIndexType, VertexIndexType> vertexMapping;
261 
262  // Add all new vertices from the other graph
263  for(VertexIndexType i = 0; i < boost::num_vertices(other.graph_); ++i) {
264  // Does this vertex exist in this graph already?
265  const auto& vertexData = other.graph_[i].data;
266  if(sourceMap_.count(vertexData) == 0) {
267  auto newIndex = addItem_(other.graph_[i].data);
268  vertexMapping.emplace(
269  i,
270  newIndex
271  );
272  } else {
273  vertexMapping.emplace(
274  i,
275  sourceMap_.at(vertexData)
276  );
277  }
278  }
279 
280  // Add non-contradicting edges from the other graph
281  for(
282  auto edgeIterPair = boost::edges(other.graph_);
283  edgeIterPair.first != edgeIterPair.second;
284  ++edgeIterPair.first
285  ) {
286  auto otherEdgeSource = boost::source(*edgeIterPair.first, other.graph_);
287  auto otherEdgeTarget = boost::target(*edgeIterPair.first, other.graph_);
288 
289  auto thisEdgeSource = vertexMapping.at(otherEdgeSource);
290  auto thisEdgeTarget = vertexMapping.at(otherEdgeTarget);
291 
292  // Check if that edge already exists (or its inverse)
293  auto thisGraphEdge = boost::edge(
294  thisEdgeSource,
295  thisEdgeTarget,
296  graph_
297  );
298 
299  // In case the graph edge is found, go to the next edge
300  if(thisGraphEdge.second) {
301  continue;
302  }
303 
304  // If not, check if the inverse is present in the graph
305  auto inverseGraphEdge = boost::edge(
306  thisEdgeTarget,
307  thisEdgeSource,
308  graph_
309  );
310 
311  if(inverseGraphEdge.second) {
312  throw "Contradicting information in other OrderDiscoveryHelper graph!";
313  }
314 
315  // Add the edge to this graph
316  boost::add_edge(thisEdgeSource, thisEdgeTarget, graph_);
317  }
318 
319  addTransferabilityEdges();
320  }
321 
332  for(VertexIndexType i = 0; i < boost::num_vertices(graph_); ++i) {
333  std::vector<VertexIndexType> seeds;
334 
335  for(
336  auto edgeIterPair = boost::out_edges(i, graph_);
337  edgeIterPair.first != edgeIterPair.second;
338  ++edgeIterPair.first
339  ) {
340  seeds.emplace_back(
341  boost::target(*edgeIterPair.first, graph_)
342  );
343  }
344 
345  do {
346  std::vector<VertexIndexType> newSeeds;
347 
348  for(const auto& seed: seeds) {
349  if(!boost::edge(i, seed, graph_).second) {
350  boost::add_edge(i, seed, graph_);
351  }
352 
353  for(
354  auto edgeIterPair = boost::out_edges(seed, graph_);
355  edgeIterPair.first != edgeIterPair.second;
356  ++edgeIterPair.first
357  ) {
358  newSeeds.emplace_back(
359  boost::target(*edgeIterPair.first, graph_)
360  );
361  }
362  }
363 
364  seeds = std::move(newSeeds);
365  } while(!seeds.empty());
366  }
367  }
368 
374  template<typename It>
375  void setUnorderedValues(It iter, const It end) {
376  if(boost::num_vertices(graph_) > 0) {
377  graph_.clear();
378  }
379 
380  sourceMap_.clear();
381 
382  for(/* */; iter != end; ++iter) {
383  addItem_(*iter);
384  }
385  }
386 
391  template<typename Container>
392  void setUnorderedValues(Container&& container) {
393  static_assert(
394  std::is_same<
395  Temple::Traits::getValueType<Container>,
396  T
397  >::value,
398  "Container value must match OrderDiscoveryHelper's template argument T"
399  );
400 
401  setUnorderedValues(
402  std::begin(container),
403  std::end(container)
404  );
405  }
406 
411  std::vector<
412  std::vector<T>
413  > getSets() const {
414  // Keep only sets with at least one member
415  return getSetsByDegree_();
416  }
417 
422  std::vector<
423  std::vector<T>
424  > getUndecidedSets() const {
425  // Keep only sets with more than one member
426  return Temple::copy_if(
427  getSetsByDegree_(),
428  [](const auto& set) -> bool {
429  return set.size() > 1;
430  }
431  );
432  }
433 
438  bool isTotallyOrdered() const {
439  auto N = boost::num_vertices(graph_);
440 
441  return(boost::num_edges(graph_) == N * (N - 1) / 2);
442  }
443 
449  const T& a,
450  const T& b
451  ) {
452  assert(a != b);
453 
454  /* Less-than relationship is represented as directed edge from vertex
455  * storing a to vertex storing b
456  */
457  boost::add_edge(
458  sourceMap_.at(a),
459  sourceMap_.at(b),
460  graph_
461  );
462  }
463 
468  std::string dumpGraphviz() const {
469  GraphvizWriter propertyWriter(*this);
470 
471  std::stringstream ss;
472 
473  boost::write_graphviz(
474  ss,
475  graph_,
476  propertyWriter,
477  propertyWriter,
478  propertyWriter
479  );
480 
481  return ss.str();
482  }
483 
495  bool isSmaller(const T& a, const T& b) const {
496  if(sourceMap_.count(a) == 0 || sourceMap_.count(b) == 0) {
497  return false;
498  }
499 
500  // is a < b?
501  auto smallerEdge = boost::edge(sourceMap_.at(a), sourceMap_.at(b), graph_);
502  return smallerEdge.second;
503  }
504 };
505 
506 } // namespace Molassembler
507 
508 } // namespace Scine
509 
510 #endif
std::vector< std::vector< T > > getUndecidedSets() const
Returns grouped data (in descending order) whose internal order is undecided.
Definition: OrderDiscoveryHelper.h:424
void setUnorderedValues(It iter, const It end)
Range-abstracted.
Definition: OrderDiscoveryHelper.h:375
Definition: OrderDiscoveryHelper.h:39
auto map_stl(const Container< T, Dependents< T >...> &container, UnaryFunction &&function)
Definition: Functional.h:73
Container aiding in gradual discovery of order.
Definition: OrderDiscoveryHelper.h:37
std::vector< std::vector< T > > getSetsByDegree_() const
Get a grouped list of the ordered data sorted by out_degree ascending.
Definition: OrderDiscoveryHelper.h:77
Provides functional-style transformation of container elements.
std::string dumpGraphviz() const
Dumps a graphviz string for visualization of relationships.
Definition: OrderDiscoveryHelper.h:468
std::vector< std::vector< T > > getSets() const
Get a list of sets (in descending order) as currently discovered.
Definition: OrderDiscoveryHelper.h:413
constexpr Get< 1 > second
Calls std::get&lt;1&gt; on any argument it is invoked with.
Definition: Functor.h:125
void addRelationshipsFromOther(const OrderDiscoveryHelper &other)
Transfer relationships from another OrderDiscoveryHelper.
Definition: OrderDiscoveryHelper.h:185
bool isTotallyOrdered() const
Returns whether the total order has been discovered or not.
Definition: OrderDiscoveryHelper.h:438
void addAllFromOther(const OrderDiscoveryHelper &other)
Adds vertices and edges from another instance, then adds transferability edges.
Definition: OrderDiscoveryHelper.h:258
void setUnorderedValues(Container &&container)
Container-abstracted.
Definition: OrderDiscoveryHelper.h:392
constexpr auto map(const ArrayType< T, size > &array, UnaryFunction &&function)
Maps all elements of any array-like container with a unary function.
Definition: Containers.h:62
void addTransferabilityEdges()
Adds missing transferability edges.
Definition: OrderDiscoveryHelper.h:331
Definition: OrderDiscoveryHelper.h:136
OrderDiscoveryHelper(const Container &container)
Arbitrary container constructor.
Definition: OrderDiscoveryHelper.h:169
bool isSmaller(const T &a, const T &b) const
Check if a value is smaller than another.
Definition: OrderDiscoveryHelper.h:495
Functional-style container-related algorithms.
void addLessThanRelationship(const T &a, const T &b)
Add a less-than relationship to the graph.
Definition: OrderDiscoveryHelper.h:448