Skip to content

Commit

Permalink
Added compute_hessian_vector_product() in Model and OptimizationProblem
Browse files Browse the repository at this point in the history
  • Loading branch information
cvanaret committed Nov 28, 2024
1 parent d3da1c5 commit 6785634
Show file tree
Hide file tree
Showing 15 changed files with 87 additions and 7 deletions.
29 changes: 27 additions & 2 deletions bindings/AMPL/AMPLModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,8 @@ namespace uno {
fint error_flag = 0;
// prevent ASL to crash by catching all evaluation errors
Jmp_buf err_jmp_uno;
asl->i.err_jmp_ = &err_jmp_uno;
asl->i.err_jmp1_ = &err_jmp_uno;
this->asl->i.err_jmp_ = &err_jmp_uno;
this->asl->i.err_jmp1_ = &err_jmp_uno;
if (setjmp(err_jmp_uno.jb)) {
error_flag = 1;
}
Expand Down Expand Up @@ -317,6 +317,31 @@ namespace uno {
this->asl->i.x_known = 0;
}

void AMPLModel::compute_hessian_vector_product(const Vector<double>& x, double objective_multiplier, const Vector<double>& multipliers,
Vector<double>& result) const {
fint error_flag = 0;
// prevent ASL to crash by catching all evaluation errors
Jmp_buf err_jmp_uno;
this->asl->i.err_jmp_ = &err_jmp_uno;
this->asl->i.err_jmp1_ = &err_jmp_uno;
if (setjmp(err_jmp_uno.jb)) {
error_flag = 1;
}

// scale by the objective sign
objective_multiplier *= this->objective_sign;
const int objective_number = -1;
// flip the signs of the multipliers: in AMPL, the Lagrangian is f + lambda.g, while Uno uses f - lambda.g
this->multipliers_with_flipped_sign = -multipliers;

// compute the Hessian-vector product
(this->asl->p.Hvcomp)(this->asl, result.data(), const_cast<double*>(x.data()), objective_number, &objective_multiplier,
const_cast<double*>(this->multipliers_with_flipped_sign.data()));
if (error_flag) {
throw HessianEvaluationError();
}
}

double AMPLModel::variable_lower_bound(size_t variable_index) const {
return this->variable_lower_bounds[variable_index];
}
Expand Down
2 changes: 2 additions & 0 deletions bindings/AMPL/AMPLModel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ namespace uno {
void evaluate_constraint_jacobian(const Vector<double>& x, RectangularMatrix<double>& constraint_jacobian) const override;
void evaluate_lagrangian_hessian(const Vector<double>& x, double objective_multiplier, const Vector<double>& multipliers,
SymmetricMatrix<size_t, double>& hessian) const override;
void compute_hessian_vector_product(const Vector<double>& x, double objective_multiplier, const Vector<double>& multipliers,
Vector<double>& result) const override;

[[nodiscard]] double variable_lower_bound(size_t variable_index) const override;
[[nodiscard]] double variable_upper_bound(size_t variable_index) const override;
Expand Down
4 changes: 4 additions & 0 deletions uno/model/BoundRelaxedModel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ namespace uno {
SymmetricMatrix<size_t, double>& hessian) const override {
this->model->evaluate_lagrangian_hessian(x, objective_multiplier, multipliers, hessian);
}
void compute_hessian_vector_product(const Vector<double>& x, double objective_multiplier, const Vector<double>& multipliers,
Vector<double>& result) const override {
this->model->compute_hessian_vector_product(x, objective_multiplier, multipliers, result);
}

// only these two functions are redefined
[[nodiscard]] double variable_lower_bound(size_t variable_index) const override;
Expand Down
5 changes: 5 additions & 0 deletions uno/model/FixedBoundsConstraintsModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ namespace uno {
this->model->evaluate_lagrangian_hessian(x, objective_multiplier, multipliers, hessian);
}

void FixedBoundsConstraintsModel::compute_hessian_vector_product(const Vector<double>& x, double objective_multiplier,
const Vector<double>& multipliers, Vector<double>& result) const {
this->model->compute_hessian_vector_product(x, objective_multiplier, multipliers, result);
}

double FixedBoundsConstraintsModel::variable_lower_bound(size_t variable_index) const {
if (this->model->variable_lower_bound(variable_index) == this->model->variable_upper_bound(variable_index)) {
// remove bounds of fixed variables
Expand Down
2 changes: 2 additions & 0 deletions uno/model/FixedBoundsConstraintsModel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ namespace uno {
void evaluate_constraint_jacobian(const Vector<double>& x, RectangularMatrix<double>& constraint_jacobian) const override;
void evaluate_lagrangian_hessian(const Vector<double>& x, double objective_multiplier, const Vector<double>& multipliers,
SymmetricMatrix<size_t, double>& hessian) const override;
void compute_hessian_vector_product(const Vector<double>& x, double objective_multiplier, const Vector<double>& multipliers,
Vector<double>& result) const override;

[[nodiscard]] double variable_lower_bound(size_t variable_index) const override;
[[nodiscard]] double variable_upper_bound(size_t variable_index) const override;
Expand Down
5 changes: 5 additions & 0 deletions uno/model/HomogeneousEqualityConstrainedModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ namespace uno {
}
}

void HomogeneousEqualityConstrainedModel::compute_hessian_vector_product(const Vector<double>& x, double objective_multiplier,
const Vector<double>& multipliers, Vector<double>& result) const {
this->model->compute_hessian_vector_product(x, objective_multiplier, multipliers, result);
}

double HomogeneousEqualityConstrainedModel::variable_lower_bound(size_t variable_index) const {
if (variable_index < this->model->number_variables) { // original variable
return this->model->variable_lower_bound(variable_index);
Expand Down
2 changes: 2 additions & 0 deletions uno/model/HomogeneousEqualityConstrainedModel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ namespace uno {
void evaluate_constraint_jacobian(const Vector<double>& x, RectangularMatrix<double>& constraint_jacobian) const override;
void evaluate_lagrangian_hessian(const Vector<double>& x, double objective_multiplier, const Vector<double>& multipliers,
SymmetricMatrix<size_t, double>& hessian) const override;
void compute_hessian_vector_product(const Vector<double>& x, double objective_multiplier, const Vector<double>& multipliers,
Vector<double>& result) const override;

[[nodiscard]] double variable_lower_bound(size_t variable_index) const override;
[[nodiscard]] double variable_upper_bound(size_t variable_index) const override;
Expand Down
2 changes: 2 additions & 0 deletions uno/model/Model.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ namespace uno {
virtual void evaluate_constraint_jacobian(const Vector<double>& x, RectangularMatrix<double>& constraint_jacobian) const = 0;
virtual void evaluate_lagrangian_hessian(const Vector<double>& x, double objective_multiplier, const Vector<double>& multipliers,
SymmetricMatrix<size_t, double>& hessian) const = 0;
virtual void compute_hessian_vector_product(const Vector<double>& x, double objective_multiplier, const Vector<double>& multipliers,
Vector<double>& result) const = 0;

// purely virtual functions
[[nodiscard]] virtual double variable_lower_bound(size_t variable_index) const = 0;
Expand Down
19 changes: 14 additions & 5 deletions uno/model/ScaledModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ namespace uno {
Model(original_model->name + " -> scaled", original_model->number_variables, original_model->number_constraints,
original_model->objective_sign),
model(std::move(original_model)),
scaling(this->model->number_constraints, options.get_double("function_scaling_threshold")) {
scaling(this->model->number_constraints, options.get_double("function_scaling_threshold")),
scaled_multipliers(this->number_constraints) {
if (options.get_bool("scale_functions")) {
// evaluate the gradients at the current point
initial_iterate.evaluate_objective_gradient(*this->model);
Expand Down Expand Up @@ -66,12 +67,20 @@ namespace uno {
SymmetricMatrix<size_t, double>& hessian) const {
// scale the objective and constraint multipliers
const double scaled_objective_multiplier = objective_multiplier*this->scaling.get_objective_scaling();
// TODO preallocate this vector
static Vector<double> scaled_multipliers(this->number_constraints);
for (size_t constraint_index: Range(this->number_constraints)) {
scaled_multipliers[constraint_index] = this->scaling.get_constraint_scaling(constraint_index) * multipliers[constraint_index];
this->scaled_multipliers[constraint_index] = this->scaling.get_constraint_scaling(constraint_index) * multipliers[constraint_index];
}
this->model->evaluate_lagrangian_hessian(x, scaled_objective_multiplier, scaled_multipliers, hessian);
this->model->evaluate_lagrangian_hessian(x, scaled_objective_multiplier, this->scaled_multipliers, hessian);
}

void ScaledModel::compute_hessian_vector_product(const Vector<double>& x, double objective_multiplier, const Vector<double>& multipliers,
Vector<double>& result) const {
// scale the objective and constraint multipliers
const double scaled_objective_multiplier = objective_multiplier*this->scaling.get_objective_scaling();
for (size_t constraint_index: Range(this->number_constraints)) {
this->scaled_multipliers[constraint_index] = this->scaling.get_constraint_scaling(constraint_index) * multipliers[constraint_index];
}
this->model->compute_hessian_vector_product(x, scaled_objective_multiplier, this->scaled_multipliers, result);
}

double ScaledModel::variable_lower_bound(size_t variable_index) const {
Expand Down
4 changes: 4 additions & 0 deletions uno/model/ScaledModel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include <memory>
#include "Model.hpp"
#include "linear_algebra/Vector.hpp"
#include "preprocessing/Scaling.hpp"

namespace uno {
Expand All @@ -23,6 +24,8 @@ namespace uno {
void evaluate_constraint_jacobian(const Vector<double>& x, RectangularMatrix<double>& constraint_jacobian) const override;
void evaluate_lagrangian_hessian(const Vector<double>& x, double objective_multiplier, const Vector<double>& multipliers,
SymmetricMatrix<size_t, double>& hessian) const override;
void compute_hessian_vector_product(const Vector<double>& x, double objective_multiplier, const Vector<double>& multipliers,
Vector<double>& result) const override;

[[nodiscard]] double variable_lower_bound(size_t variable_index) const override;
[[nodiscard]] double variable_upper_bound(size_t variable_index) const override;
Expand Down Expand Up @@ -53,6 +56,7 @@ namespace uno {
private:
const std::unique_ptr<Model> model{};
Scaling scaling;
mutable Vector<double> scaled_multipliers;
};
} // namespace

Expand Down
4 changes: 4 additions & 0 deletions uno/reformulation/OptimalityProblem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ namespace uno {
this->model.evaluate_lagrangian_hessian(x, this->get_objective_multiplier(), multipliers, hessian);
}

void OptimalityProblem::compute_hessian_vector_product(const Vector<double>& x, const Vector<double>& multipliers, Vector<double>& result) const {
this->model.compute_hessian_vector_product(x, this->get_objective_multiplier(), multipliers, result);
}

// Lagrangian gradient split in two parts: objective contribution and constraints' contribution
void OptimalityProblem::evaluate_lagrangian_gradient(LagrangianGradient<double>& lagrangian_gradient, Iterate& iterate,
const Multipliers& multipliers) const {
Expand Down
1 change: 1 addition & 0 deletions uno/reformulation/OptimalityProblem.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ namespace uno {
void evaluate_constraints(Iterate& iterate, std::vector<double>& constraints) const override;
void evaluate_constraint_jacobian(Iterate& iterate, RectangularMatrix<double>& constraint_jacobian) const override;
void evaluate_lagrangian_hessian(const Vector<double>& x, const Vector<double>& multipliers, SymmetricMatrix<size_t, double>& hessian) const override;
void compute_hessian_vector_product(const Vector<double>& x, const Vector<double>& multipliers, Vector<double>& result) const override;

[[nodiscard]] double variable_lower_bound(size_t variable_index) const override { return this->model.variable_lower_bound(variable_index); }
[[nodiscard]] double variable_upper_bound(size_t variable_index) const override { return this->model.variable_upper_bound(variable_index); }
Expand Down
1 change: 1 addition & 0 deletions uno/reformulation/OptimizationProblem.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ namespace uno {
virtual void evaluate_constraints(Iterate& iterate, std::vector<double>& constraints) const = 0;
virtual void evaluate_constraint_jacobian(Iterate& iterate, RectangularMatrix<double>& constraint_jacobian) const = 0;
virtual void evaluate_lagrangian_hessian(const Vector<double>& x, const Vector<double>& multipliers, SymmetricMatrix<size_t, double>& hessian) const = 0;
virtual void compute_hessian_vector_product(const Vector<double>& x, const Vector<double>& multipliers, Vector<double>& result) const = 0;

[[nodiscard]] size_t get_number_original_variables() const;
[[nodiscard]] virtual double variable_lower_bound(size_t variable_index) const = 0;
Expand Down
13 changes: 13 additions & 0 deletions uno/reformulation/l1RelaxedProblem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,19 @@ namespace uno {
}
}

void l1RelaxedProblem::compute_hessian_vector_product(const Vector<double>& x, const Vector<double>& multipliers, Vector<double>& result) const {
this->model.compute_hessian_vector_product(x, this->objective_multiplier, multipliers, result);

// proximal contribution
if (this->proximal_center != nullptr && this->proximal_coefficient != 0.) {
for (size_t variable_index: Range(this->model.number_variables)) {
const double scaling = std::min(1., 1./std::abs(this->proximal_center[variable_index]));
const double proximal_term = this->proximal_coefficient * scaling * scaling;
result[variable_index] += proximal_term * x[variable_index] * x[variable_index];
}
}
}

// Lagrangian gradient split in two parts: objective contribution and constraints' contribution
void l1RelaxedProblem::evaluate_lagrangian_gradient(LagrangianGradient<double>& lagrangian_gradient, Iterate& iterate,
const Multipliers& multipliers) const {
Expand Down
1 change: 1 addition & 0 deletions uno/reformulation/l1RelaxedProblem.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ namespace uno {
void evaluate_constraints(Iterate& iterate, std::vector<double>& constraints) const override;
void evaluate_constraint_jacobian(Iterate& iterate, RectangularMatrix<double>& constraint_jacobian) const override;
void evaluate_lagrangian_hessian(const Vector<double>& x, const Vector<double>& multipliers, SymmetricMatrix<size_t, double>& hessian) const override;
void compute_hessian_vector_product(const Vector<double>& x, const Vector<double>& multipliers, Vector<double>& result) const override;

[[nodiscard]] double variable_lower_bound(size_t variable_index) const override;
[[nodiscard]] double variable_upper_bound(size_t variable_index) const override;
Expand Down

0 comments on commit 6785634

Please sign in to comment.