Move CompareModel to a separate library.
PiperOrigin-RevId: 927174846 Change-Id: Ia8b8b2c8593133d8910399be755e614282bf3252
This commit is contained in:
committed by
Copybara-Service
parent
5ce9a0ad0b
commit
d80a847bae
@@ -104,6 +104,12 @@ target_link_libraries(
|
|||||||
)
|
)
|
||||||
target_include_directories(fixture PRIVATE ${mujoco_SOURCE_DIR}/include gmock)
|
target_include_directories(fixture PRIVATE ${mujoco_SOURCE_DIR}/include gmock)
|
||||||
|
|
||||||
|
add_library(compare_model STATIC compare_model.h compare_model.cc)
|
||||||
|
target_include_directories(compare_model PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/..)
|
||||||
|
target_compile_definitions(compare_model PUBLIC MJSTATIC)
|
||||||
|
target_link_libraries(compare_model PUBLIC mujoco::mujoco)
|
||||||
|
target_include_directories(compare_model PRIVATE ${mujoco_SOURCE_DIR}/include)
|
||||||
|
|
||||||
mujoco_test(fixture_test)
|
mujoco_test(fixture_test)
|
||||||
|
|
||||||
mujoco_test(header_test)
|
mujoco_test(header_test)
|
||||||
|
|||||||
@@ -0,0 +1,138 @@
|
|||||||
|
// Copyright 2026 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 "test/compare_model.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstring>
|
||||||
|
#include <limits>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include <mujoco/mjmodel.h>
|
||||||
|
#include <mujoco/mjxmacro.h>
|
||||||
|
#include <mujoco/mujoco.h>
|
||||||
|
|
||||||
|
namespace mujoco {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
auto Compare(T val1, T val2);
|
||||||
|
|
||||||
|
auto Compare(char val1, char val2) {
|
||||||
|
return val1 != val2;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Compare(unsigned char val1, unsigned char val2) {
|
||||||
|
return val1 != val2;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Compare(bool val1, bool val2) {
|
||||||
|
return val1 != val2;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Compare(mjtSize val1, mjtSize val2) {
|
||||||
|
return val1 > val2 ? val1 - val2 : val2 - val1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The maximum spacing between a normalised floating point number x and an
|
||||||
|
// adjacent normalised number is 2 epsilon |x|; a factor 10 is added accounting
|
||||||
|
// for losses during non-idempotent operations such as vector normalizations.
|
||||||
|
template <typename T>
|
||||||
|
auto Compare(T val1, T val2) {
|
||||||
|
using ReturnType =
|
||||||
|
std::conditional_t<std::is_same_v<T, float>, float, double>;
|
||||||
|
ReturnType error;
|
||||||
|
if (std::abs(val1) <= 1 || std::abs(val2) <= 1) {
|
||||||
|
// Absolute precision for small numbers
|
||||||
|
error = std::abs(val1-val2);
|
||||||
|
} else {
|
||||||
|
// Relative precision for larger numbers
|
||||||
|
ReturnType magnitude = std::abs(val1) + std::abs(val2);
|
||||||
|
error = std::abs(val1/magnitude - val2/magnitude) / magnitude;
|
||||||
|
}
|
||||||
|
ReturnType safety_factor = 200;
|
||||||
|
return error < safety_factor * std::numeric_limits<ReturnType>::epsilon()
|
||||||
|
? 0
|
||||||
|
: error;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
mjtNum CompareModel(const mjModel* m1, const mjModel* m2,
|
||||||
|
std::string& field) {
|
||||||
|
mjtNum dif, maxdif = 0.0;
|
||||||
|
|
||||||
|
// define symbols corresponding to number of columns
|
||||||
|
// (needed in MJMODEL_POINTERS)
|
||||||
|
MJMODEL_POINTERS_PREAMBLE(m1);
|
||||||
|
|
||||||
|
// compare ints, exclude nbuffer because it hides the actual difference
|
||||||
|
#define X(name) \
|
||||||
|
if constexpr (std::string_view(#name) != "nbuffer") { \
|
||||||
|
if (m1->name != m2->name) { \
|
||||||
|
maxdif = std::abs((long)m1->name - (long)m2->name); \
|
||||||
|
field = #name; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
MJMODEL_SIZES
|
||||||
|
#undef X
|
||||||
|
if (maxdif > 0) return maxdif;
|
||||||
|
|
||||||
|
// compare arrays, apart from bvh-related ones (which includes flex_vert0), as
|
||||||
|
// those are sensitive to numerical differences when meshes are perfectly
|
||||||
|
// symmetric. Also skip flex fields derived from node local positions and
|
||||||
|
// cell geometry that are not fully serialized to XML.
|
||||||
|
#define X(type, name, nr, nc) \
|
||||||
|
if (strncmp(#name, "bvh_", 4) && \
|
||||||
|
strncmp(#name, "flex_vert", 9) && \
|
||||||
|
strncmp(#name, "mesh_poly", 9) && \
|
||||||
|
strcmp(#name, "flex_centered") && \
|
||||||
|
strcmp(#name, "flex_size") && \
|
||||||
|
strcmp(#name, "flexedge_length0") && \
|
||||||
|
strcmp(#name, "flexedge_invweight0") && \
|
||||||
|
strncmp(#name, "flex_node", 9)) { \
|
||||||
|
for (int r = 0; r < m1->nr; r++) { \
|
||||||
|
for (int c = 0; c < nc; c++) { \
|
||||||
|
dif = Compare(m1->name[r * nc + c], m2->name[r * nc + c]); \
|
||||||
|
if (dif > maxdif) { \
|
||||||
|
maxdif = dif; \
|
||||||
|
field = #name; \
|
||||||
|
field += " row: " + std::to_string(r); \
|
||||||
|
field += " col: " + std::to_string(c); \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} // NOLINT
|
||||||
|
MJMODEL_POINTERS
|
||||||
|
#undef X
|
||||||
|
|
||||||
|
// compare fields in mjOption
|
||||||
|
#define X(type, name, n) \
|
||||||
|
dif = Compare(m1->opt.name, m2->opt.name); \
|
||||||
|
if (dif > maxdif) {maxdif = dif; field = #name;}
|
||||||
|
#define XVEC(type, name, n) \
|
||||||
|
for (int c=0; c < n; c++) { \
|
||||||
|
dif = Compare(m1->opt.name[c], m2->opt.name[c]); \
|
||||||
|
if (dif > maxdif) {maxdif = dif; field = #name;} }
|
||||||
|
MJOPTION_FIELDS
|
||||||
|
#undef XVEC
|
||||||
|
#undef X
|
||||||
|
|
||||||
|
// Return largest difference and field name
|
||||||
|
return maxdif;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace mujoco
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
// Copyright 2026 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.
|
||||||
|
|
||||||
|
#ifndef MUJOCO_TEST_COMPARE_MODEL_H_
|
||||||
|
#define MUJOCO_TEST_COMPARE_MODEL_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <mujoco/mujoco.h>
|
||||||
|
|
||||||
|
namespace mujoco {
|
||||||
|
|
||||||
|
// Compares all fields of two mjModels.
|
||||||
|
// Returns the name of the different field and the max difference.
|
||||||
|
mjtNum CompareModel(const mjModel* m1, const mjModel* m2, std::string& field);
|
||||||
|
|
||||||
|
} // namespace mujoco
|
||||||
|
|
||||||
|
#endif // MUJOCO_TEST_COMPARE_MODEL_H_
|
||||||
-104
@@ -192,110 +192,6 @@ std::vector<mjtNum> GetCtrlNoise(const mjModel* m, int nsteps,
|
|||||||
return ctrl;
|
return ctrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
auto Compare(T val1, T val2);
|
|
||||||
|
|
||||||
auto Compare(char val1, char val2) {
|
|
||||||
return val1 != val2;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Compare(unsigned char val1, unsigned char val2) {
|
|
||||||
return val1 != val2;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Compare(bool val1, bool val2) {
|
|
||||||
return val1 != val2;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Compare(mjtSize val1, mjtSize val2) {
|
|
||||||
return val1 > val2 ? val1 - val2 : val2 - val1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The maximum spacing between a normalised floating point number x and an
|
|
||||||
// adjacent normalised number is 2 epsilon |x|; a factor 10 is added accounting
|
|
||||||
// for losses during non-idempotent operations such as vector normalizations.
|
|
||||||
template <typename T>
|
|
||||||
auto Compare(T val1, T val2) {
|
|
||||||
using ReturnType =
|
|
||||||
std::conditional_t<std::is_same_v<T, float>, float, double>;
|
|
||||||
ReturnType error;
|
|
||||||
if (std::abs(val1) <= 1 || std::abs(val2) <= 1) {
|
|
||||||
// Absolute precision for small numbers
|
|
||||||
error = std::abs(val1-val2);
|
|
||||||
} else {
|
|
||||||
// Relative precision for larger numbers
|
|
||||||
ReturnType magnitude = std::abs(val1) + std::abs(val2);
|
|
||||||
error = std::abs(val1/magnitude - val2/magnitude) / magnitude;
|
|
||||||
}
|
|
||||||
ReturnType safety_factor = 200;
|
|
||||||
return error < safety_factor * std::numeric_limits<ReturnType>::epsilon()
|
|
||||||
? 0
|
|
||||||
: error;
|
|
||||||
}
|
|
||||||
|
|
||||||
mjtNum CompareModel(const mjModel* m1, const mjModel* m2,
|
|
||||||
std::string& field) {
|
|
||||||
mjtNum dif, maxdif = 0.0;
|
|
||||||
|
|
||||||
// define symbols corresponding to number of columns
|
|
||||||
// (needed in MJMODEL_POINTERS)
|
|
||||||
MJMODEL_POINTERS_PREAMBLE(m1);
|
|
||||||
|
|
||||||
// compare ints, exclude nbuffer because it hides the actual difference
|
|
||||||
#define X(name) \
|
|
||||||
if constexpr (std::string_view(#name) != "nbuffer") { \
|
|
||||||
if (m1->name != m2->name) { \
|
|
||||||
maxdif = std::abs((long)m1->name - (long)m2->name); \
|
|
||||||
field = #name; \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
MJMODEL_SIZES
|
|
||||||
#undef X
|
|
||||||
if (maxdif > 0) return maxdif;
|
|
||||||
|
|
||||||
// compare arrays, apart from bvh-related ones (which includes flex_vert0), as
|
|
||||||
// those are sensitive to numerical differences when meshes are perfectly
|
|
||||||
// symmetric. Also skip flex fields derived from node local positions and
|
|
||||||
// cell geometry that are not fully serialized to XML.
|
|
||||||
#define X(type, name, nr, nc) \
|
|
||||||
if (strncmp(#name, "bvh_", 4) && \
|
|
||||||
strncmp(#name, "flex_vert", 9) && \
|
|
||||||
strncmp(#name, "mesh_poly", 9) && \
|
|
||||||
strcmp(#name, "flex_centered") && \
|
|
||||||
strcmp(#name, "flex_size") && \
|
|
||||||
strcmp(#name, "flexedge_length0") && \
|
|
||||||
strcmp(#name, "flexedge_invweight0") && \
|
|
||||||
strncmp(#name, "flex_node", 9)) { \
|
|
||||||
for (int r = 0; r < m1->nr; r++) { \
|
|
||||||
for (int c = 0; c < nc; c++) { \
|
|
||||||
dif = Compare(m1->name[r * nc + c], m2->name[r * nc + c]); \
|
|
||||||
if (dif > maxdif) { \
|
|
||||||
maxdif = dif; \
|
|
||||||
field = #name; \
|
|
||||||
field += " row: " + std::to_string(r); \
|
|
||||||
field += " col: " + std::to_string(c); \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
} // NOLINT
|
|
||||||
MJMODEL_POINTERS
|
|
||||||
#undef X
|
|
||||||
|
|
||||||
// compare fields in mjOption
|
|
||||||
#define X(type, name, n) \
|
|
||||||
dif = Compare(m1->opt.name, m2->opt.name); \
|
|
||||||
if (dif > maxdif) {maxdif = dif; field = #name;}
|
|
||||||
#define XVEC(type, name, n) \
|
|
||||||
for (int c=0; c < n; c++) { \
|
|
||||||
dif = Compare(m1->opt.name[c], m2->opt.name[c]); \
|
|
||||||
if (dif > maxdif) {maxdif = dif; field = #name;} }
|
|
||||||
MJOPTION_FIELDS
|
|
||||||
#undef XVEC
|
|
||||||
#undef X
|
|
||||||
|
|
||||||
// Return largest difference and field name
|
|
||||||
return maxdif;
|
|
||||||
}
|
|
||||||
|
|
||||||
MockFilesystem::MockFilesystem(std::string unit_test_name) {
|
MockFilesystem::MockFilesystem(std::string unit_test_name) {
|
||||||
prefix_ = absl::StrCat("MjMock.", unit_test_name);
|
prefix_ = absl::StrCat("MjMock.", unit_test_name);
|
||||||
|
|||||||
@@ -182,9 +182,6 @@ std::string SaveAndReadXml(const mjSpec* spec);
|
|||||||
std::vector<mjtNum> GetCtrlNoise(const mjModel* m, int nsteps,
|
std::vector<mjtNum> GetCtrlNoise(const mjModel* m, int nsteps,
|
||||||
mjtNum ctrlnoise = 0.01);
|
mjtNum ctrlnoise = 0.01);
|
||||||
|
|
||||||
// Compares all fields of two mjModels.
|
|
||||||
// Returns the name of the different field and the max difference.
|
|
||||||
mjtNum CompareModel(const mjModel* m1, const mjModel* m2, std::string& field);
|
|
||||||
|
|
||||||
// Returns a vector containing the elements of the array.
|
// Returns a vector containing the elements of the array.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
|||||||
@@ -14,13 +14,14 @@
|
|||||||
|
|
||||||
mujoco_test(
|
mujoco_test(
|
||||||
user_model_test
|
user_model_test
|
||||||
ADDITIONAL_LINK_LIBRARIES absl::str_format
|
ADDITIONAL_LINK_LIBRARIES absl::str_format compare_model
|
||||||
)
|
)
|
||||||
|
|
||||||
mujoco_test(user_objects_test)
|
mujoco_test(user_objects_test)
|
||||||
|
|
||||||
mujoco_test(
|
mujoco_test(
|
||||||
user_api_test
|
user_api_test
|
||||||
|
ADDITIONAL_LINK_LIBRARIES compare_model
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
ENVIRONMENT
|
ENVIRONMENT
|
||||||
"MUJOCO_PLUGIN_DIR=$<TARGET_FILE_DIR:elasticity>"
|
"MUJOCO_PLUGIN_DIR=$<TARGET_FILE_DIR:elasticity>"
|
||||||
@@ -28,6 +29,7 @@ mujoco_test(
|
|||||||
|
|
||||||
mujoco_test(
|
mujoco_test(
|
||||||
user_recompile_test
|
user_recompile_test
|
||||||
|
ADDITIONAL_LINK_LIBRARIES compare_model
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
ENVIRONMENT
|
ENVIRONMENT
|
||||||
"MUJOCO_PLUGIN_DIR=$<TARGET_FILE_DIR:elasticity>"
|
"MUJOCO_PLUGIN_DIR=$<TARGET_FILE_DIR:elasticity>"
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#include <mujoco/mjspec.h>
|
#include <mujoco/mjspec.h>
|
||||||
#include <mujoco/mjplugin.h>
|
#include <mujoco/mjplugin.h>
|
||||||
#include "src/xml/xml_api.h"
|
#include "src/xml/xml_api.h"
|
||||||
|
#include "test/compare_model.h"
|
||||||
#include "test/fixture.h"
|
#include "test/fixture.h"
|
||||||
|
|
||||||
namespace mujoco {
|
namespace mujoco {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
#include <absl/strings/str_format.h>
|
#include <absl/strings/str_format.h>
|
||||||
#include <mujoco/mjmodel.h>
|
#include <mujoco/mjmodel.h>
|
||||||
#include <mujoco/mujoco.h>
|
#include <mujoco/mujoco.h>
|
||||||
|
#include "test/compare_model.h"
|
||||||
#include "test/fixture.h"
|
#include "test/fixture.h"
|
||||||
|
|
||||||
namespace mujoco {
|
namespace mujoco {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include <mujoco/mjspec.h>
|
#include <mujoco/mjspec.h>
|
||||||
#include "src/xml/xml_api.h"
|
#include "src/xml/xml_api.h"
|
||||||
#include "src/xml/xml_numeric_format.h"
|
#include "src/xml/xml_numeric_format.h"
|
||||||
|
#include "test/compare_model.h"
|
||||||
#include "test/fixture.h"
|
#include "test/fixture.h"
|
||||||
|
|
||||||
namespace mujoco {
|
namespace mujoco {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ mujoco_test(xml_api_test)
|
|||||||
|
|
||||||
mujoco_test(
|
mujoco_test(
|
||||||
xml_native_reader_test
|
xml_native_reader_test
|
||||||
|
ADDITIONAL_LINK_LIBRARIES compare_model
|
||||||
)
|
)
|
||||||
|
|
||||||
mujoco_test(xml_utils_test)
|
mujoco_test(xml_utils_test)
|
||||||
@@ -33,6 +34,7 @@ mujoco_test(
|
|||||||
xml_write_read_test
|
xml_write_read_test
|
||||||
ADDITIONAL_LINK_LIBRARIES
|
ADDITIONAL_LINK_LIBRARIES
|
||||||
absl::flat_hash_set
|
absl::flat_hash_set
|
||||||
|
compare_model
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
ENVIRONMENT
|
ENVIRONMENT
|
||||||
"MUJOCO_PLUGIN_DIR=$<TARGET_FILE_DIR:elasticity>"
|
"MUJOCO_PLUGIN_DIR=$<TARGET_FILE_DIR:elasticity>"
|
||||||
|
|||||||
@@ -20,4 +20,5 @@ mujoco_test(
|
|||||||
|
|
||||||
mujoco_test(
|
mujoco_test(
|
||||||
mjz_encoder_test
|
mjz_encoder_test
|
||||||
|
ADDITIONAL_LINK_LIBRARIES compare_model
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#include <mujoco/mjspec.h>
|
#include <mujoco/mjspec.h>
|
||||||
#include <mujoco/mujoco.h>
|
#include <mujoco/mujoco.h>
|
||||||
#include "src/xml/xml_numeric_format.h"
|
#include "src/xml/xml_numeric_format.h"
|
||||||
|
#include "test/compare_model.h"
|
||||||
#include "test/fixture.h"
|
#include "test/fixture.h"
|
||||||
|
|
||||||
namespace mujoco {
|
namespace mujoco {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#include "src/cc/array_safety.h"
|
#include "src/cc/array_safety.h"
|
||||||
#include "src/engine/engine_util_errmem.h"
|
#include "src/engine/engine_util_errmem.h"
|
||||||
#include "src/xml/xml_api.h"
|
#include "src/xml/xml_api.h"
|
||||||
|
#include "test/compare_model.h"
|
||||||
#include "test/fixture.h"
|
#include "test/fixture.h"
|
||||||
|
|
||||||
namespace mujoco {
|
namespace mujoco {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
#include <mujoco/mjmodel.h>
|
#include <mujoco/mjmodel.h>
|
||||||
#include <mujoco/mujoco.h>
|
#include <mujoco/mujoco.h>
|
||||||
#include "src/xml/xml_numeric_format.h"
|
#include "src/xml/xml_numeric_format.h"
|
||||||
|
#include "test/compare_model.h"
|
||||||
#include "test/fixture.h"
|
#include "test/fixture.h"
|
||||||
|
|
||||||
namespace mujoco {
|
namespace mujoco {
|
||||||
|
|||||||
Reference in New Issue
Block a user