// Copyright 2022 DeepMind Technologies Limited // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include #include #include #include #include #include #include "function_traits.h" #include "functions.h" #include "raw.h" #include #include #include namespace mujoco::python { namespace { PYBIND11_MODULE(_functions, pymodule) { namespace py = ::pybind11; namespace traits = python_traits; using EigenVectorX = Eigen::Vector; using EigenArrayXX = Eigen::Array< mjtNum, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>; // Import the _structs module so that pybind11 knows about Python bindings // for MjWrapper types and therefore generates prettier docstrings. py::module::import("mujoco._structs"); // Activation Def(pymodule); Def(pymodule); // Virtual file system // Skipped entire section // Parse and compile // Skipped: mj_loadXML (have MjModel.from_xml_string) DEF_WITH_OMITTED_PY_ARGS(traits::mj_saveLastXML, "error", "error_sz")( pymodule, [](const char* filename, const mjModel* m) { std::array error; int success = InterceptMjErrors(::mj_saveLastXML)( filename, m, error.data(), error.size()); if (!success) { throw FatalError(std::string(error.data())); } }); // Skipped: mj_freeLastXML DEF_WITH_OMITTED_PY_ARGS(traits::mj_printSchema, "filename", "buffer", "buffer_sz")( pymodule, [](bool flg_html, bool flg_pad) { constexpr int kBufferSize = 27000; auto buffer = std::unique_ptr(new char[kBufferSize]); const int out_length = InterceptMjErrors(::mj_printSchema)( nullptr, buffer.get(), kBufferSize, flg_html, flg_pad); if (out_length >= kBufferSize) { throw UnexpectedError("output buffer too small"); } return std::string(buffer.get(), out_length); }); // Main simulation pymodule.def( "mj_step", InterceptMjErrors( [](const MjModelWrapper& m, MjDataWrapper& d, int nstep) { const raw::MjModel* const m_ptr = m.get(); raw::MjData* const d_ptr = d.get(); for (int i = 0; i < nstep; ++i) { ::mj_step(m_ptr, d_ptr); } }), py::arg("m"), py::arg("d"), py::arg_v("nstep", 1), py::doc((std::string(traits::mj_step::doc) + std::string(" Optionally, repeat nstep times.")).c_str()), py::call_guard()); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); // Initialization Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); // Skipped: mj_copyModel (have MjModel.__copy__, memory managed by MjModel) DEF_WITH_OMITTED_PY_ARGS(traits::mj_saveModel, "buffer_sz")( pymodule, [](const raw::MjModel* m, const std::optional& filename, std::optional< Eigen::Ref>> buffer) { void* buffer_ptr = nullptr; int buffer_sz = 0; if (buffer.has_value()) { buffer_ptr = buffer->data(); buffer_sz = buffer->size(); } return InterceptMjErrors(::mj_saveModel)( m, filename.has_value() ? filename->c_str() : nullptr, buffer_ptr, buffer_sz); }); // Skipped: mj_loadModel (have MjModel.from_binary_path) // Skipped: mj_deleteModel (have MjModel.__del__) Def(pymodule); // Skipped: mj_makeData (have MjData.__init__) // Skipped: mj_copyData (have MjData.__copy__, memory managed by MjData) Def(pymodule); Def(pymodule); Def(pymodule); // Skipped: mj_stackAlloc (doesn't make sense in Python) // Skipped: mj_deleteData (have MjData.__del__) Def(pymodule); Def(pymodule); DEF_WITH_OMITTED_PY_ARGS(traits::mj_setLengthRange, "error", "error_sz")( pymodule, [](raw::MjModel* m, raw::MjData* d, int index, const raw::MjLROpt* opt) { std::array error; int success = InterceptMjErrors(::mj_setLengthRange)( m, d, index, opt, error.data(), error.size()); if (!success) { throw FatalError(std::string(error.data())); } }); // Printing Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); DEF_WITH_OMITTED_PY_ARGS(traits::mju_printMat, "nr", "nc")( pymodule, [](Eigen::Ref mat) { return ::mju_printMat(mat.data(), mat.rows(), mat.cols()); }); DEF_WITH_OMITTED_PY_ARGS(traits::mju_printMatSparse, "nr")( pymodule, [](Eigen::Ref mat, Eigen::Ref> rownnz, Eigen::Ref> rowadr, Eigen::Ref> colind) { if (rownnz.size() != rowadr.size()) { throw py::type_error("size of rownnz should equal size of rowadr"); } const int nnz = rowadr[rowadr.size() - 1] + rownnz[rownnz.size() - 1]; if (mat.size() != nnz) { throw py::type_error( "size of mat should equal rownnz[-1] + rowadr[-1]"); } if (colind.size() != nnz) { throw py::type_error( "size of colind should equal rownnz[-1] + rowadr[-1]"); } return InterceptMjErrors(::mju_printMatSparse)( mat.data(), rowadr.size(), rownnz.data(), rowadr.data(), colind.data()); }); // Components Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); // Sub components Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); DEF_WITH_OMITTED_PY_ARGS(traits::mj_solveM, "n")( pymodule, [](const raw::MjModel* m, raw::MjData* d, Eigen::Ref x, Eigen::Ref y) { if (x.rows() != y.rows()) { throw py::type_error( "the first dimension of x and y should be of the same size"); } if (x.cols() != m->nv) { throw py::type_error( "the last dimension of x should be of size nv"); } if (y.cols() != m->nv) { throw py::type_error( "the last dimension of y should be of size nv"); } return InterceptMjErrors(::mj_solveM)( m, d, x.data(), y.data(), y.rows()); }); DEF_WITH_OMITTED_PY_ARGS(traits::mj_solveM2, "n")( pymodule, [](const raw::MjModel* m, raw::MjData* d, Eigen::Ref x, Eigen::Ref y) { if (x.rows() != y.rows()) { throw py::type_error( "the first dimension of x and y should be of the same size"); } if (x.cols() != m->nv) { throw py::type_error( "the last dimension of x should be of size nv"); } if (y.cols() != m->nv) { throw py::type_error( "the last dimension of y should be of size nv"); } return InterceptMjErrors(::mj_solveM2)( m, d, x.data(), y.data(), y.rows()); }); Def(pymodule); Def(pymodule); Def(pymodule); Def( pymodule, [](const raw::MjModel* m, raw::MjData* d, int flg_acc, Eigen::Ref result) { if (result.size() != m->nv) { throw py::type_error("result should have length nv"); } return InterceptMjErrors(::mj_rne)( m, d, flg_acc, result.data()); }); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def( pymodule, [](const raw::MjModel* m, raw::MjData* d, Eigen::Ref jar, std::optional>> cost, int flg_coneHessian) { if (jar.size() != d->nefc) { throw py::type_error("size of jar should equal nefc"); } return InterceptMjErrors(::mj_constraintUpdate)( m, d, jar.data(), cost.has_value() ? cost->data() : nullptr, flg_coneHessian); }); // Support Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def( pymodule, [](const raw::MjModel* m, raw::MjData* d, Eigen::Ref res, Eigen::Ref vec) { if (res.size() != d->nefc) { throw py::type_error("res should be of length nefc"); } if (vec.size() != m->nv) { throw py::type_error("vec should be of length nv"); } return InterceptMjErrors(::mj_mulJacVec)(m, d, res.data(), vec.data()); }); Def( pymodule, [](const raw::MjModel* m, raw::MjData* d, Eigen::Ref res, Eigen::Ref vec) { if (res.size() != m->nv) { throw py::type_error("res should be of length nv"); } if (vec.size() != d->nefc) { throw py::type_error("vec should be of length nefc"); } return InterceptMjErrors(::mj_mulJacTVec)(m, d, res.data(), vec.data()); }); Def( pymodule, [](const raw::MjModel* m, raw::MjData* d, std::optional> jacp, std::optional> jacr, const mjtNum (*point)[3], int body) { if (jacp.has_value() && (jacp->rows() != 3 || jacp->cols() != m->nv)) { throw py::type_error("jacp should be of shape (3, nv)"); } if (jacr.has_value() && (jacr->rows() != 3 || jacr->cols() != m->nv)) { throw py::type_error("jacr should be of shape (3, nv)"); } return InterceptMjErrors(::mj_jac)( m, d, jacp.has_value() ? jacp->data() : nullptr, jacr.has_value() ? jacr->data() : nullptr, &(*point)[0], body); }); Def( pymodule, [](const raw::MjModel* m, raw::MjData* d, std::optional> jacp, std::optional> jacr, int body) { if (jacp.has_value() && (jacp->rows() != 3 || jacp->cols() != m->nv)) { throw py::type_error("jacp should be of shape (3, nv)"); } if (jacr.has_value() && (jacr->rows() != 3 || jacr->cols() != m->nv)) { throw py::type_error("jacr should be of shape (3, nv)"); } return InterceptMjErrors(::mj_jacBody)( m, d, jacp.has_value() ? jacp->data() : nullptr, jacr.has_value() ? jacr->data() : nullptr, body); }); Def( pymodule, [](const raw::MjModel* m, raw::MjData* d, std::optional> jacp, std::optional> jacr, int body) { if (jacp.has_value() && (jacp->rows() != 3 || jacp->cols() != m->nv)) { throw py::type_error("jacp should be of shape (3, nv)"); } if (jacr.has_value() && (jacr->rows() != 3 || jacr->cols() != m->nv)) { throw py::type_error("jacr should be of shape (3, nv)"); } return InterceptMjErrors(::mj_jacBodyCom)( m, d, jacp.has_value() ? jacp->data() : nullptr, jacr.has_value() ? jacr->data() : nullptr, body); }); Def( pymodule, [](const raw::MjModel* m, raw::MjData* d, std::optional> jacp, std::optional> jacr, int geom) { if (jacp.has_value() && (jacp->rows() != 3 || jacp->cols() != m->nv)) { throw py::type_error("jacp should be of shape (3, nv)"); } if (jacr.has_value() && (jacr->rows() != 3 || jacr->cols() != m->nv)) { throw py::type_error("jacr should be of shape (3, nv)"); } return InterceptMjErrors(::mj_jacGeom)( m, d, jacp.has_value() ? jacp->data() : nullptr, jacr.has_value() ? jacr->data() : nullptr, geom); }); Def( pymodule, [](const raw::MjModel* m, raw::MjData* d, std::optional> jacp, std::optional> jacr, int site) { if (jacp.has_value() && (jacp->rows() != 3 || jacp->cols() != m->nv)) { throw py::type_error("jacp should be of shape (3, nv)"); } if (jacr.has_value() && (jacr->rows() != 3 || jacr->cols() != m->nv)) { throw py::type_error("jacr should be of shape (3, nv)"); } return InterceptMjErrors(::mj_jacSite)( m, d, jacp.has_value() ? jacp->data() : nullptr, jacr.has_value() ? jacr->data() : nullptr, site); }); Def( pymodule, [](const raw::MjModel* m, raw::MjData* d, std::optional> jacp, std::optional> jacr, const mjtNum (*point)[3], const mjtNum (*axis)[3], int body) { if (jacp.has_value() && (jacp->rows() != 3 || jacp->cols() != m->nv)) { throw py::type_error("jacp should be of shape (3, nv)"); } if (jacr.has_value() && (jacr->rows() != 3 || jacr->cols() != m->nv)) { throw py::type_error("jacr should be of shape (3, nv)"); } return InterceptMjErrors(::mj_jacPointAxis)( m, d, jacp.has_value() ? jacp->data() : nullptr, jacr.has_value() ? jacr->data() : nullptr, &(*point)[0], &(*axis)[0], body); }); Def(pymodule); Def(pymodule); Def( pymodule, [](const raw::MjModel* m, Eigen::Ref dst, Eigen::Ref M) { if (M.size() != m->nM) { throw py::type_error("M should be of size nM"); } if (dst.cols() != m->nv || dst.rows() != m->nv) { throw py::type_error("dst should be of shape (nv, nv)"); } return ::mj_fullM(m, dst.data(), M.data()); }); Def( pymodule, [](const raw::MjModel* m, const raw::MjData* d, Eigen::Ref res, Eigen::Ref vec) { if (res.size() != m->nv) { throw py::type_error("res should be of size nv"); } if (vec.size() != m->nv) { throw py::type_error("vec should be of size nv"); } return InterceptMjErrors(::mj_mulM)(m, d, res.data(), vec.data()); }); Def( pymodule, [](const raw::MjModel* m, const raw::MjData* d, Eigen::Ref res, Eigen::Ref vec) { if (res.size() != m->nv) { throw py::type_error("res should be of size nv"); } if (vec.size() != m->nv) { throw py::type_error("vec should be of size nv"); } return InterceptMjErrors(::mj_mulM2)(m, d, res.data(), vec.data()); }); Def( pymodule, [](const raw::MjModel* m, raw::MjData* d, Eigen::Ref dst, Eigen::Ref> rownnz, Eigen::Ref> rowadr, Eigen::Ref> colind) { if (dst.size() != m->nM) { throw py::type_error("dst should be of size nM"); } if (rownnz.size() != m->nv) { throw py::type_error("rownnz should be of size nv"); } if (rowadr.size() != m->nv) { throw py::type_error("rowadr should be of size nv"); } if (colind.size() != m->nM) { throw py::type_error("colind should be of size nM"); } return InterceptMjErrors(::mj_addM)( m, d, dst.data(), rownnz.data(), rowadr.data(), colind.data()); }); Def( pymodule, [](const raw::MjModel* m, raw::MjData* d, const mjtNum (*force)[3], const mjtNum (*torque)[3], const mjtNum (*point)[3], int body, Eigen::Ref qfrc_target) { if (qfrc_target.size() != m->nv) { throw py::type_error("qfrc_target should be of size nv"); } return InterceptMjErrors(::mj_applyFT)( m, d, &(*force)[0], &(*torque)[0], &(*point)[0], body, qfrc_target.data()); }); Def(pymodule); Def(pymodule); Def(pymodule); Def( pymodule, [](const raw::MjModel* m, Eigen::Ref qvel, mjtNum dt, Eigen::Ref qpos1, Eigen::Ref qpos2) { if (qvel.size() != m->nv) { throw py::type_error("qvel should be of size nq"); } if (qpos1.size() != m->nq) { throw py::type_error("qpos1 should be of size nq"); } if (qpos2.size() != m->nq) { throw py::type_error("qpos2 should be of size nq"); } return InterceptMjErrors(::mj_differentiatePos)( m, qvel.data(), dt, qpos1.data(), qpos2.data()); }); Def( pymodule, [](const raw::MjModel* m, Eigen::Ref qpos, Eigen::Ref qvel, mjtNum dt) { if (qpos.size() != m->nq) { throw py::type_error("qpos should be of size nq"); } if (qvel.size() != m->nv) { throw py::type_error("qvel should be of size nq"); } return InterceptMjErrors(::mj_integratePos)( m, qpos.data(), qvel.data(), dt); }); Def( pymodule, [](const raw::MjModel* m, Eigen::Ref qpos) { if (qpos.size() != m->nq) { throw py::type_error("qpos should be of size nq"); } return InterceptMjErrors(::mj_normalizeQuat)(m, qpos.data()); }); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); // Ray collision Def( pymodule, [](const raw::MjModel* m, const raw::MjData* d, const mjtNum(*pnt)[3], const mjtNum(*vec)[3], std::optional>> geomgroup, mjtByte flg_static, int bodyexclude, int(*geomid)[1]) { return mj_ray(m, d, &(*pnt)[0], &(*vec)[0], geomgroup.has_value() ? geomgroup->data() : nullptr, flg_static, bodyexclude, &(*geomid)[0]); }); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); // Interaction Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); // Skipped: mjv_averageCamera (defined in structs.cc due to the return type) Def(pymodule); // Visualization Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); // Skipped: mjv_defaultScene (have MjvScene.__init__, memory managed by // MjvScene). // Skipped: mjv_makeScene (have MjvScene.__init__) // Skipped: mjv_freeScene (have MjvScene.__del__) Def( pymodule, [](const raw::MjModel* m, raw::MjData* d, const raw::MjvOption* opt, const std::optional pert, raw::MjvCamera* cam, int catmask, raw::MjvScene* scn) { const raw::MjvPerturb* pert_ptr = pert.has_value() ? *pert : nullptr; return mjv_updateScene(m, d, opt, pert_ptr, cam, catmask, scn); }); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); // UI framework // Skipped: entire section (can add this if there's demand) // Error and memory // Skipped: everything other than the function below (Python has exceptions) Def(pymodule); // Standard math // This section consists only of preprocessor macros. // Vector math Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); DEF_WITH_OMITTED_PY_ARGS(traits::mju_zero, "n")( pymodule, [](Eigen::Ref res) { return InterceptMjErrors(::mju_zero)(res.data(), res.size()); }); DEF_WITH_OMITTED_PY_ARGS(traits::mju_copy, "n")( pymodule, [](Eigen::Ref res, Eigen::Ref data) { if (res.size() != data.size()) { throw py::type_error("res and data should have the same size"); } return InterceptMjErrors(::mju_copy)( res.data(), data.data(), res.size()); }); DEF_WITH_OMITTED_PY_ARGS(traits::mju_sum, "n")( pymodule, [](Eigen::Ref vec) { return ::mju_sum(vec.data(), vec.size()); }); DEF_WITH_OMITTED_PY_ARGS(traits::mju_L1, "n")( pymodule, [](Eigen::Ref vec) { return InterceptMjErrors(::mju_L1)(vec.data(), vec.size()); }); DEF_WITH_OMITTED_PY_ARGS(traits::mju_scl, "n")( pymodule, [](Eigen::Ref res, Eigen::Ref vec, mjtNum scl) { if (res.size() != vec.size()) { throw py::type_error("res and vec should have the same size"); } return InterceptMjErrors(::mju_scl)( res.data(), vec.data(), scl, res.size()); }); DEF_WITH_OMITTED_PY_ARGS(traits::mju_add, "n")( pymodule, [](Eigen::Ref res, Eigen::Ref vec1, Eigen::Ref vec2) { if (res.size() != vec1.size()) { throw py::type_error("res and vec1 should have the same size"); } if (res.size() != vec2.size()) { throw py::type_error("res and vec2 should have the same size"); } return ::mju_add(res.data(), vec1.data(), vec2.data(), res.size()); }); DEF_WITH_OMITTED_PY_ARGS(traits::mju_sub, "n")( pymodule, [](Eigen::Ref res, Eigen::Ref vec1, Eigen::Ref vec2) { if (res.size() != vec1.size()) { throw py::type_error("res and vec1 should have the same size"); } if (res.size() != vec2.size()) { throw py::type_error("res and vec2 should have the same size"); } return InterceptMjErrors(::mju_sub)( res.data(), vec1.data(), vec2.data(), res.size()); }); DEF_WITH_OMITTED_PY_ARGS(traits::mju_addTo, "n")( pymodule, [](Eigen::Ref res, Eigen::Ref vec) { if (res.size() != vec.size()) { throw py::type_error("res and vec should have the same size"); } return InterceptMjErrors(::mju_addTo)( res.data(), vec.data(), res.size()); }); DEF_WITH_OMITTED_PY_ARGS(traits::mju_subFrom, "n")( pymodule, [](Eigen::Ref res, Eigen::Ref vec) { if (res.size() != vec.size()) { throw py::type_error("res and vec should have the same size"); } return InterceptMjErrors(::mju_subFrom)( res.data(), vec.data(), res.size()); }); DEF_WITH_OMITTED_PY_ARGS(traits::mju_addToScl, "n")( pymodule, [](Eigen::Ref res, Eigen::Ref vec, mjtNum scl) { if (res.size() != vec.size()) { throw py::type_error("res and vec should have the same size"); } return ::mju_addToScl(res.data(), vec.data(), scl, res.size()); }); DEF_WITH_OMITTED_PY_ARGS(traits::mju_addScl, "n")( pymodule, [](Eigen::Ref res, Eigen::Ref vec1, Eigen::Ref vec2, mjtNum scl) { if (res.size() != vec1.size()) { throw py::type_error("res and vec1 should have the same size"); } if (res.size() != vec2.size()) { throw py::type_error("res and vec2 should have the same size"); } return InterceptMjErrors(::mju_addScl)( res.data(), vec1.data(), vec2.data(), scl, res.size()); }); DEF_WITH_OMITTED_PY_ARGS(traits::mju_normalize, "n")( pymodule, [](Eigen::Ref vec) { return InterceptMjErrors(::mju_normalize)(vec.data(), vec.size()); }); DEF_WITH_OMITTED_PY_ARGS(traits::mju_norm, "n")( pymodule, [](Eigen::Ref vec) { return InterceptMjErrors(::mju_norm)(vec.data(), vec.size()); }); DEF_WITH_OMITTED_PY_ARGS(traits::mju_dot, "n")( pymodule, [](Eigen::Ref vec1, Eigen::Ref vec2) { if (vec1.size() != vec2.size()) { throw py::type_error("vec1 and vec2 should have the same size"); } return InterceptMjErrors(::mju_dot)( vec1.data(), vec2.data(), vec1.size()); }); DEF_WITH_OMITTED_PY_ARGS(traits::mju_mulMatVec, "nr", "nc")( pymodule, [](Eigen::Ref res, Eigen::Ref mat, Eigen::Ref vec) { if (res.size() != mat.rows()) { throw py::type_error( "size of res should equal the number of rows in mat"); } if (vec.size() != mat.cols()) { throw py::type_error( "size of vec should equal the number of columns in mat"); } return InterceptMjErrors(::mju_mulMatVec)( res.data(), mat.data(), vec.data(), mat.rows(), mat.cols()); }); DEF_WITH_OMITTED_PY_ARGS(traits::mju_mulMatTVec, "nr", "nc")( pymodule, [](Eigen::Ref res, Eigen::Ref mat, Eigen::Ref vec) { if (res.size() != mat.cols()) { throw py::type_error( "size of res should equal the number of columns in mat"); } if (vec.size() != mat.rows()) { throw py::type_error( "size of vec should equal the number of rows in mat"); } return InterceptMjErrors(::mju_mulMatTVec)( res.data(), mat.data(), vec.data(), mat.rows(), mat.cols()); }); DEF_WITH_OMITTED_PY_ARGS(traits::mju_transpose, "nr", "nc")( pymodule, [](Eigen::Ref res, Eigen::Ref mat) { if (res.cols() != mat.rows()) { throw py::type_error("#columns in res should equal #rows in mat"); } if (res.rows() != mat.cols()) { throw py::type_error("#rows in res should equal #columns in mat"); } return InterceptMjErrors(::mju_transpose)( res.data(), mat.data(), mat.rows(), mat.cols()); }); DEF_WITH_OMITTED_PY_ARGS(traits::mju_mulMatMat, "r1", "c1", "c2")( pymodule, [](Eigen::Ref res, Eigen::Ref mat1, Eigen::Ref mat2) { if (res.rows() != mat1.rows()) { throw py::type_error("#rows in res should equal #rows in mat1"); } if (res.cols() != mat2.cols()) { throw py::type_error( "#columns in res should equal #columns in mat2"); } if (mat1.cols() != mat2.rows()) { throw py::type_error("#columns in mat1 should equal #rows in mat2"); } return InterceptMjErrors(::mju_mulMatMat)( res.data(), mat1.data(), mat2.data(), mat1.rows(), mat1.cols(), mat2.cols()); }); DEF_WITH_OMITTED_PY_ARGS(traits::mju_mulMatMatT, "r1", "c1", "r2")( pymodule, [](Eigen::Ref res, Eigen::Ref mat1, Eigen::Ref mat2) { if (res.rows() != mat1.rows()) { throw py::type_error("#rows in res should equal #rows in mat1"); } if (res.cols() != mat2.rows()) { throw py::type_error("#columns in res should equal #rows in mat2"); } if (mat1.cols() != mat2.cols()) { throw py::type_error( "#columns in mat1 should equal #columns in mat2"); } return InterceptMjErrors(::mju_mulMatMatT)( res.data(), mat1.data(), mat2.data(), mat1.rows(), mat1.cols(), mat2.rows()); }); DEF_WITH_OMITTED_PY_ARGS(traits::mju_mulMatTMat, "r1", "c1", "c2")( pymodule, [](Eigen::Ref res, Eigen::Ref mat1, Eigen::Ref mat2) { if (res.rows() != mat1.cols()) { throw py::type_error("#rows in res should equal #columns in mat1"); } if (res.cols() != mat2.cols()) { throw py::type_error( "#columns in res should equal #columns in mat2"); } if (mat1.rows() != mat2.rows()) { throw py::type_error("#rows in mat1 should equal #rows in mat2"); } return ::mju_mulMatTMat(res.data(), mat1.data(), mat2.data(), mat1.rows(), mat1.cols(), mat2.cols()); }); DEF_WITH_OMITTED_PY_ARGS(traits::mju_sqrMatTD, "nr", "nc")( pymodule, [](Eigen::Ref res, Eigen::Ref mat, std::optional> diag) { if (res.rows() != mat.cols()) { throw py::type_error("#rows in res should equal #columns in mat"); } if (res.cols() != mat.cols()) { throw py::type_error("#rows in res should equal #columns in mat"); } if (diag.has_value() && diag->size() != mat.rows()) { throw py::type_error( "size of diag should equal the number of rows in mat"); } return InterceptMjErrors(::mju_sqrMatTD)( res.data(), mat.data(), diag.has_value() ? diag->data() : nullptr, mat.rows(), mat.cols()); }); Def(pymodule); // Quaternions Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); // Poses Def(pymodule); Def(pymodule); Def(pymodule); // Decompositions DEF_WITH_OMITTED_PY_ARGS(traits::mju_cholFactor, "n")( pymodule, [](Eigen::Ref mat, mjtNum mindiag) { if (mat.rows() != mat.cols()) { throw py::type_error("mat should be a square matrix"); } return InterceptMjErrors(::mju_cholFactor)( mat.data(), mat.rows(), mindiag); }); DEF_WITH_OMITTED_PY_ARGS(traits::mju_cholSolve, "n")( pymodule, [](Eigen::Ref res, Eigen::Ref mat, Eigen::Ref vec) { if (mat.rows() != mat.cols()) { throw py::type_error("mat should be a square matrix"); } if (res.size() != mat.rows()) { throw py::type_error( "size of res should equal the number of rows in mat"); } if (vec.size() != mat.cols()) { throw py::type_error( "size of vec should equal the number of rows in mat"); } return InterceptMjErrors(::mju_cholSolve)( res.data(), mat.data(), vec.data(), mat.rows()); }); DEF_WITH_OMITTED_PY_ARGS(traits::mju_cholUpdate, "n")( pymodule, [](Eigen::Ref mat, Eigen::Ref x, int flg_plus) { if (mat.rows() != mat.cols()) { throw py::type_error("mat should be a square matrix"); } if (x.size() != mat.rows()) { throw py::type_error( "size of x should equal the number of rows in mat"); } return InterceptMjErrors(::mju_cholUpdate)( mat.data(), x.data(), mat.rows(), flg_plus); }); Def(pymodule); // Miscellaneous Def(pymodule); Def(pymodule); Def(pymodule); DEF_WITH_OMITTED_PY_ARGS(traits::mju_encodePyramid, "dim")( pymodule, [](Eigen::Ref pyramid, Eigen::Ref force, Eigen::Ref mu) { if (pyramid.size() != 2*mu.size()) { throw py::type_error( "size of pyramid should be twice as large as size of mu"); } if (force.size() != mu.size() + 1) { throw py::type_error( "size of force should be exactly one larger than size of mu"); } return InterceptMjErrors(::mju_encodePyramid)( pyramid.data(), force.data(), mu.data(), mu.size()); }); DEF_WITH_OMITTED_PY_ARGS(traits::mju_decodePyramid, "dim")( pymodule, [](Eigen::Ref force, Eigen::Ref pyramid, Eigen::Ref mu) { if (pyramid.size() != 2*mu.size()) { throw py::type_error( "size of pyramid should be twice as large as size of mu"); } if (force.size() != mu.size() + 1) { throw py::type_error( "size of force should be exactly one larger than size of mu"); } return InterceptMjErrors(::mju_decodePyramid)( force.data(), pyramid.data(), mu.data(), mu.size()); }); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); Def(pymodule); DEF_WITH_OMITTED_PY_ARGS(traits::mju_isZero, "n")( pymodule, [](Eigen::Ref vec) { return InterceptMjErrors(::mju_isZero)(vec.data(), vec.size()); }); Def( pymodule, [](std::optional num2) { return InterceptMjErrors(::mju_standardNormal)( num2.has_value() ? &*num2 : nullptr); }); DEF_WITH_OMITTED_PY_ARGS(traits::mju_f2n, "n")( pymodule, [](Eigen::Ref res, Eigen::Ref> vec) { if (res.size() != vec.size()) { throw py::type_error("res and vec should have the same size"); } return InterceptMjErrors(::mju_f2n)(res.data(), vec.data(), res.size()); }); DEF_WITH_OMITTED_PY_ARGS(traits::mju_n2f, "n")( pymodule, [](Eigen::Ref> res, Eigen::Ref vec) { if (res.size() != vec.size()) { throw py::type_error("res and vec should have the same size"); } return InterceptMjErrors(::mju_n2f)(res.data(), vec.data(), res.size()); }); DEF_WITH_OMITTED_PY_ARGS(traits::mju_d2n, "n")( pymodule, [](Eigen::Ref res, Eigen::Ref> vec) { if (res.size() != vec.size()) { throw py::type_error("res and vec should have the same size"); } return InterceptMjErrors(::mju_d2n)(res.data(), vec.data(), res.size()); }); DEF_WITH_OMITTED_PY_ARGS(traits::mju_n2d, "n")( pymodule, [](Eigen::Ref> res, Eigen::Ref vec) { if (res.size() != vec.size()) { throw py::type_error("res and vec should have the same size"); } return InterceptMjErrors(::mju_n2d)(res.data(), vec.data(), res.size()); }); DEF_WITH_OMITTED_PY_ARGS(traits::mju_insertionSort, "n")( pymodule, [](Eigen::Ref res) { return InterceptMjErrors(::mju_insertionSort)(res.data(), res.size()); }); DEF_WITH_OMITTED_PY_ARGS(traits::mju_insertionSortInt, "n")( pymodule, [](Eigen::Ref> res) { return InterceptMjErrors(::mju_insertionSortInt)( res.data(), res.size()); }); Def(pymodule); // Skipped: mju_strncpy (doesn't make sense in Python) Def(pymodule); } // PYBIND11_MODULE NOLINT(readability/fn_size) } // namespace } // namespace mujoco::python