IMP logo
IMP Reference Guide  develop.eb1b99edaa,2026/06/25
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-2025 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 "internal/KeyVector.h"
26 #include <IMP/Object.h>
27 #include <IMP/Pointer.h>
28 #include <IMP/internal/IDGenerator.h>
29 #include <boost/unordered_map.hpp>
30 #include <boost/unordered_set.hpp>
31 #include <IMP/tuple_macros.h>
32 #include <boost/iterator/transform_iterator.hpp>
33 #include <boost/iterator/filter_iterator.hpp>
34 #include <cereal/access.hpp>
35 #include <cereal/types/polymorphic.hpp>
36 
37 #include <limits>
38 
39 IMPKERNEL_BEGIN_NAMESPACE
40 
41 class ModelObject;
42 class Undecorator;
43 class Particle;
44 
45 #if !defined(SWIG) && !defined(IMP_DOXYGEN)
46 namespace internal {
47 enum Stage {
48  NOT_EVALUATING,
49  BEFORE_EVALUATING,
50  EVALUATING,
51  AFTER_EVALUATING,
52  COMPUTING_DEPENDENCIES
53 };
54 }
55 #endif
56 
57 class Model;
58 
59 #if !defined(SWIG) && !defined(IMP_DOXYGEN)
60 // This is needed as NodeInfo (below) needs to be showable, and Edges are not
61 inline std::ostream &operator<<(
62  std::ostream &out, const std::set<ModelObject *> &) {
63  out << "(set of ModelObject)";
64  return out;
65 }
66 #endif
67 
68 //! Class for storing model, its restraints, constraints, and particles.
69 /** The Model maintains a standard \imp container for each of Particle,
70  ScoreState and Restraint object types.
71 
72  Each Float attribute has an associated range which reflects the
73  range of values that it is expected to take on during optimization.
74  The optimizer can use these ranges to make the optimization process
75  more efficient. By default, the range estimates are simply the
76  range of values for that attribute in the various particles, but
77  it can be set to another value. For example, an attribute storing
78  an angle could have the range set to (0,PI).
79 
80  The ranges are not enforced; they are just guidelines. In order to
81  enforce ranges, see, for example,
82  IMP::example::ExampleSingletonModifier.
83 
84  \headerfile Model.h "IMP/Model.h"
85  */
86 class IMPKERNELEXPORT Model : public Object
87 #if !defined(SWIG) && !defined(IMP_DOXYGEN)
88  ,
89  public internal::Masks,
90  // The attribute tables provide fast access to
91  // e.g. particle attributes, etc.
92  public internal::FloatAttributeTable,
93  public internal::StringAttributeTable,
94  public internal::IntAttributeTable,
95  public internal::ObjectAttributeTable,
96  public internal::WeakObjectAttributeTable,
97  public internal::IntsAttributeTable,
98  public internal::FloatsAttributeTable,
99  public internal::Vector3DAttributeTable,
100  public internal::Vector4DAttributeTable,
101  public internal::ObjectsAttributeTable,
102  public internal::ParticleAttributeTable,
103  public internal::ParticlesAttributeTable,
104  public internal::SparseStringAttributeTable,
105  public internal::SparseIntAttributeTable,
106  public internal::SparseFloatAttributeTable,
107  public internal::SparseParticleAttributeTable,
108  public internal::Vector3DDerivAttributeTable,
109  public internal::Vector4DDerivAttributeTable
110 #endif
111  {
112  typedef std::set<ModelObject *> Edges;
113  // must be up top
114  // we don't want any liveness checks
115  IMP_NAMED_TUPLE_5(NodeInfo, NodeInfos, Edges, inputs, Edges, input_outputs,
116  Edges, outputs, Edges, readers, Edges, writers, );
117  typedef boost::unordered_map<const ModelObject *, NodeInfo> DependencyGraph;
118  DependencyGraph dependency_graph_;
119  boost::unordered_set<const ModelObject *> no_dependencies_;
120  boost::unordered_map<const ModelObject *, ScoreStatesTemp>
121  required_score_states_;
122 
123  // basic representation
124  boost::unordered_map<FloatKey, FloatRange> ranges_;
125 
126  ParticleIndexes free_particles_;
129 
130  internal::KeyVector<ModelKey, PointerMember<Object> > model_data_;
131 
132 #if !defined(IMP_DOXYGEN)
133  // Map unique ID to Model*
134  class ModelMap {
135  std::map<uint32_t, Model*> map_;
136  internal::IDGenerator id_gen_;
137  public:
138  ModelMap() {}
139  uint32_t add_new_model(Model *m);
140  void add_model_with_id(Model *m, uint32_t id);
141  void remove_model(Model *m);
142  Model *get(uint32_t id) const;
143  };
144 
145  static ModelMap model_map_;
146  uint32_t unique_id_;
147 #endif
148 
149  void do_add_dependencies(const ModelObject *mo);
150  void do_clear_required_score_states(ModelObject *mo);
151  void do_check_required_score_states(const ModelObject *mo) const;
152  void do_check_update_order(const ScoreState *ss) const;
153  void do_check_inputs_and_outputs(const ModelObject *mo) const;
154  void do_check_readers_and_writers(const ModelObject *mo) const;
155  void do_check_not_in_readers_and_writers(const ModelObject *mo) const;
156  void do_clear_dependencies(const ModelObject *mo);
157 
158  // used to track time when triggers are activated
159  unsigned age_counter_;
160  // all triggers
161  Vector<unsigned> trigger_age_;
162  // time when dependencies were last changed, or 0
163  unsigned dependencies_age_;
164  // time when particles or attributes were last removed, or 0
165  unsigned removed_particles_attributes_age_;
166 
167  // allow skipping updating dependencies_age_ for temporary ModelObjects
168  bool dependencies_saved_;
169  unsigned saved_dependencies_age_;
170  // We don't use ModelObjectsTemp here because these objects might get freed
171  // under us, which would cause WeakPointer to raise an exception
172  std::vector<ModelObject *> mos_added_since_save_, mos_removed_since_save_;
173 
174  // cache of restraints that are affected by each moved particle,
175  // used for evaluate_moved() and related functions
176  internal::MovedParticlesRestraintCache moved_particles_restraint_cache_;
177  // cache of particles that are affected by each moved particle
178  internal::MovedParticlesParticleCache moved_particles_particle_cache_;
179  // time when moved_particles_*_cache_ were last updated, or 0
180  unsigned moved_particles_cache_age_;
181 
182  void register_unique_id();
183 
184  friend class cereal::access;
185 
186  template<class Archive> void serialize(Archive &ar,
187  std::uint32_t const version) {
188  ar(cereal::base_class<Object>(this));
189  // We need to get unique_id_ early on read, so that any ModelObjects
190  // that reference it get correctly associated with this model
191  ar(unique_id_);
192  if (std::is_base_of<cereal::detail::InputArchiveBase, Archive>::value) {
193  register_unique_id();
194  }
195  ar(cereal::base_class<internal::FloatAttributeTable>(this),
196  cereal::base_class<internal::StringAttributeTable>(this),
197  cereal::base_class<internal::IntAttributeTable>(this),
198  cereal::base_class<internal::IntsAttributeTable>(this),
199  cereal::base_class<internal::FloatsAttributeTable>(this),
200  cereal::base_class<internal::Vector3DAttributeTable>(this),
201  cereal::base_class<internal::ParticleAttributeTable>(this),
202  cereal::base_class<internal::ParticlesAttributeTable>(this),
203  cereal::base_class<internal::SparseStringAttributeTable>(this),
204  cereal::base_class<internal::SparseIntAttributeTable>(this),
205  cereal::base_class<internal::SparseFloatAttributeTable>(this),
206  cereal::base_class<internal::SparseParticleAttributeTable>(this),
207  cereal::base_class<internal::Vector3DDerivAttributeTable>(this),
208  cereal::base_class<internal::Vector4DAttributeTable>(this),
209  cereal::base_class<internal::Vector4DDerivAttributeTable>(this));
210 
211  if (std::is_base_of<cereal::detail::InputArchiveBase, Archive>::value) {
212  size_t count;
213  free_particles_.clear();
214  ar(count);
215  particle_index_.clear();
216  while(count-- > 0) {
217  std::string name;
218  ar(name);
219  add_particle(name);
220  }
221  ParticleIndexes to_free;
222  ar(to_free);
223  for (auto pi : to_free) {
224  remove_particle(pi);
225  }
226  } else {
227  size_t count = particle_index_.size();
228  ar(count);
229  for (size_t i = 0; i < count; ++i) {
230  std::string name;
231  if (get_has_particle(ParticleIndex(i))) {
232  name = get_particle_name(ParticleIndex(i));
233  }
234  ar(name);
235  }
236  ar(free_particles_);
237  }
238 
239  // Need particle info before anything that might refer to a particle
240  // (ScoreState, or arbitrary Object)
241  ar(cereal::base_class<internal::ObjectAttributeTable>(this),
242  cereal::base_class<internal::ObjectsAttributeTable>(this),
243  cereal::base_class<internal::WeakObjectAttributeTable>(this),
244  model_data_, mutable_access_score_states());
245 
246  if (std::is_base_of<cereal::detail::InputArchiveBase, Archive>::value) {
247  // clear caches
248  age_counter_ = 1;
249  trigger_age_.clear();
250  dependencies_age_ = 0;
251  removed_particles_attributes_age_ = 0;
252  saved_dependencies_age_ = 0;
253  dependencies_saved_ = false;
254  moved_particles_cache_age_ = 0;
255  }
256  }
257 
258  // update model age (can never be zero, even if it wraps)
259  void increase_age() {
260  age_counter_++;
261  if (age_counter_ == 0) {
262  age_counter_ = 1;
263  }
264  }
265 
266  template <class MOType, class MOVector>
267  void do_get_dependent(ModelObject *mo, MOVector &ret) {
268  const auto &node = dependency_graph_.find(mo);
270  "Object " << mo->get_name()
271  << " does not have dependencies.");
272  IMP_INTERNAL_CHECK(node != dependency_graph_.end(),
273  "Node not in dependency_graph.");
274  MOType *r = dynamic_cast<MOType *>(mo);
275  if (r) {
276  ret.push_back(r);
277  }
278  for (ModelObject *cur : node->second.get_outputs()) {
279  do_get_dependent<MOType, MOVector>(cur, ret);
280  }
281  for (ModelObject *cur : node->second.get_readers()) {
282  do_get_dependent<MOType, MOVector>(cur, ret);
283  }
284  }
285 
286 #if !defined(IMP_DOXYGEN) && !defined(SWIG)
287  // things the evaluate template functions need, can't be bothered with friends
288  public:
289 #endif
290  // check more things on the first call
291  bool first_call_;
292  // the stage of evaluation
293  internal::Stage cur_stage_;
294 
295  //! Get all Restraints that depend on the given particle
296  const std::set<Restraint *> &get_dependent_restraints(ParticleIndex pi) {
297  return moved_particles_restraint_cache_.get_dependent_restraints(pi);
298  }
299 
300  //! Get all particles that depend on the given particle
301  const ParticleIndexes &get_dependent_particles(ParticleIndex pi) {
302  return moved_particles_particle_cache_.get_dependent_particles(pi);
303  }
304 
305  ModelObjectsTemp get_dependency_graph_inputs(const ModelObject *mo) const;
306  ModelObjectsTemp get_dependency_graph_outputs(const ModelObject *mo) const;
307  bool do_get_has_dependencies(const ModelObject *mo) const {
308  return no_dependencies_.find(mo) == no_dependencies_.end();
309  }
310  void do_set_has_dependencies(const ModelObject *mo, bool tf);
311  void do_set_has_all_dependencies(bool tf);
312 
313  void validate_computed_derivatives() const {}
314  void set_has_all_dependencies(bool tf);
315  bool get_has_all_dependencies() const;
316  void check_dependency_invariants() const;
317  void check_dependency_invariants(const ModelObject *mo) const;
318  ScoreStatesTemp get_ancestor_score_states(const ModelObject *mo) const;
319  ScoreStatesTemp get_descendent_score_states(const ModelObject *mo) const;
320 
321  void before_evaluate(const ScoreStatesTemp &states);
322  void after_evaluate(const ScoreStatesTemp &states, bool calc_derivs);
323 
324  internal::Stage get_stage() const { return cur_stage_; }
325  ParticleIndex add_particle_internal(Particle *p);
326  static void do_remove_score_state(ScoreState *obj);
327  void do_add_score_state(ScoreState *obj);
328  void do_remove_particle(ParticleIndex pi);
329  bool do_get_has_required_score_states(const ModelObject *mo) const;
330  void do_set_has_required_score_states(ModelObject *mo, bool tf);
331  const ScoreStatesTemp &do_get_required_score_states(const ModelObject *mo)
332  const {
333  IMP_USAGE_CHECK(do_get_has_required_score_states(mo),
334  "Doesn't have score states");
335  return required_score_states_.find(mo)->second;
336  }
337  void do_add_model_object(ModelObject *mo);
338  void do_remove_model_object(ModelObject *mo);
339 
340  public:
341  //! Construct an empty model
342  Model(std::string name = "Model %1%");
343 
344  public:
345 #if !defined(SWIG) && !defined(IMP_DOXYGEN)
346  IMP_MODEL_DERIV_IMPORT(internal::FloatAttributeTable);
347  IMP_MODEL_IMPORT(internal::StringAttributeTable);
348  IMP_MODEL_IMPORT(internal::IntAttributeTable);
349  IMP_MODEL_IMPORT(internal::ObjectAttributeTable);
350  IMP_MODEL_IMPORT(internal::WeakObjectAttributeTable);
351  IMP_MODEL_IMPORT(internal::IntsAttributeTable);
352  IMP_MODEL_IMPORT(internal::FloatsAttributeTable);
353  IMP_MODEL_IMPORT(internal::Vector3DAttributeTable);
354  IMP_MODEL_IMPORT(internal::Vector4DAttributeTable);
355  IMP_MODEL_IMPORT(internal::ObjectsAttributeTable);
356  IMP_MODEL_IMPORT(internal::ParticleAttributeTable);
357  IMP_MODEL_IMPORT(internal::ParticlesAttributeTable);
358  IMP_MODEL_SPARSE_IMPORT(internal::SparseStringAttributeTable);
359  IMP_MODEL_SPARSE_IMPORT(internal::SparseIntAttributeTable);
360  IMP_MODEL_SPARSE_IMPORT(internal::SparseFloatAttributeTable);
361  IMP_MODEL_SPARSE_IMPORT(internal::SparseParticleAttributeTable);
362  IMP_MODEL_DERIV_IMPORT(internal::Vector3DDerivAttributeTable);
363  IMP_MODEL_DERIV_IMPORT(internal::Vector4DDerivAttributeTable);
364 
365  void zero_derivatives() {
366  internal::FloatAttributeTable::zero_derivatives();
367  internal::Vector3DDerivAttributeTable::zero_derivatives();
368  internal::Vector4DDerivAttributeTable::zero_derivatives();
369  }
370 
371 #endif
372  //! Clear all the cache attributes of a given particle.
373  void clear_particle_caches(ParticleIndex pi);
374 
375  //! Add particle to the model
376  ParticleIndex add_particle(std::string name);
377 
378  //! Get the name of a particle
379  std::string get_particle_name(ParticleIndex pi);
380 
381  //! Add the passed Undecorator to the particle.
382  void add_undecorator(ParticleIndex pi, Undecorator *d);
383 
384 #if !defined(IMP_DOXYGEN)
385  RestraintsTemp get_dependent_restraints_uncached(ParticleIndex pi);
386 
387  ParticlesTemp get_dependent_particles_uncached(ParticleIndex pi);
388 
389  ScoreStatesTemp get_dependent_score_states_uncached(ParticleIndex pi);
390 #endif
391 
392  /** @name States
393 
394  ScoreStates maintain invariants in the Model (see ScoreState
395  for more information.)
396 
397  ScoreStates do not need to be explicitly added to the Model, but they
398  can be if desired in order to keep them alive as long as the model is
399  alive.
400 
401  \advancedmethod
402  */
403  /**@{*/
404  IMP_LIST_ACTION(public, ScoreState, ScoreStates, score_state, score_states,
405  ScoreState *, ScoreStates, do_add_score_state(obj), {},
406  do_remove_score_state(obj));
407  /**@}*/
408 
409  public:
410 #ifndef SWIG
411  using Object::clear_caches;
412 #endif
413 
414  //! Sometimes it is useful to be able to make sure the model is up to date
415  /** This method updates all the state but does not necessarily compute the
416  score. Use this to make sure that your containers and rigid bodies are
417  up to date.
418  */
419  void update();
420 
421  //! Determine and return the correct order to evaluate ScoreStates in
422  /** ScoreStates are not evaluated in the order in which they were added
423  to the Model; instead, the Model's dependency graph is used to ensure
424  that a ScoreState that takes particle x as an input is always
425  evaluated after a state that modifies particle x on output.
426  This method determines and returns the correct order. This is also
427  cached in the ScoreStates themselves; see IMP::get_update_order.
428  */
429  ScoreStatesTemp get_ordered_score_states();
430 
431 #ifdef IMP_DOXYGEN
432  /** \name Accessing attributes
433  \anchor model_attributes
434  All the attribute data associated with each Particle are stored in the
435  Model. For each type of attribute, there are the methods detailed below
436  (where, eg, TypeKey is FloatKey or StringKey)
437  @{
438  */
439  //! add particle attribute with the specified key and initial value
440  /** \pre get_has_attribute(attribute_key, particle) is false*/
441  void add_attribute(TypeKey attribute_key, ParticleIndex particle, Type value);
442 
443  //! remove particle attribute with the specified key
444  /** \pre get_has_attribute(attribute_key, particle) is true*/
445  void remove_attribute(TypeKey attribute_key, ParticleIndex particle);
446 
447  //! return true if particle has attribute with the specified key
448  bool get_has_attribute(TypeKey attribute_key, ParticleIndex particle) const;
449 
450  //! set the value of particle attribute with the specified key
451  /** \pre get_has_attribute(attribute_key, particle) is true*/
452  void set_attribute(TypeKey attribute_key, ParticleIndex particle, Type value);
453 
454  //! get the value of the particle attribute with the specified key
455  /** \pre get_has_attribute(attribute_key, particle) is true*/
456  Type get_attribute(TypeKey attribute_key, ParticleIndex particle);
457 
458  /** Cache attributes, unlike normal attributes, can be added during
459  evaluation. They are also cleared by the clear_cache_attributes() method.
460  Cache attributes should be used when one is adding data to a particle
461  to aid scoring (eg cache the rigid body collision acceleration structure).
462 
463  When some pertinent aspect of the particle changes, the clear method
464  should
465  be called (yes, this is a bit vague). Examples where it should be cleared
466  include changing the set of members of a core::RigidBody or their
467  coordinates, changing the members of an atom::Hierarchy.
468  */
469  void add_cache_attribute(TypeKey attribute_key, ParticleIndex particle,
470  Type value);
471 
472  //! Optimized attributes are the parameters of the model that are
473  //! allowed to be modified by samplers and optimizers
474  void set_is_optimized(TypeKey attribute_key, ParticleIndex particle,
475  bool true_or_false);
476 /** @} */
477 #endif
478 
479 #ifdef SWIG
480 #define IMP_MODEL_ATTRIBUTE_METHODS(Type, Value) \
481  void add_attribute(Type##Key attribute_key, ParticleIndex particle, \
482  Value value); \
483  void remove_attribute(Type##Key attribute_key, ParticleIndex particle); \
484  bool get_has_attribute(Type##Key attribute_key, \
485  ParticleIndex particle) const; \
486  void set_attribute(Type##Key attribute_key, ParticleIndex particle, \
487  Value value); \
488  Value get_attribute(Type##Key attribute_key, ParticleIndex particle); \
489  void add_cache_attribute(Type##Key attribute_key, ParticleIndex particle, \
490  Value value)
491 
492 #define IMP_MODEL_DERIV_ATTRIBUTE_METHODS(Type, Value) \
493  IMP_MODEL_ATTRIBUTE_METHODS(Type, Value); \
494  void add_to_derivative(Type##Key attribute_key, ParticleIndex particle, \
495  const Value &v, const DerivativeAccumulator &da); \
496  void set_is_optimized(Type##Key, ParticleIndex, bool)
497 
498 #define IMP_MODEL_SPARSE_ATTRIBUTE_METHODS(Type, Value) \
499  void add_attribute(Type##Key attribute_key, ParticleIndex particle, \
500  Value value); \
501  void remove_attribute(Type##Key attribute_key, ParticleIndex particle); \
502  bool get_has_attribute(Type##Key attribute_key, \
503  ParticleIndex particle) const; \
504  void set_attribute(Type##Key attribute_key, ParticleIndex particle, \
505  Value value); \
506  Value get_attribute(Type##Key attribute_key, ParticleIndex particle)
507 
508  IMP_MODEL_ATTRIBUTE_METHODS(Float, Float);
509  IMP_MODEL_ATTRIBUTE_METHODS(Int, Int);
510  IMP_MODEL_ATTRIBUTE_METHODS(Floats, Floats);
511  IMP_MODEL_ATTRIBUTE_METHODS(Vector3D, IMP::Vector3D);
512  IMP_MODEL_ATTRIBUTE_METHODS(Vector4D, IMP::Vector4D);
513  IMP_MODEL_DERIV_ATTRIBUTE_METHODS(Vector3DDeriv, IMP::Vector3D);
514  IMP_MODEL_DERIV_ATTRIBUTE_METHODS(Vector4DDeriv, IMP::Vector4D);
515  IMP_MODEL_ATTRIBUTE_METHODS(Ints, Ints);
516  IMP_MODEL_ATTRIBUTE_METHODS(String, String);
517  IMP_MODEL_ATTRIBUTE_METHODS(ParticleIndexes, ParticleIndexes);
518  IMP_MODEL_ATTRIBUTE_METHODS(ParticleIndex, ParticleIndex);
519  IMP_MODEL_ATTRIBUTE_METHODS(Object, Object *);
520  IMP_MODEL_ATTRIBUTE_METHODS(WeakObject, Object *);
521  IMP_MODEL_SPARSE_ATTRIBUTE_METHODS(SparseString, String);
522  IMP_MODEL_SPARSE_ATTRIBUTE_METHODS(SparseInt, Int);
523  IMP_MODEL_SPARSE_ATTRIBUTE_METHODS(SparseFloat, Float);
524  IMP_MODEL_SPARSE_ATTRIBUTE_METHODS(SparseParticleIndex, ParticleIndex);
525  void set_is_optimized(FloatKey, ParticleIndex, bool);
526  void add_to_derivative(FloatKey k, ParticleIndex particle, double v,
527  const DerivativeAccumulator &da);
528 #endif
529 
530  //! Get the particle from an index.
532  IMP_USAGE_CHECK(get_has_particle(p), "Invalid particle requested");
533  return particle_index_[p];
534  }
535 
536  //! Check whether a given particle index exists.
538  if (particle_index_.size() <= get_as_unsigned_int(p)) return false;
539  return particle_index_[p];
540  }
541 
542  //! Get all particle indexes
544 
545  //! Get all the ModelObjects associated with this Model.
546  ModelObjectsTemp get_model_objects() const;
547 
548  //! Remove a particle from the Model.
549  /** The particle will then be inactive and cannot be used for anything
550  and all data stored in the particle is lost.
551  */
552  void remove_particle(ParticleIndex pi);
553 
554  /** \name Storing data in the model
555 
556  One can store data associated with the model. This is used, for example,
557  to keep a central ScoreState to normalize rigid body rotational variables.
558  @{ */
559  //! Store a piece of data in the model referenced by the key.
560  void add_data(ModelKey mk, Object *o);
561  //! Get back some data stored in the model.
562  Object *get_data(ModelKey mk) const;
563  //! Remove data stored in the model.
564  void remove_data(ModelKey mk);
565  //! Check if the model has a certain piece of data attached.
566  bool get_has_data(ModelKey mk) const;
567  /** @} */
568 
569  /** \name Model triggers
570 
571  Triggers can be used to track when to clear and rebuild caches
572  of derived model properties. For example, a Restraint may restrain
573  two particles as a function of the number of chemical bonds between
574  them. To speed up this restraint, the bond graph can be cached; however,
575  this graph needs to be rebuilt if bonds are created or removed. This
576  can be achieved by checking that the model time (see get_age()) of the
577  cache matches the time when the 'bond added/removed' Trigger was last
578  updated (see get_trigger_last_updated()), either when the Restraint is
579  evaluated or in an associated ScoreState.
580 
581  Triggers are intended for events that are rare during a typical
582  optimization. Triggers can be created by any IMP module in either C++
583  or Python by creating a new TriggerKey, much as model attributes
584  are handled. To avoid name collisions, it is recommended to prepend
585  the module and/or class name to the trigger, e.g. "atom.Bond.changed".
586 
587  For an example, see IMP::score_functor::OrientedSoap, which uses
588  a cache built from the molecular hierarchy, which is cleared when the
589  IMP::core::Hierarchy::get_changed_key() trigger is updated.
590 
591  @{ */
592 
593  //! Get the current 'model time'.
594  /** This is a number 1 or more that tracks the 'age' of the model;
595  it is incremented every time before_evaluate() is called.
596  It may wrap (and so should not be assumed to always increase)
597  but will never be 0. */
598  unsigned get_age() { return age_counter_; }
599 
600  //! Get the time when the given trigger was last updated, or 0.
601  /** Return the 'model time' (as given by get_age()) when the given
602  trigger was last updated on this model, or 0 if never. */
604  if (trigger_age_.size() > tk.get_index()) {
605  return trigger_age_[tk.get_index()];
606  } else {
607  return 0;
608  }
609  }
610 
611  //! Update the given trigger
613  if (tk.get_index() >= trigger_age_.size()) {
614  trigger_age_.resize(tk.get_index() + 1, 0);
615  }
616  trigger_age_[tk.get_index()] = age_counter_;
617  }
618  /** @} */
619 
620  //! Get the model age when ModelObject dependencies were last changed, or 0.
621  /** This gives the Model age (see get_age()) when Particles, Restraints,
622  or ScoreStates were last added or removed. It is typically used to
623  help maintain caches that depend on the model's dependency graph. */
624  unsigned get_dependencies_updated() { return dependencies_age_; }
625 
626  //! Get the model age when particles or attributes were last removed, or 0.
627  /** This gives the Model age (see get_age()) when any particle or attribute
628  was last removed. It is typically used by callers that rely on certain
629  particles or decorators being present. */
631  return removed_particles_attributes_age_;
632  }
633 
634  //! Mark a 'restore point' for ModelObject dependencies.
635  /** \see restore_dependencies() */
637  dependencies_saved_ = true;
638  saved_dependencies_age_ = dependencies_age_;
640  mos_added_since_save_.clear();
641  mos_removed_since_save_.clear();
642  }
643  }
644 
645  //! Restore ModelObject dependencies to previous restore point.
646  /** This method, when paired with save_dependencies(), can be used to
647  avoid triggering a model dependency update due to a temporary change
648  in the model dependency graph, for example due to adding a temporary
649  restraint, evaluating it, then removing that same restraint. It should
650  only be called in cases where it is known that the dependency graph
651  is the same as when save_dependencies() was called (this is only checked
652  in debug mode). Save/restore call pairs cannot be nested, although it
653  is OK to skip the call to restore_dependencies(), e.g. if an exception
654  occurs.
655 
656  \see get_dependencies_updated()
657  \see save_dependencies()
658  */
660  if (dependencies_saved_) {
661  dependencies_saved_ = false;
662  dependencies_age_ = saved_dependencies_age_;
664  // Need to sort pointers since we may not add/remove in the same order
665  std::sort(mos_added_since_save_.begin(), mos_added_since_save_.end());
666  std::sort(mos_removed_since_save_.begin(),
667  mos_removed_since_save_.end());
668  IMP_INTERNAL_CHECK(mos_added_since_save_ == mos_removed_since_save_,
669  "ModelObjects added do not match those removed");
670  }
671  }
672  }
673 
674  //! Get an upper bound on the number of particles in the Model.
675  /** This value is guaranteed to be at least the number of particles in
676  the model (there may be fewer particles if any have been removed)
677  and every ParticleIndex will be smaller than this value. */
678  unsigned get_particles_size() const { return particle_index_.size(); }
679 
680  //! Get the unique ID of this Model.
681  /** When multiple Models exist simultaneously, each has a different unique ID.
682  */
683  uint32_t get_unique_id() const {
684  return unique_id_;
685  }
686 
687  //! Return the Model with the given unique ID.
688  /** If no Model with this ID exists, nullptr is returned. */
689  static Model* get_by_unique_id(uint32_t id) {
690  return model_map_.get(id);
691  }
692 
694 
695  public:
696 #if !defined(IMP_DOXYGEN)
697  virtual void do_destroy() override;
698 #endif
699 };
700 
701 IMPKERNEL_END_NAMESPACE
702 
703 CEREAL_SPECIALIZE_FOR_ALL_ARCHIVES(
704  IMP::Model, cereal::specialization::member_serialize);
705 
706 CEREAL_CLASS_VERSION(IMP::Model, 1);
707 
708 // This is needed for per cpp compilations, a not even sure why
709 // (perhaps cause Model returns ParticleIterator here and there?)
710 // - Feel free to remove if you *really* know what you're doing
711 #include "IMP/Particle.h"
712 
713 #endif /* IMPKERNEL_MODEL_H */
Particle * get_particle(ParticleIndex p) const
Get the particle from an index.
Definition: Model.h:531
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:659
Index< ParticleIndexTag > ParticleIndex
Definition: base_types.h:194
VectorD< 4 > Vector4D
Definition: VectorD.h:411
void add_particle(RMF::FileHandle fh, Particle *hs)
Macros to help in defining tuple classes.
virtual void clear_caches()
Definition: Object.h:270
unsigned get_dependencies_updated()
Get the model age when ModelObject dependencies were last changed, or 0.
Definition: Model.h:624
A more IMP-like version of the std::vector.
Definition: Vector.h:50
unsigned get_particles_size() const
Get an upper bound on the number of particles in the Model.
Definition: Model.h:678
unsigned get_age()
Get the current 'model time'.
Definition: Model.h:598
bool get_has_particle(ParticleIndex p) const
Check whether a given particle index exists.
Definition: Model.h:537
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:603
#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:86
Base class for objects in a Model that depend on other objects.
Definition: ModelObject.h:28
virtual void do_destroy()
Definition: Object.h:274
Common base class for heavy weight IMP objects.
Definition: Object.h:111
ParticleIndexes get_particle_indexes(ParticlesTemp const &particles)
VectorD< 3 > Vector3D
Definition: VectorD.h:407
ScoreStates maintain invariants in the Model.
Definition: ScoreState.h:56
static Model * get_by_unique_id(uint32_t id)
Return the Model with the given unique ID.
Definition: Model.h:689
uint32_t get_unique_id() const
Get the unique ID of this Model.
Definition: Model.h:683
Implements a vector tied to a particular index of type Index<Tag>.
Definition: Index.h:90
unsigned get_removed_particles_attributes_age()
Get the model age when particles or attributes were last removed, or 0.
Definition: Model.h:630
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:636
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:45
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:612
std::string String
Basic string value.
Definition: types.h:43
Class for adding derivatives from restraints to the model.