|
29 | 29 | namespace mujoco {
|
30 | 30 | namespace {
|
31 | 31 |
|
| 32 | +using ::std::vector; |
| 33 | +using ::testing::ContainsRegex; // NOLINT |
32 | 34 | using ::testing::DoubleNear;
|
| 35 | +using ::testing::ElementsAreArray; |
33 | 36 | using ::testing::Eq;
|
34 |
| -using ::testing::ContainsRegex; // NOLINT |
35 | 37 | using ::testing::MatchesRegex;
|
36 |
| -using ::testing::Pointwise; |
37 |
| -using ::testing::ElementsAreArray; |
| 38 | +using ::testing::NotNull; |
38 | 39 | using ::testing::Pointwise;
|
39 | 40 |
|
40 | 41 | using AngMomMatTest = MujocoTest;
|
@@ -685,9 +686,9 @@ TEST_F(SupportTest, GetSetStateStepEqual) {
|
685 | 686 | mj_deleteModel(model);
|
686 | 687 | }
|
687 | 688 |
|
688 |
| -using AddMTest = MujocoTest; |
| 689 | +using InertiaTest = MujocoTest; |
689 | 690 |
|
690 |
| -TEST_F(AddMTest, DenseSameAsSparse) { |
| 691 | +TEST_F(InertiaTest, DenseSameAsSparse) { |
691 | 692 | mjModel* m = LoadModelFromPath("humanoid/humanoid100.xml");
|
692 | 693 | mjData* d = mj_makeData(m);
|
693 | 694 | int nv = m->nv;
|
@@ -732,6 +733,72 @@ TEST_F(AddMTest, DenseSameAsSparse) {
|
732 | 733 | mj_deleteModel(m);
|
733 | 734 | }
|
734 | 735 |
|
| 736 | +static const char* const kInertiaPath = "engine/testdata/inertia.xml"; |
| 737 | + |
| 738 | +TEST_F(InertiaTest, mulM) { |
| 739 | + const std::string xml_path = GetTestDataFilePath(kInertiaPath); |
| 740 | + char error[1024]; |
| 741 | + mjModel* model = mj_loadXML(xml_path.c_str(), nullptr, error, sizeof(error)); |
| 742 | + ASSERT_THAT(model, NotNull()) << "Failed to load model: " << error; |
| 743 | + int nv = model->nv; |
| 744 | + |
| 745 | + mjData* data = mj_makeData(model); |
| 746 | + mj_forward(model, data); |
| 747 | + |
| 748 | + // dense M matrix |
| 749 | + vector<mjtNum> Mdense(nv*nv); |
| 750 | + mj_fullM(model, Mdense.data(), data->qM); |
| 751 | + |
| 752 | + // arbitrary RHS vector |
| 753 | + vector<mjtNum> vec(nv); |
| 754 | + for (int i=0; i < nv; i++) vec[i] = vec[i] = 20 + 30*i; |
| 755 | + |
| 756 | + // multiply directly |
| 757 | + vector<mjtNum> res1(nv, 0); |
| 758 | + mju_mulMatVec(res1.data(), Mdense.data(), vec.data(), nv, nv); |
| 759 | + |
| 760 | + // multiply with mj_mulM |
| 761 | + vector<mjtNum> res2(nv, 0); |
| 762 | + mj_mulM(model, data, res2.data(), vec.data()); |
| 763 | + |
| 764 | + // expect vectors to match to floating point precision |
| 765 | + EXPECT_THAT(res1, Pointwise(DoubleNear(1e-10), res2)); |
| 766 | + |
| 767 | + mj_deleteData(data); |
| 768 | + mj_deleteModel(model); |
| 769 | +} |
| 770 | + |
| 771 | +TEST_F(InertiaTest, mulM2) { |
| 772 | + const std::string xml_path = GetTestDataFilePath(kInertiaPath); |
| 773 | + char error[1024]; |
| 774 | + mjModel* model = mj_loadXML(xml_path.c_str(), nullptr, error, sizeof(error)); |
| 775 | + ASSERT_THAT(model, NotNull()) << "Failed to load model: " << error; |
| 776 | + int nv = model->nv; |
| 777 | + |
| 778 | + mjData* data = mj_makeData(model); |
| 779 | + mj_forward(model, data); |
| 780 | + |
| 781 | + // arbitrary RHS vector |
| 782 | + vector<mjtNum> vec(nv); |
| 783 | + for (int i=0; i < nv; i++) vec[i] = .2 + .3*i; |
| 784 | + |
| 785 | + // multiply sqrtMvec = M^1/2 * vec |
| 786 | + vector<mjtNum> sqrtMvec(nv); |
| 787 | + mj_mulM2(model, data, sqrtMvec.data(), vec.data()); |
| 788 | + |
| 789 | + // multiply Mvec = M * vec |
| 790 | + vector<mjtNum> Mvec(nv); |
| 791 | + mj_mulM(model, data, Mvec.data(), vec.data()); |
| 792 | + |
| 793 | + // compute vec' * M * vec in two different ways, expect them to match |
| 794 | + mjtNum sqrtMvec2 = mju_dot(sqrtMvec.data(), sqrtMvec.data(), nv); |
| 795 | + mjtNum vecMvec = mju_dot(vec.data(), Mvec.data(), nv); |
| 796 | + EXPECT_FLOAT_EQ(sqrtMvec2, vecMvec); |
| 797 | + |
| 798 | + mj_deleteData(data); |
| 799 | + mj_deleteModel(model); |
| 800 | +} |
| 801 | + |
735 | 802 | static const char* const kIlslandEfcPath =
|
736 | 803 | "engine/testdata/island/island_efc.xml";
|
737 | 804 |
|
|
0 commit comments