.. _ensemble-tricks:
.. currentmodule:: openpathsampling
==============================
Advanced Uses of OPS Ensembles
==============================
One of the novelties of OpenPathSampling is the new set-theoretic treatment
of path ensembles. The second paper on OPS contains more details. This
section will illustrate a few uses of that approach.
Visit all states
----------------
We want a path which contains at least one frame in each state. The question
is, what ensemble can we use to create such a trajectory?
The first obvious thought would be:
.. code::
goal_ensemble = PartInXEnsemble(A) & PartInXEnsemble(B)
(which can, of course, be further generalized to more states). However,
while that *is* the ensemble we want to eventually satisfy, we can't use its
``can_append`` to create it, because its ``can_append`` always returns
``True``: the trajectory will go on forever!
But we can use a trick: since what we want is the first trajectory that
satisfies ``goal_ensemble``, we know that every shorter trajectory will not
satisfy it. This means that the shorter trajectories must satisfy the
*complement* of ``goal_ensemble``, and the trajectory we want will be the
first trajectory that does *not* satisfy the complement!
So the trick we'll use is to build the trajectory by using the fact that the
shorter trajectories are in the complement of ``goal_ensemble``, which is
given by:
.. code::
complement = AllOutXEnsemble(A) | AllOutXEnsemble(B)
The ``generate`` function will stop when that is no longer true, giving us the
trajectory we want. This can be directly generalized to more states.
Here we're not even using the ``can_append`` function. That happens to be
the same as the ensemble itself for this particular ensemble, but
conceptually, we're actually using the test of whether a trajectory is in
the ensemble at all.
.. code::
init_traj_ensemble = paths.AllOutXEnsemble(A) | paths.AllOutXEnsemble(B)
trajectory = engine.generate(engine.current_snapshot, [init_traj_ensemble])
Those two lines are the entirety of what you need to do to generate a
trajectory that visits both states.
Note that this functionality has been implemented in OPS as the
:class:`.VisitAllStatesEnsemble`. When using that, you *should* use the
``can_append`` method. That ensemble adds progress reporting functionality
to show how many frames have been run and what states have been found so
far, so it is more pleasant to use. However, this idea is what underlies its
implementation.