IMP logo
IMP Reference Guide  develop.63e71f5352,2022/07/06
The Integrative Modeling Platform
Model.h
Go to the documentation of this file.
1 /**
2  * \file IMP/Model.h
3  * \brief Storage of a model, its restraints, constraints and particles.
4  *
5  * Copyright 2007-2022 IMP Inventors. All rights reserved.
6  *
7  */
8 
9 #ifndef IMPKERNEL_MODEL_H
10 #define IMPKERNEL_MODEL_H
11 
12 #include <IMP/kernel_config.h>
13 #include "ModelObject.h"
14 #include "ScoringFunction.h"
15 #include "Restraint.h"
16 #include "RestraintSet.h"
17 #include "ScoreState.h"
18 #include "container_macros.h"
19 #include "base_types.h"
20 //#include "Particle.h"
21 #include "Undecorator.h"
22 #include "internal/AttributeTable.h"
23 #include "internal/attribute_tables.h"
24 #include "internal/moved_particles_cache.h"
25 #include <IMP/Object.h>
26 #include <IMP/Pointer.h>
27 #include <boost/unordered_map.hpp>
28 #include <boost/unordered_set.hpp>
29 #include <IMP/tuple_macros.h>
30 #include <boost/iterator/transform_iterator.hpp>
31 #include <boost/iterator/filter_iterator.hpp>
32 
33 #include <limits>
34 
35 IMPKERNEL_BEGIN_NAMESPACE
36 
37 class ModelObject;
38 class Undecorator;
39 class Particle;
40 
41 #if !defined(SWIG) && !defined(IMP_DOXYGEN)
42 namespace internal {
43 enum Stage {
44  NOT_EVALUATING,
45  BEFORE_EVALUATING,
46  EVALUATING,
47  AFTER_EVALUATING,
48  COMPUTING_DEPENDENCIES
49 };
50 }
51 #endif
52 
53 class Model;
54 
55 //! Class for storing model, its restraints, constraints, and particles.
56 /** The Model maintains a standard \imp container for each of Particle,
57  ScoreState and Restraint object types.
58 
59  Each Float attribute has an associated range which reflects the
60  range of values that it is expected to take on during optimization.
61  The optimizer can use these ranges to make the optimization process
62  more efficient. By default, the range estimates are simply the
63  range of values for that attribute in the various particles, but
64  it can be set to another value. For example, an attribute storing
65  an angle could have the range set to (0,PI).
66 
67  The ranges are not enforced; they are just guidelines. In order to
68  enforce ranges, see, for example,
69  IMP::example::ExampleSingletonModifier.
70 
71  \headerfile Model.h "IMP/Model.h"
72  */
73 class IMPKERNELEXPORT Model : public Object
74 #if !defined(SWIG) && !defined(IMP_DOXYGEN)
75  ,
76  public internal::Masks,
77  // The attribute tables provide fast access to
78  // e.g. particle attributes, etc.
79  public internal::FloatAttributeTable,
80  public internal::StringAttributeTable,
81  public internal::IntAttributeTable,
82  public internal::ObjectAttributeTable,
83  public internal::WeakObjectAttributeTable,
84  public internal::IntsAttributeTable,
85  public internal::FloatsAttributeTable,
86  public internal::ObjectsAttributeTable,
87  public internal::ParticleAttributeTable,
88  public internal::ParticlesAttributeTable
89 #endif
90  {
92  // must be up top
93  // we don't want any liveness checks
94  IMP_NAMED_TUPLE_5(NodeInfo, NodeInfos, Edges, inputs, Edges, input_outputs,
95  Edges, outputs, Edges, readers, Edges, writers, );
96  typedef boost::unordered_map<const ModelObject *, NodeInfo> DependencyGraph;
97  DependencyGraph dependency_graph_;
98  boost::unordered_set<const ModelObject *> no_dependencies_;
99  boost::unordered_map<const ModelObject *, ScoreStatesTemp>
100  required_score_states_;
101 
102  // basic representation
103  boost::unordered_map<FloatKey, FloatRange> ranges_;
104 
105  ParticleIndexes free_particles_;
108 
109  Vector<PointerMember<Object> > model_data_;
110 
111  void do_add_dependencies(const ModelObject *mo);
112  void do_clear_required_score_states(ModelObject *mo);
113  void do_check_required_score_states(const ModelObject *mo) const;
114  void do_check_update_order(const ScoreState *ss) const;
115  void do_check_inputs_and_outputs(const ModelObject *mo) const;
116  void do_check_readers_and_writers(const ModelObject *mo) const;
117  void do_check_not_in_readers_and_writers(const ModelObject *mo) const;
118  void do_clear_dependencies(const ModelObject *mo);
119 
120  // used to track time when triggers are activated
121  unsigned age_counter_;
122  // all triggers
123  Vector<unsigned> trigger_age_;
124  // time when dependencies were last changed, or 0
125  unsigned dependencies_age_;
126 
127  // allow skipping updating dependencies_age_ for temporary ModelObjects
128  bool dependencies_saved_;
129  unsigned saved_dependencies_age_;
130  // We don't use ModelObjectsTemp here because these objects might get freed
131  // under us, which would cause WeakPointer to raise an exception
132  std::vector<ModelObject *> mos_added_since_save_, mos_removed_since_save_;
133 
134  // cache of restraints that are affected by each moved particle,
135  // used for evaluate_moved() and related functions
136  internal::MovedParticlesRestraintCache moved_particles_restraint_cache_;
137  // cache of particles that are affected by each moved particle
138  internal::MovedParticlesParticleCache moved_particles_particle_cache_;
139  // time when moved_particles_*_cache_ were last updated, or 0
140  unsigned moved_particles_cache_age_;
141 
142  // update model age (can never be zero, even if it wraps)
143  void increase_age() {
144  age_counter_++;
145  if (age_counter_ == 0) {
146  age_counter_ = 1;
147  }
148  }
149 
150  template <class MOType, class MOVector>
151  void do_get_dependent(ModelObject *mo, MOVector &ret) {
152  const auto &node = dependency_graph_.find(mo);
154  "Object " << mo->get_name()
155  << " does not have dependencies.");
156  IMP_INTERNAL_CHECK(node != dependency_graph_.end(),
157  "Node not in dependency_graph.");
158  MOType *r = dynamic_cast<MOType *>(mo);
159  if (r) {
160  ret.push_back(r);
161  }
162  for (ModelObject *cur : node->second.get_outputs()) {
163  do_get_dependent<MOType, MOVector>(cur, ret);
164  }
165  for (ModelObject *cur : node->second.get_readers()) {
166  do_get_dependent<MOType, MOVector>(cur, ret);
167  }
168  }
169 
170 #if !defined(IMP_DOXYGEN) && !defined(SWIG)
171  // things the evaluate template functions need, can't be bothered with friends
172  public:
173 #endif
174  // check more things on the first call
175  bool first_call_;
176  // the stage of evaluation
177  internal::Stage cur_stage_;
178 
179  //! Get all Restraints that depend on the given particle
180  const std::set<Restraint *> &get_dependent_restraints(ParticleIndex pi) {
181  return moved_particles_restraint_cache_.get_dependent_restraints(pi);
182  }
183 
184  //! Get all particles that depend on the given particle
185  const ParticleIndexes &get_dependent_particles(ParticleIndex pi) {
186  return moved_particles_particle_cache_.get_dependent_particles(pi);
187  }
188 
189  ModelObjectsTemp get_dependency_graph_inputs(const ModelObject *mo) const;
190  ModelObjectsTemp get_dependency_graph_outputs(const ModelObject *mo) const;
191  bool do_get_has_dependencies(const ModelObject *mo) const {
192  return no_dependencies_.find(mo) == no_dependencies_.end();
193  }
194  void do_set_has_dependencies(const ModelObject *mo, bool tf);
195  void do_set_has_all_dependencies(bool tf);
196 
197  void validate_computed_derivatives() const {}
198  void set_has_all_dependencies(bool tf);
199  bool get_has_all_dependencies() const;
200  void check_dependency_invariants() const;
201  void check_dependency_invariants(const ModelObject *mo) const;
202  ScoreStatesTemp get_ancestor_score_states(const ModelObject *mo) const;
203  ScoreStatesTemp get_descendent_score_states(const ModelObject *mo) const;
204 
205  void before_evaluate(const ScoreStatesTemp &states);
206  void after_evaluate(const ScoreStatesTemp &states, bool calc_derivs);
207 
208  internal::Stage get_stage() const { return cur_stage_; }
209  ParticleIndex add_particle_internal(Particle *p);
210  static void do_remove_score_state(ScoreState *obj);
211  void do_add_score_state(ScoreState *obj);
212  void do_remove_particle(ParticleIndex pi);
213  bool do_get_has_required_score_states(const ModelObject *mo) const;
214  void do_set_has_required_score_states(ModelObject *mo, bool tf);
215  const ScoreStatesTemp &do_get_required_score_states(const ModelObject *mo)
216  const {
217  IMP_USAGE_CHECK(do_get_has_required_score_states(mo),
218  "Doesn't have score states");
219  return required_score_states_.find(mo)->second;
220  }
221  void do_add_model_object(ModelObject *mo);
222  void do_remove_model_object(ModelObject *mo);
223 
224  public:
225  //! Construct an empty model
226  Model(std::string name = "Model %1%");
227 
228  public:
229 #if !defined(SWIG) && !defined(IMP_DOXYGEN)
230  IMP_MODEL_IMPORT(internal::FloatAttributeTable);
231  IMP_MODEL_IMPORT(internal::StringAttributeTable);
232  IMP_MODEL_IMPORT(internal::IntAttributeTable);
233  IMP_MODEL_IMPORT(internal::ObjectAttributeTable);
234  IMP_MODEL_IMPORT(internal::WeakObjectAttributeTable);
235  IMP_MODEL_IMPORT(internal::IntsAttributeTable);
236  IMP_MODEL_IMPORT(internal::FloatsAttributeTable);
237  IMP_MODEL_IMPORT(internal::ObjectsAttributeTable);
238  IMP_MODEL_IMPORT(internal::ParticleAttributeTable);
239  IMP_MODEL_IMPORT(internal::ParticlesAttributeTable);
240 #endif
241  //! Clear all the cache attributes of a given particle.
242  void clear_particle_caches(ParticleIndex pi);
243 
244  //! Add particle to the model
245  ParticleIndex add_particle(std::string name);
246 
247  //! Get the name of a particle
248  std::string get_particle_name(ParticleIndex pi);
249 
250  //! Add the passed Undecorator to the particle.
251  void add_undecorator(ParticleIndex pi, Undecorator *d);
252 
253 #if !defined(IMP_DOXYGEN)
254  RestraintsTemp get_dependent_restraints_uncached(ParticleIndex pi);
255 
256  ParticlesTemp get_dependent_particles_uncached(ParticleIndex pi);
257 
258  ScoreStatesTemp get_dependent_score_states_uncached(ParticleIndex pi);
259 #endif
260 
261  /** @name States
262 
263  ScoreStates maintain invariants in the Model (see ScoreState
264  for more information.)
265 
266  ScoreStates do not need to be explictly added to the Model, but they
267  can be if desired in order to keep them alive as long as the model is
268  alive.
269 
270  \advancedmethod
271  */
272  /**@{*/
273  IMP_LIST_ACTION(public, ScoreState, ScoreStates, score_state, score_states,
274  ScoreState *, ScoreStates, do_add_score_state(obj), {},
275  do_remove_score_state(obj));
276  /**@}*/
277 
278  public:
279 #ifndef SWIG
280  using Object::clear_caches;
281 #endif
282 
283  //! Sometimes it is useful to be able to make sure the model is up to date
284  /** This method updates all the state but does not necessarily compute the
285  score. Use this to make sure that your containers and rigid bodies are
286  up to date.
287  */
288  void update();
289 
290 #ifdef IMP_DOXYGEN
291  /** \name Accessing attributes
292  \anchor model_attributes
293  All the attribute data associated with each Particle are stored in the
294  Model. For each type of attribute, there are the methods detailed below
295  (where, eg, TypeKey is FloatKey or StringKey)
296  @{
297  */
298  //! add particle atribute with the specied key and initial value
299  /** \pre get_has_attribute(attribute_key, particle) is false*/
300  void add_attribute(TypeKey attribute_key, ParticleIndex particle, Type value);
301 
302  //! remove particle attribute with the specied key
303  /** \pre get_has_attribute(attribute_key, particle) is true*/
304  void remove_attribute(TypeKey attribute_key, ParticleIndex particle);
305 
306  //! return true if particle has attribute with the specified key
307  bool get_has_attribute(TypeKey attribute_key, ParticleIndex particle) const;
308 
309  //! set the value of particle attribute with the specified key
310  /** \pre get_has_attribute(attribute_key, particle) is true*/
311  void set_attribute(TypeKey attribute_key, ParticleIndex particle, Type value);
312 
313  //! get the value of the particle attribute with the specified key
314  /** \pre get_has_attribute(attribute_key, particle) is true*/
315  Type get_attribute(TypeKey attribute_key, ParticleIndex particle);
316 
317  /** Cache attributes, unlike normal attributes, can be added during
318  evaluation. They are also cleared by the clear_cache_attributes() method.
319  Cache attributes should be used when one is adding data to a particle
320  to aid scoring (eg cache the rigid body collision acceleration structure).
321 
322  When some pertinent aspect of the particle changes, the clear method
323  should
324  be called (yes, this is a bit vague). Examples where it should be cleared
325  include changing the set of members of a core::RigidBody or their
326  coordinates, changing the members of an atom::Hierarchy.
327  */
328  void add_cache_attribute(TypeKey attribute_key, ParticleIndex particle,
329  Type value);
330 
331  //! Optimized attributes are the parameters of the model that are
332  //! allowed to be modified by samplers and optimizers
333  void set_is_optimized(TypeKey attribute_key, ParticleIndex particle,
334  bool true_or_false);
335 /** @} */
336 #endif
337 
338 #ifdef SWIG
339 #define IMP_MODEL_ATTRIBUTE_METHODS(Type, Value) \
340  void add_attribute(Type##Key attribute_key, ParticleIndex particle, \
341  Value value); \
342  void remove_attribute(Type##Key attribute_key, ParticleIndex particle); \
343  bool get_has_attribute(Type##Key attribute_key, \
344  ParticleIndex particle) const; \
345  void set_attribute(Type##Key attribute_key, ParticleIndex particle, \
346  Value value); \
347  Value get_attribute(Type##Key attribute_key, ParticleIndex particle); \
348  void add_cache_attribute(Type##Key attribute_key, ParticleIndex particle, \
349  Value value)
350 
351  IMP_MODEL_ATTRIBUTE_METHODS(Float, Float);
352  IMP_MODEL_ATTRIBUTE_METHODS(Int, Int);
353  IMP_MODEL_ATTRIBUTE_METHODS(Floats, Floats);
354  IMP_MODEL_ATTRIBUTE_METHODS(Ints, Ints);
355  IMP_MODEL_ATTRIBUTE_METHODS(String, String);
356  IMP_MODEL_ATTRIBUTE_METHODS(ParticleIndexes, ParticleIndexes);
357  IMP_MODEL_ATTRIBUTE_METHODS(ParticleIndex, ParticleIndex);
358  IMP_MODEL_ATTRIBUTE_METHODS(Object, Object *);
359  IMP_MODEL_ATTRIBUTE_METHODS(WeakObject, Object *);
360  void set_is_optimized(FloatKey, ParticleIndex, bool);
361  void add_to_derivative(FloatKey k, ParticleIndex particle, double v,
362  const DerivativeAccumulator &da);
363 #endif
364 
365  //! Get the particle from an index.
367  IMP_USAGE_CHECK(get_has_particle(p), "Invalid particle requested");
368  return particle_index_[p];
369  }
370 
371  //! Check whether a given particle index exists.
373  if (particle_index_.size() <= get_as_unsigned_int(p)) return false;
374  return particle_index_[p];
375  }
376 
377  //! Get all particle indexes
379 
380  //! Get all the ModelObjects associated with this Model.
381  ModelObjectsTemp get_model_objects() const;
382 
383  //! Remove a particle from the Model.
384  /** The particle will then be inactive and cannot be used for anything
385  and all data stored in the particle is lost.
386  */
387  void remove_particle(ParticleIndex pi);
388 
389  /** \name Storing data in the model
390 
391  One can store data associated with the model. This is used, for example,
392  to keep a central ScoreState to normalize rigid body rotational variables.
393  @{ */
394  //! Store a piece of data in the model referenced by the key.
395  void add_data(ModelKey mk, Object *o);
396  //! Get back some data stored in the model.
397  Object *get_data(ModelKey mk) const;
398  //! Remove data stored in the model.
399  void remove_data(ModelKey mk);
400  //! Check if the model has a certain piece of data attached.
401  bool get_has_data(ModelKey mk) const;
402  /** @} */
403 
404  /** \name Model triggers
405 
406  Triggers can be used to track when to clear and rebuild caches
407  of derived model properties. For example, a Restraint may restrain
408  two particles as a function of the number of chemical bonds between
409  them. To speed up this restraint, the bond graph can be cached; however,
410  this graph needs to be rebuilt if bonds are created or removed. This
411  can be achieved by checking that the model time (see get_age()) of the
412  cache matches the time when the 'bond added/removed' Trigger was last
413  updated (see get_trigger_last_updated()), either when the Restraint is
414  evaluated or in an associated ScoreState.
415 
416  Triggers are intended for events that are rare during a typical
417  optimization. Triggers can be created by any IMP module in either C++
418  or Python by creating a new TriggerKey, much as model attributes
419  are handled. To avoid name collisions, it is recommended to prepend
420  the module and/or class name to the trigger, e.g. "atom.Bond.changed".
421 
422  For an example, see IMP::score_functor::OrientedSoap, which uses
423  a cache built from the molecular hierarchy, which is cleared when the
424  IMP::core::Hierarchy::get_changed_key() trigger is updated.
425 
426  @{ */
427 
428  //! Get the current 'model time'.
429  /** This is a number 1 or more that tracks the 'age' of the model;
430  it is incremented every time before_evaluate() is called.
431  It may wrap (and so should not be assumed to always increase)
432  but will never be 0. */
433  unsigned get_age() { return age_counter_; }
434 
435  //! Get the time when the given trigger was last updated, or 0.
436  /** Return the 'model time' (as given by get_age()) when the given
437  trigger was last updated on this model, or 0 if never. */
439  if (trigger_age_.size() > tk.get_index()) {
440  return trigger_age_[tk.get_index()];
441  } else {
442  return 0;
443  }
444  }
445 
446  //! Update the given trigger
448  if (tk.get_index() >= trigger_age_.size()) {
449  trigger_age_.resize(tk.get_index() + 1, 0);
450  }
451  trigger_age_[tk.get_index()] = age_counter_;
452  }
453  /** @} */
454 
455  //! Get the model age when ModelObject dependencies were last changed, or 0.
456  /** This gives the Model age (see get_age()) when Particles, Restraints,
457  or ScoreStates were last added or removed. It is typically used to
458  help maintain caches that depend on the model's dependency graph. */
459  unsigned get_dependencies_updated() { return dependencies_age_; }
460 
461  //! Mark a 'restore point' for ModelObject dependencies.
462  /** \see restore_dependencies() */
464  dependencies_saved_ = true;
465  saved_dependencies_age_ = dependencies_age_;
467  mos_added_since_save_.clear();
468  mos_removed_since_save_.clear();
469  }
470  }
471 
472  //! Restore ModelObject dependencies to previous restore point.
473  /** This method, when paired with save_dependencies(), can be used to
474  avoid triggering a model dependency update due to a temporary change
475  in the model dependency graph, for example due to adding a temporary
476  restraint, evaluating it, then removing that same restraint. It should
477  only be called in cases where it is known that the dependency graph
478  is the same as when save_dependencies() was called (this is only checked
479  in debug mode). Save/restore call pairs cannot be nested, although it
480  is OK to skip the call to restore_dependencies(), e.g. if an exception
481  occurs.
482 
483  \see get_dependencies_updated()
484  \see save_dependencies()
485  */
487  if (dependencies_saved_) {
488  dependencies_saved_ = false;
489  dependencies_age_ = saved_dependencies_age_;
491  // Need to sort pointers since we may not add/remove in the same order
492  std::sort(mos_added_since_save_.begin(), mos_added_since_save_.end());
493  std::sort(mos_removed_since_save_.begin(),
494  mos_removed_since_save_.end());
495  IMP_INTERNAL_CHECK(mos_added_since_save_ == mos_removed_since_save_,
496  "ModelObjects added do not match those removed");
497  }
498  }
499  }
500 
501  //! Get an upper bound on the number of particles in the Model.
502  /** This value is guaranteed to be at least the number of particles in
503  the model (there may be fewer particles if any have been removed)
504  and every ParticleIndex will be smaller than this value. */
505  unsigned get_particles_size() const { return particle_index_.size(); }
506 
508 
509  public:
510 #if !defined(IMP_DOXYGEN)
511  virtual void do_destroy() override;
512 #endif
513 };
514 
515 IMPKERNEL_END_NAMESPACE
516 
517 // This is needed for per cpp compilations, a not even sure why
518 // (perhaps cause Model returns ParticleIterator here and there?)
519 // - Feel free to remove if you *really* know what you're doing
520 #include "IMP/Particle.h"
521 
522 #endif /* IMPKERNEL_MODEL_H */
Particle * get_particle(ParticleIndex p) const
Get the particle from an index.
Definition: Model.h:366
Basic types used by IMP.
#define IMP_IF_CHECK(level)
Execute the code block if a certain level checks are on.
Definition: check_macros.h:104
Used to hold a set of related restraints.
boost::graph DependencyGraph
Directed graph on the interactions between the various objects in the model.
The base class for undecorators.
#define IMP_OBJECT_METHODS(Name)
Define the basic things needed by any Object.
Definition: object_macros.h:25
void restore_dependencies()
Restore ModelObject dependencies to previous restore point.
Definition: Model.h:486
void add_particle(RMF::FileHandle fh, Particle *hs)
Macros to help in defining tuple classes.
virtual void clear_caches()
Definition: Object.h:227
unsigned get_dependencies_updated()
Get the model age when ModelObject dependencies were last changed, or 0.
Definition: Model.h:459
unsigned get_particles_size() const
Get an upper bound on the number of particles in the Model.
Definition: Model.h:505
unsigned get_age()
Get the current 'model time'.
Definition: Model.h:433
bool get_has_particle(ParticleIndex p) const
Check whether a given particle index exists.
Definition: Model.h:372
Macros to define containers of objects.
unsigned get_trigger_last_updated(TriggerKey tk)
Get the time when the given trigger was last updated, or 0.
Definition: Model.h:438
#define IMP_INTERNAL_CHECK(expr, message)
An assertion to check for internal errors in IMP. An IMP::ErrorException will be thrown.
Definition: check_macros.h:139
Class for storing model, its restraints, constraints, and particles.
Definition: Model.h:73
Base class for objects in a Model that depend on other objects.
Definition: ModelObject.h:26
virtual void do_destroy()
Definition: Object.h:231
Common base class for heavy weight IMP objects.
Definition: Object.h:106
ParticleIndexes get_particle_indexes(ParticlesTemp const &particles)
ScoreStates maintain invariants in the Model.
Definition: ScoreState.h:54
Implements a vector tied to a particular index of type Index<Tag>.
Definition: Index.h:57
Shared score state.
Base class for objects in a Model that depend on other objects.
Classes to handle individual model particles. (Note that implementation of inline functions is in int...
A nullptr-initialized pointer to an IMP Object.
void save_dependencies()
Mark a 'restore point' for ModelObject dependencies.
Definition: Model.h:463
A shared base class to help in debugging and things.
Represents a scoring function on the model.
double Float
Basic floating-point value (could be float, double...)
Definition: types.h:19
Class to handle individual particles of a Model object.
Definition: Particle.h:41
bool get_has_dependencies() const
Return whether this object has dependencies computed.
#define IMP_USAGE_CHECK(expr, message)
A runtime test for incorrect usage of a class or method.
Definition: check_macros.h:168
Abstract base class for all restraints.
int Int
Basic integer value.
Definition: types.h:34
void set_trigger_updated(TriggerKey tk)
Update the given trigger.
Definition: Model.h:447
std::string String
Basic string value.
Definition: types.h:43
Class for adding derivatives from restraints to the model.