Move CompareModel to fixture.
PiperOrigin-RevId: 612759913 Change-Id: Ia5741a858003211c58c47a20d447dcbc041c8b5d
This commit is contained in:
committed by
Copybara-Service
parent
e142bca7f2
commit
9efa4ebe0b
@@ -20,10 +20,12 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <limits>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -34,6 +36,7 @@
|
|||||||
#include <absl/strings/str_join.h>
|
#include <absl/strings/str_join.h>
|
||||||
#include <absl/synchronization/mutex.h>
|
#include <absl/synchronization/mutex.h>
|
||||||
#include <mujoco/mjmodel.h>
|
#include <mujoco/mjmodel.h>
|
||||||
|
#include <mujoco/mjxmacro.h>
|
||||||
#include <mujoco/mujoco.h>
|
#include <mujoco/mujoco.h>
|
||||||
|
|
||||||
namespace mujoco {
|
namespace mujoco {
|
||||||
@@ -172,6 +175,71 @@ std::vector<mjtNum> GetCtrlNoise(const mjModel* m, int nsteps,
|
|||||||
return ctrl;
|
return ctrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 (mju_abs(val1) <= 1 || mju_abs(val2) <= 1) {
|
||||||
|
// Absolute precision for small numbers
|
||||||
|
error = mju_abs(val1-val2);
|
||||||
|
} else {
|
||||||
|
// Relative precision for larger numbers
|
||||||
|
ReturnType magnitude = mju_abs(val1) + mju_abs(val2);
|
||||||
|
error = mju_abs(val1/magnitude - val2/magnitude) / magnitude;
|
||||||
|
}
|
||||||
|
ReturnType safety_factor = 10;
|
||||||
|
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
|
||||||
|
#define X(name) \
|
||||||
|
if (m1->name != m2->name) {maxdif = 1.0; field = #name;}
|
||||||
|
MJMODEL_INTS
|
||||||
|
#undef X
|
||||||
|
if (maxdif > 0) return maxdif;
|
||||||
|
|
||||||
|
// compare arrays
|
||||||
|
#define X(type, name, nr, nc) \
|
||||||
|
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;} }
|
||||||
|
MJMODEL_POINTERS
|
||||||
|
#undef X
|
||||||
|
|
||||||
|
// compare scalars in mjOption
|
||||||
|
#define X(type, name) \
|
||||||
|
dif = Compare(m1->opt.name, m2->opt.name); \
|
||||||
|
if (dif > maxdif) {maxdif = dif; field = #name;}
|
||||||
|
MJOPTION_SCALARS
|
||||||
|
#undef X
|
||||||
|
|
||||||
|
// compare arrays in mjOption
|
||||||
|
#define X(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_VECTORS
|
||||||
|
#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);
|
||||||
dir_ = "/";
|
dir_ = "/";
|
||||||
|
|||||||
@@ -101,6 +101,10 @@ std::string SaveAndReadXml(const mjModel* model);
|
|||||||
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);
|
||||||
|
|
||||||
// Installs a mock filesystem via a resource provider. To obtain thread safety,
|
// Installs a mock filesystem via a resource provider. To obtain thread safety,
|
||||||
// each filesystem is scoped for individual unit tests with destructive
|
// each filesystem is scoped for individual unit tests with destructive
|
||||||
// operations not permitted.
|
// operations not permitted.
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
|
|
||||||
// Tests for xml/xml_native_writer.cc.
|
// Tests for xml/xml_native_writer.cc.
|
||||||
|
|
||||||
#include <type_traits>
|
|
||||||
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
|
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -31,7 +30,6 @@
|
|||||||
#include <absl/strings/match.h>
|
#include <absl/strings/match.h>
|
||||||
#include <mujoco/mjmodel.h>
|
#include <mujoco/mjmodel.h>
|
||||||
#include <mujoco/mjtnum.h>
|
#include <mujoco/mjtnum.h>
|
||||||
#include <mujoco/mjxmacro.h>
|
|
||||||
#include <mujoco/mujoco.h>
|
#include <mujoco/mujoco.h>
|
||||||
#include "src/cc/array_safety.h"
|
#include "src/cc/array_safety.h"
|
||||||
#include "src/xml/xml_numeric_format.h"
|
#include "src/xml/xml_numeric_format.h"
|
||||||
@@ -1156,74 +1154,6 @@ TEST_F(XMLWriterLocaleTest, IgnoresLocale) {
|
|||||||
|
|
||||||
|
|
||||||
// ------------------- test loading and saving multiple files ------------------
|
// ------------------- test loading and saving multiple files ------------------
|
||||||
namespace mju = ::mujoco::util;
|
|
||||||
static constexpr int kFieldSize = 500;
|
|
||||||
|
|
||||||
// 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 (mju_abs(val1) <= 1 || mju_abs(val2) <= 1) {
|
|
||||||
// Absolute precision for small numbers
|
|
||||||
error = mju_abs(val1-val2);
|
|
||||||
} else {
|
|
||||||
// Relative precision for larger numbers
|
|
||||||
ReturnType magnitude = mju_abs(val1) + mju_abs(val2);
|
|
||||||
error = mju_abs(val1/magnitude - val2/magnitude) / magnitude;
|
|
||||||
}
|
|
||||||
ReturnType safety_factor = 10;
|
|
||||||
return error < safety_factor * std::numeric_limits<ReturnType>::epsilon()
|
|
||||||
? 0
|
|
||||||
: error;
|
|
||||||
}
|
|
||||||
|
|
||||||
mjtNum CompareModel(const mjModel* m1, const mjModel* m2,
|
|
||||||
char (&field)[kFieldSize]) {
|
|
||||||
mjtNum dif, maxdif = 0.0;
|
|
||||||
|
|
||||||
// define symbols corresponding to number of columns
|
|
||||||
// (needed in MJMODEL_POINTERS)
|
|
||||||
MJMODEL_POINTERS_PREAMBLE(m1);
|
|
||||||
|
|
||||||
// compare ints
|
|
||||||
#define X(name) \
|
|
||||||
if (m1->name != m2->name) {maxdif = 1.0; mju::strcpy_arr(field, #name);}
|
|
||||||
MJMODEL_INTS
|
|
||||||
#undef X
|
|
||||||
if (maxdif > 0) return maxdif;
|
|
||||||
|
|
||||||
// compare arrays
|
|
||||||
#define X(type, name, nr, nc) \
|
|
||||||
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; mju::strcpy_arr(field, #name);} }
|
|
||||||
MJMODEL_POINTERS
|
|
||||||
#undef X
|
|
||||||
|
|
||||||
// compare scalars in mjOption
|
|
||||||
#define X(type, name) \
|
|
||||||
dif = Compare(m1->opt.name, m2->opt.name); \
|
|
||||||
if (dif > maxdif) {maxdif = dif; mju::strcpy_arr(field, #name);}
|
|
||||||
MJOPTION_SCALARS
|
|
||||||
#undef X
|
|
||||||
|
|
||||||
// compare arrays in mjOption
|
|
||||||
#define X(name, n) \
|
|
||||||
for (int c=0; c < n; c++) { \
|
|
||||||
dif = Compare(m1->opt.name[c], m2->opt.name[c]); \
|
|
||||||
if (dif > maxdif) {maxdif = dif; mju::strcpy_arr(field, #name);} }
|
|
||||||
MJOPTION_VECTORS
|
|
||||||
#undef X
|
|
||||||
|
|
||||||
// Return largest difference and field name
|
|
||||||
return maxdif;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(XMLWriterTest, WriteReadCompare) {
|
TEST_F(XMLWriterTest, WriteReadCompare) {
|
||||||
// full precision float printing
|
// full precision float printing
|
||||||
FullFloatPrecision increase_precision;
|
FullFloatPrecision increase_precision;
|
||||||
@@ -1270,7 +1200,7 @@ TEST_F(XMLWriterTest, WriteReadCompare) {
|
|||||||
absl::StrContains(p.path().string(), "belt.xml") ? 1e-13 : 0;
|
absl::StrContains(p.path().string(), "belt.xml") ? 1e-13 : 0;
|
||||||
|
|
||||||
// compare and delete
|
// compare and delete
|
||||||
char field[kFieldSize] = "";
|
std::string field = "";
|
||||||
mjtNum result = CompareModel(m, mtemp, field);
|
mjtNum result = CompareModel(m, mtemp, field);
|
||||||
EXPECT_LE(result, tol)
|
EXPECT_LE(result, tol)
|
||||||
<< "Loaded and saved models are different!\n"
|
<< "Loaded and saved models are different!\n"
|
||||||
|
|||||||
Reference in New Issue
Block a user