IMP logo

Particle.h

Go to the documentation of this file.
00001 /**
00002  *  \file Particle.h     \brief Classes to handle individual model particles.
00003  *
00004  *  Copyright 2007-2011 IMP Inventors. All rights reserved.
00005  *
00006  */
00007 
00008 #ifndef IMP_PARTICLE_H
00009 #define IMP_PARTICLE_H
00010 
00011 #include "kernel_config.h"
00012 #include "base_types.h"
00013 #include "Object.h"
00014 #include "internal/particle.h"
00015 #include "utility.h"
00016 #include "Key.h"
00017 #include "internal/AttributeTable.h"
00018 #include "DerivativeAccumulator.h"
00019 #include "Pointer.h"
00020 #include "VectorOfRefCounted.h"
00021 #include "container_base.h"
00022 #include <utility>
00023 #include <boost/scoped_ptr.hpp>
00024 
00025 
00026 // should use this once we move to a new enough boost (1.35)
00027 //#include <boost/intrusive/list.hpp>
00028 
00029 #include <list>
00030 
00031 #define IMP_PI(func) if (name.get_index() < IMP_NUM_INLINE) floats_.func;\
00032   else ps_->floats_.func;
00033 #define IMP_RPI(func) if (name.get_index() < IMP_NUM_INLINE) {  \
00034     return floats_.func;                                        \
00035   }                                                             \
00036   else return ps_->floats_.func;
00037 
00038 
00039 #if IMP_BUILD < IMP_FAST
00040 #define IMP_CHECK_ACTIVE                                                \
00041   IMP_USAGE_CHECK(get_is_active(), "Particle " << get_name() << " is inactive");
00042 
00043 #define IMP_CHECK_READABLE IMP_IF_CHECK(USAGE) {assert_values_readable();}
00044 #define IMP_CHECK_MUTABLE IMP_IF_CHECK(USAGE) {assert_values_mutable();}
00045 #define IMP_CHECK_VALID_DERIVATIVES IMP_IF_CHECK(USAGE) \
00046   {assert_valid_derivatives();}
00047 
00048 #else
00049 #define IMP_CHECK_ACTIVE
00050 #define IMP_CHECK_READABLE
00051 #define IMP_CHECK_MUTABLE
00052 #define IMP_CHECK_VALID_DERIVATIVES
00053 
00054 #endif
00055 
00056 #define IMP_PARTICLE_ATTRIBUTE_TYPE(UCName, lcname, Value, cond,        \
00057                                     table0, table1,                     \
00058                                     add_action, remove_action)          \
00059   void add_attribute(UCName##Key name, Value initial_value){            \
00060     IMP_CHECK_ACTIVE;                                                   \
00061     IMP_CHECK_MUTABLE;                                                  \
00062     IMP_USAGE_CHECK(name != UCName##Key(),                              \
00063                     "Cannot use attributes without "                    \
00064                     << "naming them.");                                 \
00065     IMP_USAGE_CHECK(!has_attribute(name),                               \
00066               "Cannot add attribute " << name << " to particle "        \
00067                     << get_name() << " twice.");                        \
00068     IMP_USAGE_CHECK(UCName##Table::Traits::get_is_valid(initial_value), \
00069                     "Initial value is not valid when adding attribute"  \
00070                     << name << " to particle " << get_name());          \
00071     on_changed();                                                       \
00072     add_action;                                                         \
00073     if (cond) table0.add(name.get_index(), initial_value);              \
00074     else table1.add(name.get_index(), initial_value);                   \
00075   }                                                                     \
00076   void remove_attribute(UCName##Key name) {                             \
00077     IMP_CHECK_ACTIVE;                                                   \
00078     IMP_USAGE_CHECK(name != UCName##Key(),                              \
00079                     "Cannot use attributes without "                    \
00080                     << "naming them.");                                 \
00081     on_changed();                                                       \
00082     remove_action;                                                      \
00083     IMP_USAGE_CHECK(has_attribute(name),                                \
00084               "Cannot remove attribute " << name << " from particle "   \
00085                     << get_name() << " as it is not there.");           \
00086     if (cond) table0.remove(name.get_index());                          \
00087     else table1.remove(name.get_index());                               \
00088   }                                                                     \
00089   bool has_attribute(UCName##Key name) const{                           \
00090     IMP_USAGE_CHECK(name != UCName##Key(),                              \
00091                     "Cannot use attributes without "                    \
00092                     << "naming them.");                                 \
00093     IMP_CHECK_ACTIVE;                                                   \
00094     if (cond) {                                                         \
00095       if (!table0.fits(name.get_index())) return false;                 \
00096       else {                                                            \
00097         return UCName##Table::Traits::get_is_valid(                     \
00098                                          table0.get(name.get_index())); \
00099       }                                                                 \
00100     } else {                                                            \
00101       if (!table1.fits(name.get_index())) return false;                 \
00102       else {                                                            \
00103         return UCName##Table::Traits::get_is_valid(                     \
00104                                          table1.get(name.get_index())); \
00105       }                                                                 \
00106     }                                                                   \
00107   }                                                                     \
00108   inline Value get_value(UCName##Key name) const {                      \
00109     IMP_CHECK_ACTIVE;                                                   \
00110     IMP_CHECK_READABLE;                                                 \
00111     IMP_USAGE_CHECK(name != UCName##Key(),                              \
00112                     "Cannot use attributes without "                    \
00113                     << "naming them.");                                 \
00114     IMP_USAGE_CHECK(has_attribute(name),                                \
00115               "Cannot get value " << name << " from particle "          \
00116                     << get_name() << " as it is not there.");           \
00117     if (cond) return table0.get(name.get_index());                      \
00118     else return table1.get(name.get_index());                           \
00119   }                                                                     \
00120   void set_value(UCName##Key name, Value value) {                       \
00121     IMP_USAGE_CHECK(name != UCName##Key(),                              \
00122                     "Cannot use attributes without "                    \
00123                     << "naming them.");                                 \
00124     if (!UCName##Table::Traits::get_is_valid(value)) {                  \
00125       IMP_THROW("Cannot set value of " << name                          \
00126                 << " to " << value                                      \
00127                 << " on particle " << get_name(), ModelException);      \
00128     }                                                                   \
00129     IMP_CHECK_ACTIVE;                                                   \
00130     IMP_CHECK_MUTABLE;                                                  \
00131     IMP_USAGE_CHECK(has_attribute(name),                                \
00132               "Cannot set value " << name << " from particle "          \
00133                     << get_name() << " as it is not there.");           \
00134     on_changed();                                                       \
00135     if (cond) table0.set(name.get_index(), value);                      \
00136     else table1.set(name.get_index(), value);                           \
00137   }                                                                     \
00138   IMP_SWITCH_DOXYGEN(class UCName##KeyIterator,                         \
00139          typedef UCName##IteratorTraits::Iterator UCName##KeyIterator); \
00140   UCName##KeyIterator lcname##_keys_begin() const {                     \
00141     return UCName##IteratorTraits::create_iterator(this, 0,             \
00142                                                    table1.get_length()); \
00143   }                                                                     \
00144   UCName##KeyIterator lcname##_keys_end() const {                       \
00145     return UCName##IteratorTraits::create_iterator(this,                \
00146                                                    table1.get_length(), \
00147                                                    table1.get_length()); \
00148   }                                                                     \
00149   UCName##Keys get_##lcname##_attributes() const {                      \
00150     return UCName##Keys(lcname##_keys_begin(),                          \
00151                         lcname##_keys_end());                           \
00152   }                                                                     \
00153   IMP_REQUIRE_SEMICOLON_CLASS(lcname)
00154 
00155 
00156 
00157 IMP_BEGIN_NAMESPACE
00158 
00159 class Model;
00160 class Changed;
00161 class SaveOptimizeds;
00162 
00163 //! Class to handle individual model particles.
00164 /**
00165 
00166    \note Direct manipuation of particles is considered advanced
00167    and Particles should only be manipulated through
00168    IMP::Decorator derived classes.
00169 
00170    A IMP::Particle is a mapping between keys and values.
00171 
00172    Four possible types of values:
00173    - Float (float)
00174    - String (std::string or Python string)
00175    - Int (int)
00176    - Particle (A pointer to another IMP::Particle)
00177 
00178    To use an attribute you first create a key
00179    \verbatim
00180    f= IMP.FloatKey("MyAttribute")
00181    \endverbatim
00182    Creating a key is expensive and should not be done often.
00183 
00184    Then use it to manipulate the attribute.
00185    \verbatim
00186    p.add_attribute(f, initial_value, whether_attribute_is_optimized)
00187    p.set_attribute(f, new_value)
00188    p.remove_attribute(f)
00189    \endverbatim
00190 
00191 
00192 
00193    This class contains particle methods and indexes to particle attributes.
00194    To prevent a particle from being moved by the optimizer during
00195    optimization, mark all of its attributes as being non-optimizable
00196    (set_is_optimized method). Note that this only affects the optimizer,
00197    ScoreStates may still change the particle attributes.
00198 
00199    A particle may only belong to one model.
00200 
00201    Any attempt to access or change an attribute which the particle does not
00202    have results is undefined. It will throw an exception if checks are on
00203    or possibly just crash if they are not. Likewise an attempt to touch
00204    an inactive particle is also undefined (and will throw an exception if
00205    checks are enabled).
00206 */
00207 class IMPEXPORT Particle : public Container
00208 {
00209  private:
00210   // doxygen produces funny docs for these things
00211 #ifndef IMP_DOXYGEN
00212   friend class Model;
00213   friend class Changed;
00214   friend class SaveOptimizeds;
00215   friend struct internal::ReadLock;
00216   friend struct internal::WriteLock;
00217   //typedef internal::ObjectContainer<Particle, unsigned int> Storage;
00218   typedef internal::ParticleStorage::Storage Storage;
00219   void zero_derivatives();
00220 
00221   void assert_values_mutable() const;
00222   void assert_values_readable() const;
00223 
00224   void assert_can_change_optimization() const;
00225 
00226   void assert_can_change_derivatives() const;
00227 
00228   void assert_valid_derivatives() const;
00229 
00230   /*void validate_float_attributes() const {
00231     for (unsigned int i=0; i< floats_.get_length(); ++i) {
00232       if (FloatTable::Traits::get_is_valid(floats_.get(i))) {
00233         if (! (floats_.get(i) <= std::numeric_limits<double>::max())) {
00234           IMP_THROW("Bad attribute value", ModelException);
00235         }
00236       }
00237     }
00238     for (unsigned int i=floats_.get_length();
00239     i< ps_->floats_.get_length(); ++i) {
00240       if (FloatTable::Traits::get_is_valid(ps_->floats_.get(i))) {
00241         if (! (ps_->floats_.get(i) <= std::numeric_limits<double>::max())) {
00242           IMP_THROW("Bad attribute value", ModelException);
00243         }
00244       }
00245     }
00246   }*/
00247   void validate_float_derivatives() const {
00248     for (unsigned int i=0; i< ps_->derivatives_.get_length(); ++i) {
00249       if (ps_->optimizeds_.fits(i) && ps_->optimizeds_.get(i)) {
00250         if (! (ps_->derivatives_.get(i) < std::numeric_limits<double>::max())) {
00251           IMP_THROW("Bad attribute value", ModelException);
00252         }
00253       }
00254     }
00255   }
00256  // begin incremental
00257   void on_changed() {
00258     dirty_=true;
00259   }
00260 
00261   void set_is_not_changed() {
00262     if (dirty_ && ps_->shadow_) {
00263       ps_->shadow_->floats_= floats_;
00264       ps_->shadow_->ps_->floats_= ps_->floats_;
00265       ps_->shadow_->ps_->strings_= ps_->strings_;
00266       ps_->shadow_->ps_->ints_= ps_->ints_;
00267       ps_->shadow_->ps_->optimizeds_= ps_->optimizeds_;
00268       ps_->shadow_->ps_->particles_.clear();
00269       for (ParticleKeyIterator it= particle_keys_begin();
00270            it != particle_keys_end(); ++it) {
00271         ps_->shadow_->ps_->particles_.add(it->get_index(),
00272                                           get_value(*it)->ps_->shadow_);
00273       }
00274     }
00275     dirty_=false;
00276   }
00277 
00278   void setup_incremental();
00279 
00280   void teardown_incremental();
00281 
00282   // don't add the particle to the model, used for incremental
00283   Particle();
00284 
00285   void accumulate_derivatives_from_shadow();
00286   void move_derivatives_to_shadow();
00287   // end incremental
00288 
00289   typedef internal::SphereInlineStorage FloatTable;
00290   typedef internal::ParticleStorage::IntTable IntTable;
00291   typedef internal::ParticleStorage::StringTable StringTable;
00292   typedef internal::ParticleStorage::ParticleTable ParticleTable;
00293   typedef internal::ParticleStorage::ObjectTable ObjectTable;
00294 
00295   typedef internal::ArrayStorage<internal::DoubleAttributeTableTraits>
00296     DerivativeTable;
00297   typedef internal::ParticleKeyIterator<FloatKey, Particle,
00298     internal::IsAttribute<FloatKey, Particle> > FloatIteratorTraits;
00299   typedef internal::ParticleKeyIterator<IntKey, Particle,
00300     internal::IsAttribute<IntKey, Particle> > IntIteratorTraits;
00301   typedef internal::ParticleKeyIterator<StringKey, Particle,
00302     internal::IsAttribute<StringKey, Particle> > StringIteratorTraits;
00303   typedef internal::ParticleKeyIterator<ParticleKey, Particle,
00304     internal::IsAttribute<ParticleKey, Particle> > ParticleIteratorTraits;
00305   typedef internal::ParticleKeyIterator<ObjectKey, Particle,
00306     internal::IsAttribute<ObjectKey, Particle> > ObjectIteratorTraits;
00307 
00308 
00309   typedef internal::ParticleKeyIterator<FloatKey, Particle,
00310     internal::IsOptimized<FloatKey, Particle> > OptimizedIteratorTraits;
00311 
00312  private:
00313   FloatTable floats_;
00314   boost::scoped_ptr<internal::ParticleStorage> ps_;
00315   bool dirty_;
00316 #endif
00317 
00318   IMP_OBJECT(Particle);
00319  public:
00320 
00321   //! Construct a particle and add it to the Model
00322   Particle(Model *m, std::string name="P%1%");
00323 
00324 
00325 #ifdef IMP_DOXYGEN
00326   /** @name Attribute manipulation
00327       For each type of attribute and their corresponding key type,
00328       the Particle provides the following methods. The Type is
00329       the type of the attribute (Float, Int, Particle * etc.) and
00330       KeyType is the type of the key (FloatKey, IntKey, ParticleKey etc.).
00331       @{
00332   */
00333   void add_attribute(KeyType name, Type initial_value);
00334   void remove_attribute(KeyType name);
00335   bool has_attribute(KeyType name) const;
00336   Type get_value(KeyType name) const;
00337   /* @} */
00338 #else
00339 
00340   IMP_PARTICLE_ATTRIBUTE_TYPE(Float, float, Float,
00341                               name.get_index() < IMP_NUM_INLINE,
00342                               floats_, ps_->floats_,
00343                               { ps_->derivatives_.add(name.get_index(), 0);},
00344                               {if (ps_->optimizeds_.fits(name.get_index())) {
00345                                   ps_->optimizeds_.remove(name.get_index());
00346                                 }
00347                                 ps_->derivatives_.remove(name.get_index());});
00348 
00349 #ifdef IMP_DOXYGEN
00350   class OptimizedKeyIterator;
00351 #else
00352   typedef OptimizedIteratorTraits::Iterator OptimizedKeyIterator;
00353 #endif
00354 
00355 
00356   OptimizedKeyIterator optimized_keys_begin() const {
00357     return OptimizedIteratorTraits::create_iterator(this, 0,
00358                                                     ps_->floats_.get_length());
00359   }
00360   OptimizedKeyIterator optimized_keys_end() const {
00361     return OptimizedIteratorTraits::create_iterator(this,
00362                                                     ps_->floats_.get_length(),
00363                                                     ps_->floats_.get_length());
00364   }
00365   IMP_PARTICLE_ATTRIBUTE_TYPE(Int, int, Int,
00366                               true, ps_->ints_,ps_->ints_,{},{});
00367   IMP_PARTICLE_ATTRIBUTE_TYPE(String, string, String,
00368                               true,ps_->strings_,ps_->strings_,{},{});
00369   IMP_PARTICLE_ATTRIBUTE_TYPE(Particle, particle, Particle*,
00370                               true,ps_->particles_,ps_->particles_,{},{});
00371   IMP_PARTICLE_ATTRIBUTE_TYPE(Object, object, Object*,
00372                               true,ps_->objects_,ps_->objects_,{},{});
00373 
00374 #endif
00375 
00376   /** \name  Add cached data to a particle
00377       Restraints and Constraints can cache data in a particle in order
00378       to accelerate computations. This data must obey the following rules:
00379       - it must be optional
00380       - if multiple restraints add the same attribute, it must all be equivalent
00381 
00382       When a Particle is changed in such a way that the cached data might
00383       be affected, the clear_caches() method should be called. Yes, this
00384       is very vague. We don't have a more precise prescription yet.
00385       @{
00386   */
00387   void add_cache_attribute(IntKey name, unsigned int value) {
00388     IMP_USAGE_CHECK(name != IntKey(),
00389                     "Cannot use attributes without "
00390                     << "naming them.");
00391     IMP_USAGE_CHECK(!has_attribute(name),
00392               "Cannot add attribute " << name << " to particle "
00393                     << get_name() << " twice.");
00394     IMP_USAGE_CHECK(IntTable::Traits::get_is_valid(value),
00395                     "Initial value is not valid when adding attribute"
00396                     << name << " to particle " << get_name());
00397     ps_->ints_.add(name.get_index(), value);
00398   }
00399   void add_cache_attribute(ObjectKey name, Object *value) {
00400     IMP_CHECK_ACTIVE;
00401     IMP_USAGE_CHECK(name != ObjectKey(), "Cannot use attributes without "
00402                     << "naming them.");
00403     IMP_USAGE_CHECK(!has_attribute(name),
00404                     "Cannot add attribute " << name << " to particle "
00405                     << get_name() << " twice.");
00406     IMP_USAGE_CHECK(ObjectTable::Traits::get_is_valid(value),
00407                     "Initial value is not valid when adding attribute"
00408                     << name << " to particle " << get_name());
00409     ps_->objects_.add(name.get_index(), value);
00410     ps_->cache_objects_.push_back(name);
00411   }
00412 
00413   void clear_caches();
00414   /** @} */
00415 
00416  /** @name Float Attributes
00417       Float attributes can be optimized, meaning the optimizer is
00418       allowed to change their value in order to improve the score.
00419       As a result, there are a number of extra methods to manipulate
00420       them.
00421 
00422       All distances are assumed to be in angstroms
00423       and derivatives in kcal/mol angstrom. This is not enforced.
00424   */
00425   /*@{*/
00426   void add_attribute(FloatKey name, const Float initial_value, bool optimized){
00427     add_attribute(name, initial_value);
00428     if (optimized) set_is_optimized(name, optimized);
00429   }
00430 
00431   void add_to_derivative(FloatKey key, Float value,
00432                          const DerivativeAccumulator &da);
00433 
00434   void set_is_optimized(FloatKey k, bool tf);
00435 
00436   bool get_is_optimized(FloatKey k) const;
00437 
00438   Float get_derivative(FloatKey name) const;
00439 
00440   /** @} */
00441 
00442   //! Get whether the particle is active.
00443   /** Restraints referencing the particle are only evaluated for 'active'
00444       particles.
00445       \return true it the particle is active.
00446   */
00447   bool get_is_active() const {
00448     IMP_CHECK_OBJECT(this);
00449     return get_has_model();
00450   }
00451 
00452    /** \name Incremental Updates
00453 
00454       Control whether incremental updates are being used. See
00455       the \ref incremental "incremental updates" page for a more
00456       detailed description.
00457       @{
00458   */
00459   //! Return true if this particle has been changed since the last evaluate call
00460   bool get_is_changed() const {
00461     return dirty_;
00462   }
00463   /** \brief Return the shadow particle having attribute values from the last
00464       evaluation
00465   */
00466   Particle *get_prechange_particle() const {
00467     return ps_->shadow_;
00468   }
00469   /** @} */
00470 
00471 #if !defined(IMP_DOXYGEN)&& !defined(SWIG)
00472   void *operator new(std::size_t sz, void*p);
00473   void operator delete(void *p);
00474   void *operator new(std::size_t sz);
00475 #endif
00476 
00477 #if !defined(IMP_DOXYGEN)
00478 #if !defined(SWIG)
00479   const algebra::Sphere3D &_get_coordinates() const {
00480     return floats_.get_data();
00481   }
00482   algebra::Sphere3D &_access_coordinates() {
00483     dirty_=true;
00484     return floats_.access_data();
00485   }
00486 #endif
00487   ContainersTemp get_input_containers() const;
00488   bool get_contained_particles_changed() const;
00489   ParticlesTemp get_contained_particles() const;
00490   bool get_is_up_to_date() const { return true;}
00491 #endif
00492 };
00493 
00494 
00495 IMP_OUTPUT_OPERATOR(Particle);
00496 
00497 inline Float Particle::get_derivative(FloatKey name) const
00498 {
00499   IMP_CHECK_ACTIVE;
00500   IMP_INTERNAL_CHECK(has_attribute(name), "Particle " << get_name()
00501              << " does not have attribute " << name);
00502   IMP_CHECK_VALID_DERIVATIVES;
00503   return ps_->derivatives_.get(name.get_index());
00504 }
00505 
00506 
00507 inline bool Particle::get_is_optimized(FloatKey name) const
00508 {
00509   IMP_CHECK_ACTIVE;
00510   if (!ps_->optimizeds_.fits(name.get_index())) return false;
00511   else return ps_->optimizeds_.get(name.get_index());
00512 }
00513 
00514 inline void Particle::set_is_optimized(FloatKey name, bool tf)
00515 {
00516   IMP_CHECK_ACTIVE;
00517   IMP_USAGE_CHECK(has_attribute(name), "set_is_optimized called "
00518             << "with invalid attribute" << name);
00519   IMP_IF_CHECK(USAGE) {assert_can_change_optimization();}
00520 
00521   if (tf) {
00522     ps_->optimizeds_.add(name.get_index(), true);
00523   } else {
00524     ps_->optimizeds_.remove(name.get_index());
00525   }
00526 }
00527 
00528 inline void Particle::add_to_derivative(FloatKey name, Float value,
00529                                         const DerivativeAccumulator &da)
00530 {
00531   IMP_CHECK_ACTIVE;
00532   IMP_IF_CHECK(USAGE_AND_INTERNAL) {
00533     if (is_nan(value) || !DerivativeTable::Traits::get_is_valid(value)) {
00534       std::string message
00535         =std::string("Can't add NaN to derivative in particle ")+
00536         get_name();
00537       internal::assert_fail(message.c_str());
00538       throw ModelException(message.c_str());
00539     }
00540   }
00541   IMP_INTERNAL_CHECK(has_attribute(name), "Particle " << get_name()
00542              << " does not have attribute " << name);
00543   IMP_IF_CHECK(USAGE_AND_INTERNAL) { assert_can_change_derivatives();}
00544   IMP_INTERNAL_CHECK(name.get_index() < ps_->derivatives_.get_length(),
00545              "Something is wrong with derivative table.");
00546   ps_->derivatives_.set(name.get_index(),
00547                         ps_->derivatives_.get(name.get_index())
00548                         + da(value));
00549 }
00550 
00551 
00552 IMP_END_NAMESPACE
00553 
00554 #undef IMP_CHECK_ACTIVE
00555 #undef IMP_CHECK_MUTABLE
00556 #undef IMP_CHECK_VALID_DERIVATIVES
00557 
00558 #include "Model.h"
00559 
00560 #endif  /* IMP_PARTICLE_H */

Generated on Thu Mar 24 2011 02:01:39 for IMP by doxygen 1.7.3