IMP logo
IMP Reference Guide  2.18.0
The Integrative Modeling Platform
core/Hierarchy.h
Go to the documentation of this file.
1 /**
2  * \file IMP/core/Hierarchy.h \brief Decorator for helping deal with
3  * a hierarchy.
4  *
5  * Copyright 2007-2022 IMP Inventors. All rights reserved.
6  *
7  */
8 
9 #ifndef IMPCORE_HIERARCHY_H
10 #define IMPCORE_HIERARCHY_H
11 
12 #include <IMP/core/core_config.h>
13 #include "internal/hierarchy_helpers.h"
14 
15 #include <IMP/SingletonModifier.h>
16 #include <IMP/Particle.h>
17 #include <IMP/Model.h>
18 #include <IMP/Decorator.h>
19 #include <IMP/decorator_macros.h>
20 #include <IMP/internal/utility.h>
21 #include <IMP/Pointer.h>
22 
23 #include <boost/tuple/tuple.hpp>
24 #include <boost/serialization/access.hpp>
25 
26 #include <limits>
27 #include <vector>
28 #include <deque>
29 
30 IMPCORE_BEGIN_NAMESPACE
31 
32 /** \defgroup hierarchy Hierarchies of particles
33  \brief Manipulate particles representing molecules at multiple levels.
34  */
35 #ifndef SWIG
36 class Hierarchy;
37 #endif
38 
39 //! Define the type for a type of hierarchy
40 /** The hierarchy class is identified by the passed string so two
41  hierarchies created with the same initialization string will be
42  the same.
43 
44  This example shows how to make and use a custom hierarchy:
45  \include custom_hierarchy.py
46  \see Hierarchy
47 */
48 class IMPCOREEXPORT HierarchyTraits {
49  ParticleIndexesKey children_;
50  ParticleIndexKey parent_;
51 
52  public:
53  HierarchyTraits() {}
54  //! Create a HierarchyTraits with the given name
55  HierarchyTraits(std::string name);
56  ParticleIndexesKey get_children_key() const { return children_; }
57  ParticleIndexKey get_parent_key() const { return parent_; }
58  bool operator==(const HierarchyTraits &o) const {
59  return parent_ == o.parent_;
60  }
61  IMP_SHOWABLE_INLINE(HierarchyTraits, out << parent_);
62 };
63 
65 
66 class Hierarchy;
67 
68 #ifndef IMP_DOXYGEN
69 typedef IMP::Vector<Hierarchy> GenericHierarchies;
70 #endif
71 
72 //! A decorator for helping deal with a generalized hierarchy.
73 /**
74  See HierarchyTraits for an example of how to define a custom hierarchy
75  and IMP::atom::Hierarchy for a hierarchy for molecules.
76  \ingroup hierarchy
77  \see HierarchyTraits
78  */
79 class IMPCOREEXPORT Hierarchy : public Decorator {
80  static void do_setup_particle(Model *, ParticleIndex,
81  HierarchyTraits) {}
82  static void do_setup_particle(Model *m, ParticleIndex pi,
83  const ParticleIndexes &children,
84  HierarchyTraits traits) {
85  for (unsigned int i = 0; i < children.size(); ++i) {
86  m->add_attribute(traits.get_parent_key(), children[i], pi);
87  }
88  m->add_attribute(traits.get_children_key(), pi, children);
89  }
90  static void do_setup_particle(Model *m, ParticleIndex pi,
91  const ParticlesTemp &children,
92  HierarchyTraits traits) {
93  do_setup_particle(m, pi, get_indexes(children), traits);
94  }
95 
96  //! Signal to the Model that this Hierarchy has changed
97  void update_changed_trigger() const {
98  get_model()->set_trigger_updated(get_changed_key());
99  }
100 
101  public:
103  traits, get_default_traits());
105  /** Setup the particle and add children. */
108 
109  /** Check if the particle has the needed attributes for a
110  cast to succeed */
112  HierarchyTraits = Hierarchy::get_default_traits()) {
113  return true;
114  }
115 
116  //! The key used to signal to the Model that the Hierarchy has changed
117  static TriggerKey get_changed_key();
118 
119  /** \return the parent particle, or Hierarchy()
120  if it has no parent.
121  */
123  if (get_model()->get_has_attribute(get_decorator_traits().get_parent_key(),
124  get_particle_index())) {
126  get_decorator_traits().get_parent_key(), get_particle_index());
127  return Hierarchy(get_model(), VALUE, get_decorator_traits());
128  } else {
129  return Hierarchy();
130  }
131  }
132 
133  unsigned int get_number_of_children() const {
134  if (get_model()->get_has_attribute(
135  get_decorator_traits().get_children_key(), get_particle_index())) {
136  return get_model()
137  ->get_attribute(get_decorator_traits().get_children_key(),
139  .size();
140  } else {
141  return 0;
142  }
143  }
144  Hierarchy get_child(unsigned int i) const {
145  IMP_USAGE_CHECK(i < get_number_of_children(), "Invalid child requested");
146  return Hierarchy(get_model(), get_model()->get_attribute(
147  get_decorator_traits().get_children_key(),
148  get_particle_index())[i],
149  get_decorator_traits());
150  }
151  ParticleIndex get_child_index(unsigned int i) const {
152  IMP_USAGE_CHECK(i < get_number_of_children(), "Invalid child requested");
153  return get_model()->get_attribute(get_decorator_traits().get_children_key(),
154  get_particle_index())[i];
155  }
156  ParticleIndexes get_children_indexes() const {
157  if (get_model()->get_has_attribute(
158  get_decorator_traits().get_children_key(), get_particle_index())) {
159  return get_model()->get_attribute(
160  get_decorator_traits().get_children_key(), get_particle_index());
161  } else {
162  return ParticleIndexes();
163  }
164  }
165  GenericHierarchies get_children() const {
166  GenericHierarchies ret(get_number_of_children());
167  for (unsigned int i = 0; i < ret.size(); ++i) {
168  ret[i] = get_child(i);
169  }
170  return ret;
171  }
172  void remove_child(unsigned int i) {
173  IMP_USAGE_CHECK(i < get_number_of_children(), "Invalid child requested");
174  Hierarchy c = get_child(i);
175  ParticleIndexes &pis = get_model()->access_attribute(
176  get_decorator_traits().get_children_key(), get_particle_index());
177  pis.erase(pis.begin() + i);
178  get_model()->remove_attribute(get_decorator_traits().get_parent_key(),
179  c.get_particle_index());
180  update_changed_trigger();
181  }
182  void remove_child(Hierarchy h) { remove_child(h.get_child_index()); }
183  void clear_children() {
184  ParticleIndexes &pis = get_model()->access_attribute(
185  get_decorator_traits().get_children_key(), get_particle_index());
186  for (unsigned int i = 0; i < pis.size(); ++i) {
187  get_model()->remove_attribute(get_decorator_traits().get_parent_key(),
188  pis[i]);
189  }
190  get_model()->remove_attribute(get_decorator_traits().get_children_key(),
192  update_changed_trigger();
193  }
194  void add_child(Hierarchy h) const {
195  if (get_model()->get_has_attribute(
196  get_decorator_traits().get_children_key(), get_particle_index())) {
197  get_model()
198  ->access_attribute(get_decorator_traits().get_children_key(),
200  .push_back(h.get_particle_index());
201  } else {
203  get_decorator_traits().get_children_key(), get_particle_index(),
204  ParticleIndexes(1, h.get_particle_index()));
205  }
206  get_model()->add_attribute(get_decorator_traits().get_parent_key(),
207  h.get_particle_index(), get_particle_index());
208  update_changed_trigger();
209  }
210  void add_child_at(Hierarchy h, unsigned int pos) {
211  IMP_USAGE_CHECK(get_number_of_children() >= pos, "Invalid position");
212  if (get_model()->get_has_attribute(
213  get_decorator_traits().get_children_key(), get_particle_index())) {
214  ParticleIndexes &pis = get_model()->access_attribute(
215  get_decorator_traits().get_children_key(), get_particle_index());
216  pis.insert(pis.begin() + pos, h.get_particle_index());
217  } else {
219  get_decorator_traits().get_children_key(), get_particle_index(),
220  ParticleIndexes(1, h.get_particle_index()));
221  }
222  get_model()->add_attribute(get_decorator_traits().get_parent_key(),
223  h.get_particle_index(), get_particle_index());
224  update_changed_trigger();
225  }
226  //! Return i such that `get_parent().get_child(i) == this`
227  int get_child_index() const;
228  //! Get the default hierarchy traits
229  static const HierarchyTraits &get_default_traits();
230 
231  HierarchyTraits get_traits() { return get_decorator_traits(); }
232 };
233 
234 //! A visitor for traversal of a hierarchy
235 /** This works from both C++ and Python
236  \ingroup hierarchy
237  \ingroup decorators
238  \see Hierarchy
239  */
240 class IMPCOREEXPORT HierarchyVisitor {
241  public:
242  HierarchyVisitor() {}
243  //! Return true if the traversal should visit this node's children
244  virtual bool operator()(Hierarchy p) = 0;
245  virtual ~HierarchyVisitor() {}
246 };
247 
248 //! A visitor which applies a modifier to each Particle in a hierarchy
249 /** This works from both C++ and Python
250  \ingroup hierarchy
251  \ingroup decorators
252  \see SingletonModifier
253  \see Hierarchy
254  */
255 class IMPCOREEXPORT ModifierVisitor : public HierarchyVisitor {
257 
258  public:
259  ModifierVisitor(SingletonModifier *sm) : sm_(sm) {}
260  virtual bool operator()(Hierarchy p) override {
261  sm_->apply_index(p.get_model(), p.get_particle_index());
262  return true;
263  }
264  virtual ~ModifierVisitor() {}
265 };
266 
267 #if !defined(SWIG) && !defined(IMP_DOXYGEN)
268 namespace internal {
269 template <class H, class F, class Out, bool Slice = false>
270 struct Gather {
271  //! initialize with the function and the container
272  Gather(F f, Out out) : f_(f), out_(out) {}
273  bool operator()(H p) {
274  if (f_(p)) {
275  *out_ = p;
276  ++out_;
277  if (Slice) return false;
278  }
279  return true;
280  }
281  //! Return the container
282  Out get_out() const { return out_; }
283 
284  private:
285  F f_;
286  Out out_;
287 };
288 }
289 #endif
290 
291 //! Apply the visitor to each particle, breadth first.
292 /** \param[in] d The Hierarchy for the tree in question
293  \param[in] f The visitor to be applied. This is passed by reference.
294  A branch of the traversal stops when f returns false.
295  \ingroup hierarchy
296  \see Hierarchy
297  */
298 template <class HD, class F>
299 inline F visit_breadth_first(HD d, F f) {
300  std::deque<HD> stack;
301  stack.push_back(d);
302  // d.show(std::cerr);
303  do {
304  HD cur = stack.front();
305  stack.pop_front();
306  if (f(cur)) {
307  // std::cerr << "Visiting particle " << cur.get_particle() << std::endl;
308  for (int i = cur.get_number_of_children() - 1; i >= 0; --i) {
309  stack.push_back(cur.get_child(i));
310  }
311  }
312  } while (!stack.empty());
313  return f;
314 }
315 
316 //! Apply functor F to each particle, traversing the hierarchy depth first.
317 /** See visit_breadth_first() for documentation.
318  \ingroup hierarchy
319  \see Hierarchy
320  */
321 template <class HD, class F>
322 inline F visit_depth_first(HD d, F &f) {
323  Vector<HD> stack;
324  stack.push_back(d);
325  // d.show(std::cerr);
326  do {
327  HD cur = stack.back();
328  stack.pop_back();
329  if (f(cur)) {
330  // std::cerr << "Visiting particle " << cur.get_particle() << std::endl;
331  for (int i = cur.get_number_of_children() - 1; i >= 0; --i) {
332  stack.push_back(cur.get_child(i));
333  }
334  }
335  } while (!stack.empty());
336  return f;
337 }
338 
339 //! Apply functor F to each particle, traversing the hierarchy breadth first.
340 /** This method allows data to be associated with each visited node.
341  The data of the parent is passed to each invocation of the child.
342 
343  \param[in] d The Hierarchy for the tree in question
344  \param[in] f The functor to be applied
345  F must define a type Data which is returned by each call.
346  The result of the parent call is passed as the second argument
347  to the operator() of the child. e.g.
348  struct DepthVisitor {
349  typedef int result_type;
350  result_type operator()(Particle *p, int i) const
351  {
352  if (p==nullptr) return 0;
353  else return i+1;
354  }
355  };
356  \param[in] data The data to be used for d (since it has no relevant parent)
357 
358  \return A copy of the functor passed in. Use this if you care about
359  the functor state.
360 
361  \ingroup hierarchy
362  \see Hierarchy
363  */
364 template <class HD, class F>
365 inline F visit_breadth_first_with_data(HD d, F f,
366  typename F::result_type data) {
367  typedef std::pair<typename F::result_type, HD> DP;
368  std::deque<DP> stack;
369  stack.push_back(DP(data, d));
370  // d.show(std::cerr);
371  do {
372  DP cur = stack.front();
373  stack.pop_front();
374  typename F::result_type r = f(cur.second.get_particle(), cur.first);
375  // std::cerr << "Visiting particle " << cur.get_particle() << std::endl;
376  for (int i = cur.second.get_number_of_children() - 1; i >= 0; --i) {
377  stack.push_back(std::make_pair(r, cur.second.get_child(i)));
378  }
379  } while (!stack.empty());
380  return f;
381 }
382 
383 //! Apply functor F to each particle, traversing the hierarchy depth first.
384 /** See visit_breadth_first() for documentation.
385  \ingroup hierarchy
386  \see Hierarchy
387  */
388 template <class HD, class F>
389 inline F visit_depth_first_with_data(HD d, F f, typename F::result_type data) {
390  typedef std::pair<typename F::result_type, HD> DP;
391  Vector<DP> stack;
392  stack.push_back(DP(data, d));
393  // d.show(std::cerr);
394  do {
395  DP cur = stack.back();
396  stack.pop_back();
397  typename F::result_type r = f(cur.second, cur.first);
398  // std::cerr << "Visiting particle " << cur.get_particle() << std::endl;
399  for (int i = cur.second.get_number_of_children() - 1; i >= 0; --i) {
400  stack.push_back(DP(r, cur.second.get_child(i)));
401  }
402  } while (!stack.empty());
403  return f;
404 }
405 
406 //! Print the hierarchy using a given decorator to display each node
407 /** \ingroup hierarchy
408  \see Hierarchy
409  */
410 template <class ND>
411 inline std::ostream &show(Hierarchy h, std::ostream &out = std::cout) {
412  IMP_PRINT_TREE(out, Hierarchy, h, n.get_number_of_children(), n.get_child,
413  ND(n).show(out));
414  return out;
415 }
416 
417 //! A simple functor to count the number of particles in a hierarchy.
418 /** This is a good example of a simple HierarchyVisitor.
419  \ingroup hierarchy
420  \see Hierarchy
421  */
423  HierarchyCounter() : ct_(0) {}
424 
425  //! Increment the counter
426  bool operator()(Hierarchy) override {
427  ++ct_;
428  return true;
429  }
430  //! Return how many nodes have been visited
431  unsigned int get_count() const { return ct_; }
432  IMP_SHOWABLE_INLINE(HierarchyCounter, out << get_count());
433 
434  private:
435  unsigned int ct_;
436  friend class boost::serialization::access;
437 
438  template<class Archive> void serialize(Archive &ar, const unsigned int) {
439  ar & ct_;
440  }
441 };
442 
444 
445 //! Gather all the particles in the hierarchy that meet some criteria
446 /** Gather all the particles in the hierarchy for which f() returns true
447  and appends them to out
448 
449  @param h hierarchy root
450  @param f boolean function / functor that returns true
451  on desired particles
452  @param out a output iterator for appending the result
453 
454  @return the output iterator
455 
456  \ingroup hierarchy
457  \see Hierarchy
458  */
459 template <class H, class Out, class F>
460 inline Out gather(H h, F f, Out out) {
461  internal::Gather<H, F, Out> gather(f, out);
462  visit_depth_first(h, gather);
463  return gather.get_out();
464 }
465 
466 //! Gather all the uppermost particles in the hierarchy that meet some criteria
467 /** Descent in the tree terminates when a node is gathered so that
468  none of its children are explored.
469 
470  \ingroup hierarchy
471  \see Hierarchy
472  */
473 template <class H, class Out, class F>
474 inline Out gather_slice(H h, F f, Out out) {
475  internal::Gather<H, F, Out, true> gather(f, out);
476  visit_depth_first(h, gather);
477  return gather.get_out();
478 }
479 
480 //! Gather all the particles in the hierarchy which match on an attribute
481 /** \ingroup hierarchy
482  \see Hierarchy
483  */
484 template <class H, class Out, class K, class V>
485 inline Out gather_by_attribute(H h, K k, V v, Out out) {
486  internal::Gather<H, internal::MatchAttribute<K, V>, Out> gather(
487  internal::MatchAttribute<K, V>(k, v), out);
488  visit_depth_first(h, gather);
489  return gather.get_out();
490 }
491 
492 //! Gather all the particles in the hierarchy which match on two attributes
493 /** \ingroup hierarchy
494  \see Hierarchy
495  */
496 template <class H, class Out, class K0, class V0, class K1, class V1>
497 inline Out gather_by_attributes(H h, K0 k0, V0 v0, K1 k1, V1 v1, Out out) {
498  internal::Gather<H, internal::MatchAttributes<K0, V0, K1, V1>, Out> gather(
499  internal::MatchAttributes<K0, V0, K1, V1>(k0, v0, k1, v1), out);
500  visit_depth_first(h, gather);
501  return gather.get_out();
502 }
503 
504 //! Find the first node which matches some criteria
505 /** \ingroup hierarchy
506  \see Hierarchy
507  */
508 template <class HD, class F>
509 inline HD find_breadth_first(HD h, F f) {
510  if (f(h.get_particle())) return h;
511  Vector<HD> stack;
512  stack.push_back(h);
513  // d.show(std::cerr);
514  do {
515  HD cur = stack.back();
516  stack.pop_back();
517 
518  for (int i = cur.get_number_of_children() - 1; i >= 0; --i) {
519  HD hd = cur.get_child(i);
520  if (f(hd.get_particle())) {
521  return hd;
522  } else {
523  stack.push_back(hd);
524  }
525  }
526  } while (!stack.empty());
527  return HD();
528 }
529 
530 //! Get all the leaves of the bit of hierarchy
531 /** The leaves are returned in the obvious order
532  (first child before second child).
533 
534  \see Hierarchy
535  */
536 IMPCOREEXPORT GenericHierarchies get_leaves(Hierarchy mhd);
537 
538 //! Get all the non-leaves of the bit of hierarchy
539 /** \see Hierarchy
540  */
541 IMPCOREEXPORT GenericHierarchies get_internal(Hierarchy mhd);
542 
543 //! Get all the particles in the subtree
544 /** \see Hierarchy
545  */
546 IMPCOREEXPORT GenericHierarchies get_all_descendants(Hierarchy mhd);
547 
548 //! Return the root of the hierarchy
549 /** \see Hierarchy */
551  while (h.get_parent()) {
552  h = h.get_parent();
553  }
554  return h;
555 }
556 
557 IMPCORE_END_NAMESPACE
558 
559 #endif /* IMPCORE_HIERARCHY_H */
F visit_breadth_first(HD d, F f)
Apply the visitor to each particle, breadth first.
The base class for decorators.
A base class for modifiers of ParticlesTemp.
A visitor for traversal of a hierarchy.
A Modifier on ParticlesTemp.
ParticleIndex get_particle_index() const
Returns the particle index decorated by this decorator.
Definition: Decorator.h:190
#define IMP_SHOWABLE_INLINE(Name, how_to_show)
Declare the methods needed by an object that can be printed.
static bool get_is_setup(Model *, ParticleIndex, HierarchyTraits=Hierarchy::get_default_traits())
F visit_depth_first(HD d, F &f)
Apply functor F to each particle, traversing the hierarchy depth first.
bool operator()(Hierarchy) override
Increment the counter.
Model * get_model() const
Returns the Model containing the particle.
Definition: Decorator.h:193
Storage of a model, its restraints, constraints and particles.
HD find_breadth_first(HD h, F f)
Find the first node which matches some criteria.
void remove_attribute(TypeKey attribute_key, ParticleIndex particle)
remove particle attribute with the specied key
Index< ParticleIndexTag > ParticleIndex
Definition: base_types.h:161
#define IMP_DECORATOR_TRAITS_SETUP_1(Name, FirstArgumentType,first_argument_name)
A more IMP-like version of the std::vector.
Definition: Vector.h:42
GenericHierarchies get_all_descendants(Hierarchy mhd)
Get all the particles in the subtree.
A visitor which applies a modifier to each Particle in a hierarchy.
Class for storing model, its restraints, constraints, and particles.
Definition: Model.h:73
Out gather_by_attributes(H h, K0 k0, V0 v0, K1 k1, V1 v1, Out out)
Gather all the particles in the hierarchy which match on two attributes.
virtual bool operator()(Hierarchy p) override
Return true if the traversal should visit this node's children.
#define IMP_VALUES(Name, PluralName)
Define the type for storing sets of values.
Definition: value_macros.h:23
F visit_depth_first_with_data(HD d, F f, typename F::result_type data)
Apply functor F to each particle, traversing the hierarchy depth first.
Hierarchy get_root(Hierarchy h)
Return the root of the hierarchy.
Hierarchy get_parent() const
void add_attribute(TypeKey attribute_key, ParticleIndex particle, Type value)
add particle atribute with the specied key and initial value
F visit_breadth_first_with_data(HD d, F f, typename F::result_type data)
Apply functor F to each particle, traversing the hierarchy breadth first.
Out gather_slice(H h, F f, Out out)
Gather all the uppermost particles in the hierarchy that meet some criteria.
A simple functor to count the number of particles in a hierarchy.
Define the type for a type of hierarchy.
Helper macros for implementing Decorators.
A smart pointer to a ref-counted Object that is a class member.
Definition: Pointer.h:146
#define IMP_DECORATOR_TRAITS_SETUP_0(Name)
Out gather(H h, F f, Out out)
Gather all the particles in the hierarchy that meet some criteria.
Out gather_by_attribute(H h, K k, V v, Out out)
Gather all the particles in the hierarchy which match on an attribute.
GenericHierarchies get_internal(Hierarchy mhd)
Get all the non-leaves of the bit of hierarchy.
Interface to specialized Particle types (e.g. atoms)
Definition: Decorator.h:118
Classes to handle individual model particles. (Note that implementation of inline functions is in int...
#define IMP_DECORATOR_WITH_TRAITS_METHODS(Name, Parent, TraitsType,traits_name, default_traits)
void show(Hierarchy h, std::ostream &out=std::cout)
Print out a molecular hierarchy.
A nullptr-initialized pointer to an IMP Object.
unsigned int get_count() const
Return how many nodes have been visited.
#define IMP_USAGE_CHECK(expr, message)
A runtime test for incorrect usage of a class or method.
Definition: check_macros.h:168
A decorator for helping deal with a generalized hierarchy.
void set_trigger_updated(TriggerKey tk)
Update the given trigger.
Definition: Model.h:447
Hierarchies get_leaves(Hierarchy h)
Type get_attribute(TypeKey attribute_key, ParticleIndex particle)
get the value of the particle attribute with the specified key
ParticleIndexes get_indexes(const ParticlesTemp &ps)
Get the indexes from a list of particles.