Skip to content

Commit ede72a8

Browse files
committed
First draft of the structure of the dogleg method
1 parent 82bc87d commit ede72a8

File tree

6 files changed

+245
-0
lines changed

6 files changed

+245
-0
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ file(GLOB UNO_SOURCE_FILES
7676
uno/ingredients/inertia_correction_strategies/*.cpp
7777
uno/ingredients/subproblem/*.cpp
7878
uno/ingredients/subproblem_solvers/*.cpp
79+
uno/ingredients/subproblem_solvers/dogleg/*.cpp
7980
uno/model/*.cpp
8081
uno/optimization/*.cpp
8182
uno/options/*.cpp
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright (c) 2025 Charlie Vanaret
2+
// Licensed under the MIT license. See LICENSE file in the project directory for details.
3+
4+
#ifndef UNO_TRUSTREGIONSOLVER_H
5+
#define UNO_TRUSTREGIONSOLVER_H
6+
7+
#include "InequalityConstrainedSolver.hpp"
8+
9+
namespace uno {
10+
// forward declarations
11+
class Direction;
12+
class Statistics;
13+
class Subproblem;
14+
template <typename ElementType>
15+
class Vector;
16+
class WarmstartInformation;
17+
18+
class BoundConstrainedSolver: public InequalityConstrainedSolver {
19+
public:
20+
BoundConstrainedSolver() = default;
21+
~BoundConstrainedSolver() override = default;
22+
23+
void initialize_memory(const Subproblem& subproblem) override = 0;
24+
25+
void solve(Statistics& statistics, Subproblem& subproblem, double trust_region_radius, const Vector<double>& initial_point,
26+
Direction& direction, const WarmstartInformation& warmstart_information) override = 0;
27+
28+
[[nodiscard]] virtual EvaluationSpace& get_evaluation_space() = 0;
29+
};
30+
} // namespace
31+
32+
#endif // UNO_TRUSTREGIONSOLVER_H
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Copyright (c) 2025 Charlie Vanaret
2+
// Licensed under the MIT license. See LICENSE file in the project directory for details.
3+
4+
#include "DoglegEvaluationSpace.hpp"
5+
#include "ingredients/subproblem/Subproblem.hpp"
6+
#include "ingredients/subproblem_solvers/SymmetricIndefiniteLinearSolver.hpp"
7+
#include "optimization/OptimizationProblem.hpp"
8+
#include "optimization/WarmstartInformation.hpp"
9+
10+
namespace uno {
11+
void DoglegEvaluationSpace::initialize_memory(const Subproblem& subproblem) {
12+
// Newton step
13+
this->objective_gradient.resize(subproblem.number_variables);
14+
this->newton_step.resize(subproblem.number_variables);
15+
// Cauchy step
16+
this->hessian_objective_product.resize(subproblem.number_variables);
17+
this->cauchy_step.resize(subproblem.number_variables);
18+
}
19+
20+
void DoglegEvaluationSpace::evaluate_constraint_jacobian(const OptimizationProblem& /*problem*/, Iterate& /*iterate*/) {
21+
// do nothing
22+
}
23+
24+
void DoglegEvaluationSpace::compute_constraint_jacobian_vector_product(const Vector<double>& /*vector*/, Vector<double>& result) const {
25+
result.fill(0.);
26+
}
27+
28+
void DoglegEvaluationSpace::compute_constraint_jacobian_transposed_vector_product(const Vector<double>& /*vector*/,
29+
Vector<double>& result) const {
30+
result.fill(0.);
31+
}
32+
33+
double DoglegEvaluationSpace::compute_hessian_quadratic_product(const Subproblem& /*subproblem*/,
34+
const Vector<double>& /*vector*/) const {
35+
throw std::runtime_error("Not implemented yet");
36+
}
37+
38+
void DoglegEvaluationSpace::compute_newton_step(const Subproblem& subproblem, SymmetricIndefiniteLinearSolver<double>& /*linear_solver*/,
39+
const WarmstartInformation& warmstart_information) {
40+
if (warmstart_information.objective_changed) {
41+
// g = ∇f(x_k)
42+
subproblem.problem.evaluate_objective_gradient(subproblem.current_iterate, this->objective_gradient.data());
43+
// TODO compute Newton step
44+
// linear_solver.solve_indefinite_system(statistics, subproblem, direction, warmstart_information);
45+
this->newton_step_squared_norm = dot(this->newton_step, this->newton_step);
46+
}
47+
}
48+
49+
void DoglegEvaluationSpace::compute_dogleg(const Subproblem& subproblem, Direction& /*direction*/,
50+
const WarmstartInformation& warmstart_information) {
51+
this->compute_cauchy_step(subproblem, warmstart_information);
52+
// TODO
53+
}
54+
55+
// private member functions
56+
57+
void DoglegEvaluationSpace::compute_cauchy_step(const Subproblem& subproblem, const WarmstartInformation& warmstart_information) {
58+
if (warmstart_information.objective_changed) {
59+
// g^T g
60+
this->objective_gradient_squared_norm = dot(this->objective_gradient, this->objective_gradient);
61+
// B g = H_k ∇f(x_k)
62+
subproblem.compute_hessian_vector_product(subproblem.current_iterate.primals.data(), this->objective_gradient.data(),
63+
this->hessian_objective_product.data());
64+
// g^T B g = ∇f(x_k)^T H_k ∇f(x_k)
65+
this->hessian_quadratic_product = dot(this->objective_gradient,
66+
this->hessian_objective_product);
67+
if (this->hessian_quadratic_product <= 0.) {
68+
throw std::runtime_error("The objective Hessian is not positive definite");
69+
}
70+
// Cauchy step: d_C = - (g^T g)/(g^T B g) g
71+
this->cauchy_step = this->objective_gradient;
72+
const double scaling_factor = -this->objective_gradient_squared_norm /
73+
this->hessian_quadratic_product;
74+
this->cauchy_step.scale(scaling_factor);
75+
}
76+
}
77+
78+
// find the positive real root to ax^2 + bx + c = 0
79+
double DoglegEvaluationSpace::compute_positive_root_quadratic_equation(double a, double b, double c) {
80+
// avoid catastrophic cancellation
81+
const double delta = b*b - 4.*a*c;
82+
if (delta < 0.) {
83+
throw std::runtime_error("No real root");
84+
}
85+
// TODO check denominator
86+
return (2.*c)/(-b - std::sqrt(delta));
87+
}
88+
} // namespace
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright (c) 2025 Charlie Vanaret
2+
// Licensed under the MIT license. See LICENSE file in the project directory for details.
3+
4+
#ifndef UNO_DOGLEGEVALUATIONSPACE_H
5+
#define UNO_DOGLEGEVALUATIONSPACE_H
6+
7+
#include "optimization/EvaluationSpace.hpp"
8+
#include "linear_algebra/Vector.hpp"
9+
#include "tools/Infinity.hpp"
10+
11+
namespace uno {
12+
// forward declaration
13+
class Direction;
14+
template <typename ElementType>
15+
class SymmetricIndefiniteLinearSolver;
16+
17+
class DoglegEvaluationSpace: public EvaluationSpace {
18+
public:
19+
DoglegEvaluationSpace() = default;
20+
~DoglegEvaluationSpace() override = default;
21+
22+
// Newton step
23+
Vector<double> objective_gradient{};
24+
Vector<double> newton_step{};
25+
double newton_step_squared_norm{INF<double>};
26+
// Cauchy step
27+
double objective_gradient_squared_norm{INF<double>};
28+
Vector<double> hessian_objective_product{};
29+
double hessian_quadratic_product{INF<double>};
30+
Vector<double> cauchy_step{};
31+
32+
void initialize_memory(const Subproblem& subproblem);
33+
34+
void evaluate_constraint_jacobian(const OptimizationProblem& /*problem*/, Iterate& /*iterate*/) override;
35+
void compute_constraint_jacobian_vector_product(const Vector<double>& /*vector*/, Vector<double>& result) const override;
36+
void compute_constraint_jacobian_transposed_vector_product(const Vector<double>& vector, Vector<double>& result) const override;
37+
[[nodiscard]] double compute_hessian_quadratic_product(const Subproblem& subproblem,
38+
const Vector<double>& vector) const override;
39+
40+
void compute_newton_step(const Subproblem& subproblem, SymmetricIndefiniteLinearSolver<double>& linear_solver,
41+
const WarmstartInformation& warmstart_information);
42+
void compute_dogleg(const Subproblem& subproblem, Direction& direction, const WarmstartInformation& warmstart_information);
43+
44+
private:
45+
void compute_cauchy_step(const Subproblem& subproblem, const WarmstartInformation& warmstart_information);
46+
[[nodiscard]] static double compute_positive_root_quadratic_equation(double a, double b, double c);
47+
};
48+
} // namespace
49+
50+
#endif // UNO_DOGLEGEVALUATIONSPACE_H
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright (c) 2025 Charlie Vanaret
2+
// Licensed under the MIT license. See LICENSE file in the project directory for details.
3+
4+
#include "DoglegMethod.hpp"
5+
#include "ingredients/subproblem/Subproblem.hpp"
6+
#include "ingredients/subproblem_solvers/SymmetricIndefiniteLinearSolverFactory.hpp"
7+
#include "options/Options.hpp"
8+
9+
namespace uno {
10+
DoglegMethod::DoglegMethod(const Options& options):
11+
linear_solver_name(options.get_string("linear_solver")) {
12+
}
13+
14+
void DoglegMethod::initialize_memory(const Subproblem& subproblem) {
15+
this->linear_solver = SymmetricIndefiniteLinearSolverFactory::create(this->linear_solver_name);
16+
this->linear_solver->initialize_hessian(subproblem);
17+
18+
this->evaluation_space.initialize_memory(subproblem);
19+
}
20+
21+
void DoglegMethod::solve(Statistics& /*statistics*/, Subproblem& subproblem, double trust_region_radius,
22+
const Vector<double>& /*initial_point*/, Direction& direction, const WarmstartInformation& warmstart_information) {
23+
const double squared_trust_region_radius = std::pow(trust_region_radius, 2.);
24+
// first try the Newton step. This is the solution if within the trust region
25+
this->evaluation_space.compute_newton_step(subproblem, *this->linear_solver, warmstart_information);
26+
if (this->evaluation_space.newton_step_squared_norm <= squared_trust_region_radius) {
27+
// TODO save Newton step into direction
28+
return;
29+
}
30+
// if the trust region constraint is violated, compute the dogleg path: the broken path between the Cauchy step
31+
// and the Newton step
32+
this->evaluation_space.compute_dogleg(subproblem, direction, warmstart_information);
33+
}
34+
35+
[[nodiscard]] EvaluationSpace& DoglegMethod::get_evaluation_space() {
36+
return this->linear_solver->get_evaluation_space();
37+
}
38+
} // namespace
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright (c) 2025 Charlie Vanaret
2+
// Licensed under the MIT license. See LICENSE file in the project directory for details.
3+
4+
#ifndef UNO_DOGLEGMETHOD_H
5+
#define UNO_DOGLEGMETHOD_H
6+
7+
#include <memory>
8+
#include <string>
9+
#include "../BoundConstrainedSolver.hpp"
10+
#include "DoglegEvaluationSpace.hpp"
11+
#include "ingredients/subproblem_solvers/DirectSymmetricIndefiniteLinearSolver.hpp"
12+
13+
namespace uno {
14+
// forward declaration
15+
class Options;
16+
17+
class DoglegMethod: public BoundConstrainedSolver {
18+
public:
19+
explicit DoglegMethod(const Options& options);
20+
~DoglegMethod() override = default;
21+
22+
void initialize_memory(const Subproblem& subproblem) override;
23+
24+
void solve(Statistics& statistics, Subproblem& subproblem, double trust_region_radius, const Vector<double>& initial_point,
25+
Direction& direction, const WarmstartInformation& warmstart_information) override;
26+
27+
[[nodiscard]] EvaluationSpace& get_evaluation_space() override;
28+
29+
protected:
30+
const std::string& linear_solver_name;
31+
std::unique_ptr<DirectSymmetricIndefiniteLinearSolver<double>> linear_solver;
32+
DoglegEvaluationSpace evaluation_space{};
33+
};
34+
} // namespace
35+
36+
#endif // UNO_DOGLEGMETHOD_H

0 commit comments

Comments
 (0)