Skip to content

Commit 7e9ac58

Browse files
yuvaltassacopybara-github
authored andcommitted
Migrate mjd_inverseFD mass Jacobian from qM to M
PiperOrigin-RevId: 942268237 Change-Id: I0ecfe161867ce9930cd6366d778077df2cd3197f
1 parent 4b34545 commit 7e9ac58

12 files changed

Lines changed: 24 additions & 21 deletions

File tree

doc/APIreference/functions.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3198,7 +3198,7 @@ using finite-differencing. These matrices and their dimensions are:
31983198
``DsDq``, :math:`\partial s / \partial q`, ``nv x nsensordata``
31993199
``DsDv``, :math:`\partial s / \partial v`, ``nv x nsensordata``
32003200
``DsDa``, :math:`\partial s / \partial a`, ``nv x nsensordata``
3201-
``DmDq``, :math:`\partial M / \partial q`, ``nv x nM``
3201+
``DmDq``, :math:`\partial M / \partial q`, ``nv x nC``
32023202

32033203
- All outputs are optional (can be NULL).
32043204
- All outputs are transposed relative to Control Theory convention (i.e., column major).

doc/APIreference/functions_override.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -874,7 +874,7 @@ using finite-differencing. These matrices and their dimensions are:
874874
``DsDq``, :math:`\partial s / \partial q`, ``nv x nsensordata``
875875
``DsDv``, :math:`\partial s / \partial v`, ``nv x nsensordata``
876876
``DsDa``, :math:`\partial s / \partial a`, ``nv x nsensordata``
877-
``DmDq``, :math:`\partial M / \partial q`, ``nv x nM``
877+
``DmDq``, :math:`\partial M / \partial q`, ``nv x nC``
878878

879879
- All outputs are optional (can be NULL).
880880
- All outputs are transposed relative to Control Theory convention (i.e., column major).

doc/changelog.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ General
2222
:class: attention
2323

2424
- Return type of :ref:`mj_encode` and the :ref:`mjfEncode` callback changed from ``int`` to ``mjtSize`` (64-bit).
25+
- Switched :ref:`mjd_inverseFD` to use the CSR-format ``mjData.M`` representation instead of the legacy ``mjData.qM``
26+
for the mass matrix derivative. This changes the shape of the ``DmDq`` parameter from ``(nv x nM)`` to
27+
``(nv x nC)``.
2528

2629
Version 3.10.0 (June 22, 2026)
2730
------------------------------

include/mujoco/mujoco.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1483,10 +1483,10 @@ MJAPI void mjd_transitionFD(const mjModel* m, mjData* d, mjtNum eps, mjtBool flg
14831483
// DsDq: (nv x nsensordata)
14841484
// DsDv: (nv x nsensordata)
14851485
// DsDa: (nv x nsensordata)
1486-
// DmDq: (nv x nM)
1486+
// DmDq: (nv x nC)
14871487
// single-letter shortcuts:
14881488
// inputs: q=qpos, v=qvel, a=qacc
1489-
// outputs: f=qfrc_inverse, s=sensordata, m=qM
1489+
// outputs: f=qfrc_inverse, s=sensordata, m=M
14901490
// notes:
14911491
// optionally computes mass matrix Jacobian DmDq
14921492
// flg_actuation specifies whether to subtract qfrc_actuator from qfrc_inverse

python/mujoco/bindings_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1508,7 +1508,7 @@ def test_inverse_fd(self):
15081508
ds_dq = np.zeros((self.model.nv, self.model.nsensordata), dtype=DTYPE)
15091509
ds_dv = np.zeros((self.model.nv, self.model.nsensordata), dtype=DTYPE)
15101510
ds_da = np.zeros((self.model.nv, self.model.nsensordata), dtype=DTYPE)
1511-
dm_dq = np.zeros((self.model.nv, self.model.nM), dtype=DTYPE)
1511+
dm_dq = np.zeros((self.model.nv, self.model.nC), dtype=DTYPE)
15121512
mujoco.mjd_inverseFD(
15131513
self.model,
15141514
self.data,

python/mujoco/functions.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1640,8 +1640,8 @@ PYBIND11_MODULE(_functions, pymodule, pybind11::mod_gil_not_used()) {
16401640
throw py::type_error("DsDa should be of shape (nv, nsensordata)");
16411641
}
16421642
if (DmDq.has_value() &&
1643-
(DmDq->rows() != m->nv || DmDq->cols() != m->nM)) {
1644-
throw py::type_error("DmDq should be of shape (nv, nM)");
1643+
(DmDq->rows() != m->nv || DmDq->cols() != m->nC)) {
1644+
throw py::type_error("DmDq should be of shape (nv, nC)");
16451645
}
16461646
return InterceptMjErrors(::mjd_inverseFD)(
16471647
m, d, eps, flg_actuation,

python/mujoco/introspect/functions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9533,7 +9533,7 @@
95339533
nullable=True,
95349534
),
95359535
),
9536-
doc='Finite differenced Jacobians of (force, sensors) = mj_inverse(state, acceleration) All outputs are optional. Output dimensions (transposed w.r.t Control Theory convention): DfDq: (nv x nv) DfDv: (nv x nv) DfDa: (nv x nv) DsDq: (nv x nsensordata) DsDv: (nv x nsensordata) DsDa: (nv x nsensordata) DmDq: (nv x nM) single-letter shortcuts: inputs: q=qpos, v=qvel, a=qacc outputs: f=qfrc_inverse, s=sensordata, m=qM notes: optionally computes mass matrix Jacobian DmDq flg_actuation specifies whether to subtract qfrc_actuator from qfrc_inverse', # pylint: disable=line-too-long
9536+
doc='Finite differenced Jacobians of (force, sensors) = mj_inverse(state, acceleration) All outputs are optional. Output dimensions (transposed w.r.t Control Theory convention): DfDq: (nv x nv) DfDv: (nv x nv) DfDa: (nv x nv) DsDq: (nv x nsensordata) DsDv: (nv x nsensordata) DsDa: (nv x nsensordata) DmDq: (nv x nC) single-letter shortcuts: inputs: q=qpos, v=qvel, a=qacc outputs: f=qfrc_inverse, s=sensordata, m=M notes: optionally computes mass matrix Jacobian DmDq flg_actuation specifies whether to subtract qfrc_actuator from qfrc_inverse', # pylint: disable=line-too-long
95379537
)),
95389538
('mjd_subQuat',
95399539
FunctionDecl(

src/engine/engine_derivative_fd.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -598,18 +598,18 @@ void mjd_transitionFD(const mjModel* m, mjData* d, mjtNum eps, mjtBool flg_cente
598598
// DsDq: (nv x nsensordata)
599599
// DsDv: (nv x nsensordata)
600600
// DsDa: (nv x nsensordata)
601-
// DmDq: (nv x nM)
601+
// DmDq: (nv x nC)
602602
// single-letter shortcuts:
603603
// inputs: q=qpos, v=qvel, a=qacc
604-
// outputs: f=qfrc_inverse, s=sensordata, m=qM
604+
// outputs: f=qfrc_inverse, s=sensordata, m=M
605605
// notes:
606606
// optionally compute mass matrix Jacobian DmDq
607607
// flg_actuation specifies whether to subtract qfrc_actuator from qfrc_inverse
608608
void mjd_inverseFD(const mjModel* m, mjData* d, mjtNum eps, mjtBool flg_actuation,
609609
mjtNum *DfDq, mjtNum *DfDv, mjtNum *DfDa,
610610
mjtNum *DsDq, mjtNum *DsDv, mjtNum *DsDa,
611611
mjtNum *DmDq) {
612-
int nq = m->nq, nv = m->nv, nM = m->nM, ns = m->nsensordata;
612+
int nq = m->nq, nv = m->nv, nC = m->nC, ns = m->nsensordata;
613613

614614
if (m->opt.integrator == mjINT_RK4) {
615615
mjERROR("RK4 integrator is not supported");
@@ -628,15 +628,15 @@ void mjd_inverseFD(const mjModel* m, mjData* d, mjtNum eps, mjtBool flg_actuatio
628628
mjtNum *force = mjSTACKALLOC(d, nv, mjtNum); // force
629629
mjtNum *force_plus = mjSTACKALLOC(d, nv, mjtNum); // nudged force
630630
mjtNum *sensor = skipsensor ? NULL : mjSTACKALLOC(d, ns, mjtNum); // sensor values
631-
mjtNum *mass = DmDq ? mjSTACKALLOC(d, nM, mjtNum) : NULL; // mass matrix
631+
mjtNum *mass = DmDq ? mjSTACKALLOC(d, nC, mjtNum) : NULL; // mass matrix
632632

633633
// save current positions
634634
mju_copy(pos, d->qpos, nq);
635635

636636
// center point outputs
637637
inverseSkip(m, d, mjSTAGE_NONE, skipsensor, flg_actuation, force);
638638
if (sensor) mju_copy(sensor, d->sensordata, ns);
639-
if (mass) mju_copy(mass, d->qM, nM);
639+
if (mass) mju_copy(mass, d->M, nC);
640640

641641
// acceleration: skip = mjSTAGE_VEL
642642
if (DfDa || DsDa) {
@@ -702,7 +702,7 @@ void mjd_inverseFD(const mjModel* m, mjData* d, mjtNum eps, mjtBool flg_actuatio
702702
if (DsDq) diff(DsDq + i*ns, sensor, d->sensordata, eps, ns);
703703

704704
// row of inertia Jacobian
705-
if (DmDq) diff(DmDq + i*nM, mass, d->qM, eps, nM);
705+
if (DmDq) diff(DmDq + i*nC, mass, d->M, eps, nC);
706706
}
707707
}
708708

test/engine/engine_derivative_test.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -785,15 +785,15 @@ TEST_F(DerivativeTest, LinearSystemInverse) {
785785

786786
int nv = model->nv;
787787
int ns = model->nsensordata;
788-
int nM = model->nM;
788+
int nC = model->nC;
789789

790790
vector<mjtNum> DfDq(nv * nv);
791791
vector<mjtNum> DfDv(nv * nv);
792792
vector<mjtNum> DfDa(nv * nv);
793793
vector<mjtNum> DsDq(nv * ns);
794794
vector<mjtNum> DsDv(nv * ns);
795795
vector<mjtNum> DsDa(nv * ns);
796-
vector<mjtNum> DmDq(nv * nM);
796+
vector<mjtNum> DmDq(nv * nC);
797797

798798
// call mj_forward to get accelerations at initial state
799799
mj_forward(model, data);
@@ -847,7 +847,7 @@ TEST_F(DerivativeTest, LinearSystemInverse) {
847847
EXPECT_THAT(DsDa, Pointwise(DoubleNear(eps), DsDa_expect));
848848

849849
// expect that mass matrix derivatives are zero
850-
vector<mjtNum> DmDq_expect(nv * nM, 0);
850+
vector<mjtNum> DmDq_expect(nv * nC, 0);
851851
EXPECT_THAT(DmDq, Pointwise(DoubleNear(eps), DmDq_expect));
852852

853853
mj_deleteData(data);

wasm/codegen/generated/bindings.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2057,7 +2057,7 @@ void mjd_inverseFD_wrapper(const MjModel& m, MjData& d, mjtNum eps, mjtBool flg_
20572057
CHECK_SIZE(DsDq, m.nv() * m.nsensordata());
20582058
CHECK_SIZE(DsDv, m.nv() * m.nsensordata());
20592059
CHECK_SIZE(DsDa, m.nv() * m.nsensordata());
2060-
CHECK_SIZE(DmDq, m.nv() * m.nM());
2060+
CHECK_SIZE(DmDq, m.nv() * m.nC());
20612061
mjd_inverseFD(m.get(), d.get(), eps, flg_actuation, DfDq_.data(), DfDv_.data(), DfDa_.data(), DsDq_.data(), DsDv_.data(), DsDa_.data(), DmDq_.data());
20622062
}
20632063

0 commit comments

Comments
 (0)