Degrees of freedom (DOFs)
+++++++++++++++++++++++++

Continuous degrees of freedom
-----------------------------

A degree of freedom is a variable that affects our optimization objective. We can define a simple DOF as

.. code-block:: python

    from blop import DOF

    dof = DOF(name="x1", description="my first DOF", search_domain=(lower, upper))

This will instantiate a bunch of stuff under the hood, so that our agent knows how to move things and where to search.
Typically, this will correspond to a real, physical device available in Python. In that case, we can pass the DOF an ophyd device in place of a name

.. code-block:: python

    from blop import DOF

    dof = DOF(device=my_ophyd_device, description="a real piece of hardware", search_domain=(lower, upper))

In this case, the agent will control the device as it sees fit, moving it between the search bounds.

Sometimes, a DOF may be something we can't directly control (e.g. a changing synchrotron current or a changing sample temperature) but want our agent to be aware of.
In this case, we can define a read-only DOF as

.. code-block:: python

    from blop import DOF

    dof = DOF(device=a_read_only_ophyd_device, description="a thermometer or something", read_only=True, trust_domain=(lower, upper))

and the agent will use the received values to model its objective, but won't try to move it.
We can also pass a set of ``trust_domain``, so that our agent will ignore experiments where the DOF value jumps outside of the interval.


Discrete degrees of freedom
---------------------------

In addition to degrees of freedom that vary continuously between a lower and upper bound, we can define discrete degrees of freedom.
One kind is a binary degree of freedom, where the input can take one of two values, e.g.

.. code-block:: python

    discrete_dof = DOF(name="x1", description="A discrete DOF", type="discrete", search_domain={"in", "out"})

Another is an ordinal degree of freedom, which takes more than two discrete values but has some ordering, e.g.

.. code-block:: python

    ordinal_dof = DOF(name="x1", description="An ordinal DOF", type="ordinal", search_domain={"low", "medium", "high"})

The last is a categorical degree of freedom, which can take many different discrete values with no ordering, e.g.

.. code-block:: python

    categorical_dof = DOF(name="x1", description="A categorical DOF", type="categorical", search_domain={"banana", "mango", "papaya"})


API Reference
-------------

.. autoclass:: blop.dofs.DOF
   :members:
   :undoc-members:
   :show-inheritance:
   :inherited-members:
   :no-index:
