Delete derivative code sample.
PiperOrigin-RevId: 573477509 Change-Id: I0e619e36dd3fe677a27bbbdac87a15f04670cc13
This commit is contained in:
committed by
Copybara-Service
parent
c0357ef3d0
commit
a1b6026b8c
@@ -177,69 +177,3 @@ proliferation of overlapping technologies, which differ not only between platfor
|
||||
case of Linux. The addition of a couple of extra functions (such as those provided by OSMesa for example) could have
|
||||
avoided a lot of confusion. EGL is a newer standard from Khronos which aims to do this, and it is gaining popularity.
|
||||
But we cannot yet assume that all users have it installed.
|
||||
|
||||
.. _saDerivative:
|
||||
|
||||
`derivative <https://github.com/google-deepmind/mujoco/blob/main/sample/derivative.cc>`_
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This code sample illustrates the numerical approximation of forward and inverse dynamics derivatives via finite
|
||||
differences. The process involves a number of epochs. In each epoch the simulation is advanced for a specified number
|
||||
of steps, derivatives are computed at the last state, and timing and accuracy statistics are collected. The averages
|
||||
over epochs are printed at the end.
|
||||
|
||||
The code can be incorporated in user projects where derivatives are needed, and can also be used as a stand-alone tool
|
||||
for estimating CPU time and numerical accuracy. Accuracy is estimated in the function ``checkderiv()`` using several
|
||||
mathematical identities about the derivatives of inverse functions; the residuals being computed would be zero if the
|
||||
derivatives were exact. Note that these identities involve matrix multiplications which may affect the accuracy
|
||||
estimates. Timing tests are applied only to the parallel section, where the function ``worker()`` is executed in
|
||||
multiple threads using OpenMP. There are fewer threads than forward/inverse dynamics evaluations, thus each thread
|
||||
executes multiple evaluations. For a more general discussion of parallel processing in MuJoCo see
|
||||
:ref:`multi-threading <siMultithread>` below.
|
||||
|
||||
Recall than for a differentiable function ``f(x)`` the derivative can be approximated as
|
||||
|
||||
.. code-block:: Text
|
||||
|
||||
df/dx = (f(x+eps)-f(x))/eps
|
||||
|
||||
where ``eps`` is a small number. One can also use the centered finite difference method, which is two times slower but
|
||||
more accurate. Here ``f`` is one of the functions
|
||||
|
||||
.. code-block:: Text
|
||||
|
||||
forward dynamics: qacc(qfrc_applied, qvel, qpos)
|
||||
inverse dynamics: qfrc_inverse(qacc, qvel, qpos)
|
||||
|
||||
The code sample computes six Jacobian matrices, containing the derivative of each function with respect to its three
|
||||
arguments. The results are stored in the array ``deriv``. All six Jacobian matrices are square, with dimensionality
|
||||
equal to the number of degrees of freedom ``mjModel.nv``. When the model configuration includes quaternion joints,
|
||||
mjData.qpos has larger dimensionality than the other vectors, however the derivative is only defined in the tangent
|
||||
space to the configuration manifold. This is why, when differentiating with respect to the elements of ``mjData.qpos``,
|
||||
we do not directly add ``eps`` but instead use the function :ref:`mju_quatIntegrate` to perturb the quaternion in the
|
||||
tangent space, keeping it normalized. This technique should also be used in any other situation where quaternions need
|
||||
to be perturbed.
|
||||
|
||||
There are some important subtleties in this code that improve speed as well as accuracy. To speed up the computation,
|
||||
we re-use intermediate results whenever possible. This relies on the skip mechanism described under :ref:`forward
|
||||
dynamics <siForward>` and :ref:`inverse dynamics <siInverse>` below. We first perturb force dimensions, keeping
|
||||
position and velocity fixed. In this way we avoid recomputing results that depend on position and velocity but not on
|
||||
force. Then we perturb velocity dimensions, and avoid recomputing results that depend on position but not on velocity
|
||||
or force. Finally we perturb position dimensions - which requires full computation because everything depends on
|
||||
position.
|
||||
|
||||
Accuracy depends on the value of ``eps`` which is user-adjustable, as well as the shape of the function. In the case
|
||||
of forward dynamics however, the function evaluation involves an iterative constraint solver, and this must be handled
|
||||
with care. In general, the difference between ``f(x+eps)`` and ``f(x)`` is very small, thus any noise affecting the
|
||||
two function evaluations differently can make the resulting derivatives meaningless. Different warm-starts or
|
||||
different number of solver iterations can act as such noise here. Therefore we fix the warm-start ``mjData.qacc`` to a
|
||||
value pre-computed at the center point, using ``nwarmup`` extra major iterations to obtain a more accurate warm-start.
|
||||
We also fix the number of solver iterations to ``niter`` and set ``mjModel.opt.tolerance = 0``; this disables the early
|
||||
termination mechanism. Note that the original simulation options are restored in the serial code which advances the
|
||||
state.
|
||||
|
||||
We emphasize that the above subtleties are not high-order corrections that can be incorporated later. In the presence
|
||||
of unilateral constraints, numerical derivatives are hard to compute and there is no shortcut around it; indeed they
|
||||
would not even be defined if it wasn't for our soft-constraint model. Making the constraints softer results in more
|
||||
accurate results. This effect is so strong that in some situations it makes sense to intentionally work with the wrong
|
||||
model, i.e., a model that is softer than desired, so as to obtain more accurate derivatives.
|
||||
|
||||
@@ -428,9 +428,7 @@ However, MuJoCo is designed not only for simulation but also for more advanced a
|
||||
optimization, machine learning etc. In such settings one often needs to sample the dynamics at a cloud of nearby
|
||||
states, or approximate derivatives via finite differences - which is another form of sampling. If the samples are
|
||||
arranged on a grid, where only the position or only the velocity or only the control is different from the center
|
||||
point, then the above mechanism can improve performance by about a factor of 2. The code sample :ref:`derivative.cc
|
||||
<saDerivative>` illustrates this approach, and also shows how :ref:`multi-threading <siMultithread>` can be used for
|
||||
additional speedup.
|
||||
point, then the above mechanism can improve performance by about a factor of 2.
|
||||
|
||||
.. _siInverse:
|
||||
|
||||
@@ -494,10 +492,7 @@ bodies, we may eventually implement within-step multi-threading, but for now thi
|
||||
Rather than speed up a single simulation, we prefer to use multi-threading to speed up sampling operations that are
|
||||
common in more advanced applications. Simulation is inherently serial over time (the output of one mj_step is the
|
||||
input to the next), while in sampling many calls to either forward or inverse dynamics can be executed in parallel
|
||||
since there are no dependencies among them, except perhaps for a common initial state. The code sample
|
||||
:ref:`derivative.cc <saDerivative>` illustrates one important example of sampling, namely the approximation of
|
||||
dynamics derivatives via finite differences. Here we will not repeat the material from that section, but will instead
|
||||
explain MuJoCo's general approach to parallel processing.
|
||||
since there are no dependencies among them, except perhaps for a common initial state.
|
||||
|
||||
MuJoCo was designed for multi-threading from its beginning. Unlike most existing simulators where the notion of
|
||||
dynamical system state is difficult to map to the software state and is often distributed among multiple objects, in
|
||||
@@ -515,7 +510,7 @@ management.
|
||||
|
||||
// allocate per-thread mjData
|
||||
mjData* d[64];
|
||||
for( int n=0; n<nthread; n++ )
|
||||
for( int n=0; n < nthread; n++ )
|
||||
d[n] = mj_makeData(m);
|
||||
|
||||
// ... serial code, perhaps using its own mjData* dmain
|
||||
@@ -541,8 +536,8 @@ writes to its own mjData. Therefore no further synchronization among threads is
|
||||
The above template reflects a particular style of parallel processing. Instead of creating a large number of threads,
|
||||
one for each work item, and letting OpenMP distribute them among processors, we rely on manual scheduling. More
|
||||
precisely, we create as many threads as there are processors, and then within the ``worker`` function we distribute the
|
||||
work explicitly among threads (not shown here, but see :ref:`derivative.cc <saDerivative>` for an example). This
|
||||
approach is more efficient because the thread-specific mjData is large compared to the processor cache.
|
||||
work explicitly among threads. This approach is more efficient because the thread-specific mjData is large compared to
|
||||
the processor cache.
|
||||
|
||||
We also use a shared mjModel for cache-efficiency. In some situations it may not be possible to use the same mjModel
|
||||
for all threads. One obvious reason is that mjModel may need to be modified within the thread function. Another reason
|
||||
|
||||
Reference in New Issue
Block a user