Molassembler  3.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  using Iterator = typename DependencyGraphType::vertex_iterator;
92  Iterator iter;
93  Iterator end;
94  std::tie(iter, end) = boost::vertices(graph_);
95 
96  while(iter != end) {
97  auto outDegree = boost::out_degree(*iter, graph_);
98 
99  if(degreeToSetMap.count(outDegree) == 0) {
100  degreeToSetMap[outDegree] = {*iter};
101  } else {
102  degreeToSetMap.at(outDegree).push_back(*iter);
103  }
104 
105  ++iter;
106  }
107 
108  /* The map is ordered by key (outDegree ASC), so the result of mapValues
109  * should is ordered too, so we can just map the vertex descriptors for
110  * every degree into the stored underlying data
111  */
112  return Temple::map(
113  degreeToSetMap,
114  [&](const auto& mapIterPair) -> std::vector<T> {
115  return Temple::map_stl(
116  mapIterPair.second,
117  [&](const auto& index) -> T {
118  return graph_[index].data;
119  }
120  );
121  }
122  );
123  }
124 
125  VertexIndexType addItem_(const T& item) {
126  auto newIndex = boost::add_vertex(graph_);
127 
128  graph_[newIndex].data = item;
129  sourceMap_.emplace(
130  item,
131  newIndex
132  );
133 
134  return newIndex;
135  }
136 
137 
139  private:
140  const OrderDiscoveryHelper& baseRef_;
141 
142  public:
143  GraphvizWriter(const OrderDiscoveryHelper& base) : baseRef_(base) {}
144 
145  void operator() (std::ostream& os) const {
146  os << "graph [fontname = \"Arial\", layout = \"dot\"];\n"
147  << "node [fontname = \"Arial\", shape = circle, style = filled];\n"
148  << "edge [fontname = \"Arial\"];\n";
149  }
150 
151  void operator() (std::ostream& os, const VertexIndexType& vertexIndex) const {
152  os << R"([label=")" << baseRef_.graph_[vertexIndex].data << R"("])";
153  }
154 
155  void operator() (std::ostream& /* os */, const typename DependencyGraphType::edge_descriptor& /* edge */) const {
156  // do nothing particular
157  }
158  };
159 
160 public:
161  OrderDiscoveryHelper() = default;
162 
170  template<typename Container>
171  explicit OrderDiscoveryHelper(const Container& container) {
172  setUnorderedValues(
173  std::begin(container),
174  std::end(container)
175  );
176  }
177 
188  // Build a mapping of vertices
189  std::map<VertexIndexType, VertexIndexType> vertexMapping;
190 
191  for(VertexIndexType i = 0; i < boost::num_vertices(other.graph_); ++i) {
192  // Does this vertex exist in this graph already?
193  const auto& vertexData = other.graph_[i].data;
194  if(sourceMap_.count(vertexData) > 0) {
195  vertexMapping.emplace(
196  i,
197  sourceMap_.at(vertexData)
198  );
199  }
200  }
201 
202  // Add non-contradicting edges from the other graph
203  for(
204  auto edgeIterPair = boost::edges(other.graph_);
205  edgeIterPair.first != edgeIterPair.second;
206  ++edgeIterPair.first
207  ) {
208  auto otherEdgeSource = boost::source(*edgeIterPair.first, other.graph_);
209  auto otherEdgeTarget = boost::target(*edgeIterPair.first, other.graph_);
210 
211  if( // Both endpoints must have a counterpart in this graph
212  vertexMapping.count(otherEdgeSource) > 0
213  && vertexMapping.count(otherEdgeTarget) > 0
214  ) {
215  auto thisEdgeSource = vertexMapping.at(otherEdgeSource);
216  auto thisEdgeTarget = vertexMapping.at(otherEdgeTarget);
217 
218  // Check if that edge already exists (or its inverse)
219  auto thisGraphEdge = boost::edge(
220  thisEdgeSource,
221  thisEdgeTarget,
222  graph_
223  );
224 
225  // In case the graph edge is found, go to the next edge
226  if(thisGraphEdge.second) {
227  continue;
228  }
229 
230  // If not, check if the inverse is present in the graph
231  auto inverseGraphEdge = boost::edge(
232  thisEdgeTarget,
233  thisEdgeSource,
234  graph_
235  );
236 
237  if(inverseGraphEdge.second) {
238  throw "Contradicting information in other OrderDiscoveryHelper graph!";
239  }
240 
241  // Add the edge to this graph
242  boost::add_edge(thisEdgeSource, thisEdgeTarget, graph_);
243 
244  }
245  }
246 
247  addTransferabilityEdges();
248  }
249 
261  // Left is the other graph, right is this graph
262  std::map<VertexIndexType, VertexIndexType> vertexMapping;
263 
264  // Add all new vertices from the other graph
265  for(VertexIndexType i = 0; i < boost::num_vertices(other.graph_); ++i) {
266  // Does this vertex exist in this graph already?
267  const auto& vertexData = other.graph_[i].data;
268  if(sourceMap_.count(vertexData) == 0) {
269  auto newIndex = addItem_(other.graph_[i].data);
270  vertexMapping.emplace(
271  i,
272  newIndex
273  );
274  } else {
275  vertexMapping.emplace(
276  i,
277  sourceMap_.at(vertexData)
278  );
279  }
280  }
281 
282  // Add non-contradicting edges from the other graph
283  for(
284  auto edgeIterPair = boost::edges(other.graph_);
285  edgeIterPair.first != edgeIterPair.second;
286  ++edgeIterPair.first
287  ) {
288  auto otherEdgeSource = boost::source(*edgeIterPair.first, other.graph_);
289  auto otherEdgeTarget = boost::target(*edgeIterPair.first, other.graph_);
290 
291  auto thisEdgeSource = vertexMapping.at(otherEdgeSource);
292  auto thisEdgeTarget = vertexMapping.at(otherEdgeTarget);
293 
294  // Check if that edge already exists (or its inverse)
295  auto thisGraphEdge = boost::edge(
296  thisEdgeSource,
297  thisEdgeTarget,
298  graph_
299  );
300 
301  // In case the graph edge is found, go to the next edge
302  if(thisGraphEdge.second) {
303  continue;
304  }
305 
306  // If not, check if the inverse is present in the graph
307  auto inverseGraphEdge = boost::edge(
308  thisEdgeTarget,
309  thisEdgeSource,
310  graph_
311  );
312 
313  if(inverseGraphEdge.second) {
314  throw "Contradicting information in other OrderDiscoveryHelper graph!";
315  }
316 
317  // Add the edge to this graph
318  boost::add_edge(thisEdgeSource, thisEdgeTarget, graph_);
319  }
320 
321  addTransferabilityEdges();
322  }
323 
334  for(VertexIndexType i = 0; i < boost::num_vertices(graph_); ++i) {
335  std::vector<VertexIndexType> seeds;
336 
337  for(
338  auto edgeIterPair = boost::out_edges(i, graph_);
339  edgeIterPair.first != edgeIterPair.second;
340  ++edgeIterPair.first
341  ) {
342  seeds.emplace_back(
343  boost::target(*edgeIterPair.first, graph_)
344  );
345  }
346 
347  do {
348  std::vector<VertexIndexType> newSeeds;
349 
350  for(const auto& seed: seeds) {
351  if(!boost::edge(i, seed, graph_).second) {
352  boost::add_edge(i, seed, graph_);
353  }
354 
355  for(
356  auto edgeIterPair = boost::out_edges(seed, graph_);
357  edgeIterPair.first != edgeIterPair.second;
358  ++edgeIterPair.first
359  ) {
360  newSeeds.emplace_back(
361  boost::target(*edgeIterPair.first, graph_)
362  );
363  }
364  }
365 
366  seeds = std::move(newSeeds);
367  } while(!seeds.empty());
368  }
369  }
370 
376  template<typename It>
377  void setUnorderedValues(It iter, const It end) {
378  if(boost::num_vertices(graph_) > 0) {
379  graph_.clear();
380  }
381 
382  sourceMap_.clear();
383 
384  for(/* */; iter != end; ++iter) {
385  addItem_(*iter);
386  }
387  }
388 
393  template<typename Container>
394  void setUnorderedValues(Container&& container) {
395  static_assert(
396  std::is_same<
397  Temple::Traits::getValueType<Container>,
398  T
399  >::value,
400  "Container value must match OrderDiscoveryHelper's template argument T"
401  );
402 
403  setUnorderedValues(
404  std::begin(container),
405  std::end(container)
406  );
407  }
408 
413  std::vector<
414  std::vector<T>
415  > getSets() const {
416  // Keep only sets with at least one member
417  return getSetsByDegree_();
418  }
419 
424  std::vector<
425  std::vector<T>
426  > getUndecidedSets() const {
427  // Keep only sets with more than one member
428  return Temple::copy_if(
429  getSetsByDegree_(),
430  [](const auto& set) -> bool {
431  return set.size() > 1;
432  }
433  );
434  }
435 
440  bool isTotallyOrdered() const {
441  auto N = boost::num_vertices(graph_);
442 
443  return(boost::num_edges(graph_) == N * (N - 1) / 2);
444  }
445 
451  const T& a,
452  const T& b
453  ) {
454  assert(a != b);
455 
456  /* Less-than relationship is represented as directed edge from vertex
457  * storing a to vertex storing b
458  */
459  boost::add_edge(
460  sourceMap_.at(a),
461  sourceMap_.at(b),
462  graph_
463  );
464  }
465 
470  std::string dumpGraphviz() const {
471  GraphvizWriter propertyWriter(*this);
472 
473  std::stringstream ss;
474 
475  boost::write_graphviz(
476  ss,
477  graph_,
478  propertyWriter,
479  propertyWriter,
480  propertyWriter
481  );
482 
483  return ss.str();
484  }
485 
497  bool isSmaller(const T& a, const T& b) const {
498  if(sourceMap_.count(a) == 0 || sourceMap_.count(b) == 0) {
499  return false;
500  }
501 
502  // is a < b?
503  auto smallerEdge = boost::edge(sourceMap_.at(a), sourceMap_.at(b), graph_);
504  return smallerEdge.second;
505  }
506 };
507 
508 } // namespace Molassembler
509 
510 } // namespace Scine
511 
512 #endif
std::vector< std::vector< T > > getUndecidedSets() const
Returns grouped data (in descending order) whose internal order is undecided.
Definition: OrderDiscoveryHelper.h:426
void setUnorderedValues(It iter, const It end)
Range-abstracted.
Definition: OrderDiscoveryHelper.h:377
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:470
std::vector< std::vector< T > > getSets() const
Get a list of sets (in descending order) as currently discovered.
Definition: OrderDiscoveryHelper.h:415
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:187
bool isTotallyOrdered() const
Returns whether the total order has been discovered or not.
Definition: OrderDiscoveryHelper.h:440
void addAllFromOther(const OrderDiscoveryHelper &other)
Adds vertices and edges from another instance, then adds transferability edges.
Definition: OrderDiscoveryHelper.h:260
void setUnorderedValues(Container &&container)
Container-abstracted.
Definition: OrderDiscoveryHelper.h:394
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:63
void addTransferabilityEdges()
Adds missing transferability edges.
Definition: OrderDiscoveryHelper.h:333
Definition: OrderDiscoveryHelper.h:138
OrderDiscoveryHelper(const Container &container)
Arbitrary container constructor.
Definition: OrderDiscoveryHelper.h:171
bool isSmaller(const T &a, const T &b) const
Check if a value is smaller than another.
Definition: OrderDiscoveryHelper.h:497
Functional-style container-related algorithms.
void addLessThanRelationship(const T &a, const T &b)
Add a less-than relationship to the graph.
Definition: OrderDiscoveryHelper.h:450