Skip to content

Commit

Permalink
unit test for box constraint input
Browse files Browse the repository at this point in the history
  • Loading branch information
Huangzizhou committed Nov 24, 2023
1 parent 3c01d4d commit ab3c9fc
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 47 deletions.
10 changes: 10 additions & 0 deletions non-linear-solver-spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -275,5 +275,15 @@
"default": -1,
"type": "float",
"doc": "Maximum change of optimization variables in one iteration, only for solvers with box constraints. Negative value to disable this constraint."
},
{
"pointer": "/box_constraints/max_change",
"type": "list",
"doc": "Maximum change of optimization variables in one iteration, only for solvers with box constraints."
},
{
"pointer": "/box_constraints/max_change/*",
"type": "float",
"doc": "Maximum change of every optimization variable in one iteration, only for solvers with box constraints."
}
]
1 change: 1 addition & 0 deletions src/polysolve/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
set(SOURCES
Utils.hpp
Utils.cpp
JSONUtils.hpp
)

source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "Source Files" FILES ${SOURCES})
Expand Down
59 changes: 59 additions & 0 deletions src/polysolve/JSONUtils.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#pragma once

#include "Types.hpp"

namespace nlohmann
{
template <typename T, int nrows, int ncols, int maxdim1, int maxdim2>
struct adl_serializer<Eigen::Matrix<T, nrows, ncols, Eigen::ColMajor, maxdim1, maxdim2>>
{
static void to_json(json &j, const Eigen::Matrix<T, nrows, ncols, Eigen::ColMajor, maxdim1, maxdim2> &matrix)
{
if (matrix.rows() > 1)
{
for (int row = 0; row < matrix.rows(); ++row)
{
json column = json::array();
for (int col = 0; col < matrix.cols(); ++col)
{
column.push_back(matrix(row, col));
}
j.push_back(column);
}
}
else if (matrix.rows() == 1)
{
j = json::array();
for (int col = 0; col < matrix.cols(); ++col)
{
j.push_back(matrix(0, col));
}
}
}

static void from_json(const json &j, Eigen::Matrix<T, nrows, ncols, Eigen::ColMajor, maxdim1, maxdim2> &matrix)
{
using Scalar = typename Eigen::Matrix<T, nrows, ncols, Eigen::ColMajor, maxdim1, maxdim2>::Scalar;
assert(j.size() > 0);
assert(nrows >= j.size() || nrows == -1);
assert(j.at(0).is_number() || ncols == -1 || ncols >= j.at(0).size());

const int n_cols = j.at(0).is_number() ? 1 : j.at(0).size();
if (nrows == -1 || ncols == -1)
matrix.setZero(j.size(), n_cols);

for (std::size_t row = 0; row < j.size(); ++row)
{
const auto& jrow = j.at(row);
if (jrow.is_number())
matrix(row) = jrow;
else
for (std::size_t col = 0; col < jrow.size(); ++col)
{
const auto& value = jrow.at(col);
matrix(row, col) = value.get<Scalar>();
}
}
}
};
} // namespace nlohmann
48 changes: 2 additions & 46 deletions src/polysolve/nonlinear/BoxConstraintSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,54 +4,10 @@
#include "descent_strategies/box_constraints/MMA.hpp"

#include <jse/jse.h>
#include <polysolve/JSONUtils.hpp>

#include <fstream>

namespace nlohmann
{
template <typename T, int nrows, int ncols, int maxdim1, int maxdim2>
struct adl_serializer<Eigen::Matrix<T, nrows, ncols, Eigen::ColMajor, maxdim1, maxdim2>>
{
static void to_json(json &j, const Eigen::Matrix<T, nrows, ncols, Eigen::ColMajor, maxdim1, maxdim2> &matrix)
{
for (int row = 0; row < matrix.rows(); ++row)
{
json column = json::array();
for (int col = 0; col < matrix.cols(); ++col)
{
column.push_back(matrix(row, col));
}
j.push_back(column);
}
}

static void from_json(const json &j, Eigen::Matrix<T, nrows, ncols, Eigen::ColMajor, maxdim1, maxdim2> &matrix)
{
using Scalar = typename Eigen::Matrix<T, nrows, ncols, Eigen::ColMajor, maxdim1, maxdim2>::Scalar;
assert(j.size() > 0);
assert(nrows >= j.size() || nrows == -1);
assert(j.at(0).is_number() || ncols == -1 || ncols >= j.at(0).size());

const int n_cols = j.at(0).is_number() ? 1 : j.at(0).size();
if (nrows == -1 || ncols == -1)
matrix.setZero(j.size(), n_cols);

for (std::size_t row = 0; row < j.size(); ++row)
{
const auto& jrow = j.at(row);
if (jrow.is_number())
matrix(row) = jrow;
else
for (std::size_t col = 0; col < jrow.size(); ++col)
{
const auto& value = jrow.at(col);
matrix(row, col) = value.get<Scalar>();
}
}
}
};
} // namespace nlohmann

namespace polysolve::nonlinear
{

Expand Down Expand Up @@ -121,7 +77,7 @@ namespace polysolve::nonlinear
: Superclass(name, solver_params, characteristic_length, logger)
{
json box_constraint_params = solver_params["box_constraints"];
if (box_constraint_params["max_change"] > 0)
if (box_constraint_params["max_change"].is_number())
max_change_val_ = box_constraint_params["max_change"];
else
max_change_ = box_constraint_params["max_change"];
Expand Down
70 changes: 69 additions & 1 deletion tests/test_nonlinear_solver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#include <polysolve/linear/Solver.hpp>

#include <spdlog/sinks/stdout_color_sinks.h>

#include <polysolve/JSONUtils.hpp>
#include <catch2/catch.hpp>

//////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -421,6 +421,74 @@ TEST_CASE("non-linear-box-constraint", "[solver]")
}
}

TEST_CASE("non-linear-box-constraint-input", "[solver]")
{
std::vector<std::unique_ptr<TestProblem>> problems;
problems.push_back(std::make_unique<QuadraticProblem>());

json solver_params, linear_solver_params;
solver_params["box_constraints"] = {};

solver_params["max_iterations"] = 1000;
solver_params["line_search"] = {};

const double characteristic_length = 1;

static std::shared_ptr<spdlog::logger> logger = spdlog::stdout_color_mt("test_logger2");
logger->set_level(spdlog::level::err);

for (auto &prob : problems)
{
for (auto solver_name : BoxConstraintSolver::available_solvers())
{
solver_params["solver"] = solver_name;

for (const auto &ls : line_search::LineSearch::available_methods())
{
if (ls == "None" && solver_name != "MMA")
continue;
if (solver_name == "MMA" && ls != "None")
continue;
solver_params["line_search"]["method"] = ls;

QuadraticProblem::TVector x(prob->size());
x.setConstant(3);

Eigen::MatrixXd bounds(2, x.size());
bounds.row(0).array() = 0;
bounds.row(1).array() = 4;
solver_params["box_constraints"]["bounds"] = bounds;
Eigen::MatrixXd max_change(1, x.size());
max_change.array() = 4;
solver_params["box_constraints"]["max_change"] = 4;

auto solver = BoxConstraintSolver::create(solver_params,
linear_solver_params,
characteristic_length,
*logger);
REQUIRE(solver->name() == solver_name);

try
{
solver->minimize(*prob, x);

INFO("solver: " + solver_params["solver"].get<std::string>() + " LS: " + ls);

Eigen::VectorXd gradv;
prob->gradient(x, gradv);
CHECK(solver->compute_grad_norm(x, gradv) < 1e-7);
}
catch (const std::exception &)
{
// INFO("solver: " + solver_name + " LS: " + ls + " problem " + prob->name());
// CHECK(false);
break;
}
}
}
}
}

TEST_CASE("sample", "[solver]")
{
Rosenbrock rb;
Expand Down

0 comments on commit ab3c9fc

Please sign in to comment.