Skip to content

Commit

Permalink
Added function hessian_vector_product() in Model and OptimizationProb…
Browse files Browse the repository at this point in the history
…lem (and subclasses)
  • Loading branch information
cvanaret committed Dec 2, 2024
1 parent 5f8bb67 commit 8a98563
Show file tree
Hide file tree
Showing 17 changed files with 160 additions and 72 deletions.
153 changes: 89 additions & 64 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 @@ -199,68 +199,6 @@ namespace uno {
}
}

void AMPLModel::compute_lagrangian_hessian_sparsity() {
// compute the maximum number of nonzero elements, provided that all multipliers are non-zero
// int (*Sphset) (ASL*, SputInfo**, int nobj, int ow, int y, int uptri);
const int objective_number = -1;
const int upper_triangular = 1;
this->number_asl_hessian_nonzeros = static_cast<size_t>((*(this->asl)->p.Sphset)(this->asl, nullptr, objective_number, 1, 1, upper_triangular));
this->asl_hessian.reserve(this->number_asl_hessian_nonzeros);

// sparsity pattern
[[maybe_unused]] const fint* asl_column_start = this->asl->i.sputinfo_->hcolstarts;
// check that the column pointers are sorted in increasing order
assert(in_increasing_order(asl_column_start, this->number_variables + 1) && "AMPLModel::evaluate_lagrangian_hessian: column starts are not ordered");
}

size_t AMPLModel::number_objective_gradient_nonzeros() const {
return static_cast<size_t>(this->asl->i.nzo_);
}

size_t AMPLModel::number_jacobian_nonzeros() const {
return static_cast<size_t>(this->asl->i.nzc_);
}

size_t AMPLModel::number_hessian_nonzeros() const {
return this->number_asl_hessian_nonzeros;
}

const Collection<size_t>& AMPLModel::get_equality_constraints() const {
return this->equality_constraints_collection;
}

const Collection<size_t>& AMPLModel::get_inequality_constraints() const {
return this->inequality_constraints_collection;
}

const Collection<size_t>& AMPLModel::get_linear_constraints() const {
return this->linear_constraints_collection;
}

const SparseVector<size_t>& AMPLModel::get_slacks() const {
return this->slacks;
}

const Collection<size_t>& AMPLModel::get_single_lower_bounded_variables() const {
return this->single_lower_bounded_variables_collection;
}

const Collection<size_t>& AMPLModel::get_single_upper_bounded_variables() const {
return this->single_upper_bounded_variables_collection;
}

const Vector<size_t>& AMPLModel::get_fixed_variables() const {
return this->fixed_variables;
}

const Collection<size_t>& AMPLModel::get_lower_bounded_variables() const {
return this->lower_bounded_variables_collection;
}

const Collection<size_t>& AMPLModel::get_upper_bounded_variables() const {
return this->upper_bounded_variables_collection;
}

void AMPLModel::evaluate_lagrangian_hessian(const Vector<double>& x, double objective_multiplier, const Vector<double>& multipliers,
SymmetricMatrix<size_t, double>& hessian) const {
assert(hessian.capacity() >= this->number_asl_hessian_nonzeros);
Expand Down Expand Up @@ -297,6 +235,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 All @@ -309,6 +272,30 @@ namespace uno {
return this->variable_status[variable_index];
}

const Collection<size_t>& AMPLModel::get_lower_bounded_variables() const {
return this->lower_bounded_variables_collection;
}

const Collection<size_t>& AMPLModel::get_upper_bounded_variables() const {
return this->upper_bounded_variables_collection;
}

const SparseVector<size_t>& AMPLModel::get_slacks() const {
return this->slacks;
}

const Collection<size_t>& AMPLModel::get_single_lower_bounded_variables() const {
return this->single_lower_bounded_variables_collection;
}

const Collection<size_t>& AMPLModel::get_single_upper_bounded_variables() const {
return this->single_upper_bounded_variables_collection;
}

const Vector<size_t>& AMPLModel::get_fixed_variables() const {
return this->fixed_variables;
}

double AMPLModel::constraint_lower_bound(size_t constraint_index) const {
return this->constraint_lower_bounds[constraint_index];
}
Expand All @@ -325,6 +312,18 @@ namespace uno {
return this->constraint_status[constraint_index];
}

const Collection<size_t>& AMPLModel::get_equality_constraints() const {
return this->equality_constraints_collection;
}

const Collection<size_t>& AMPLModel::get_inequality_constraints() const {
return this->inequality_constraints_collection;
}

const Collection<size_t>& AMPLModel::get_linear_constraints() const {
return this->linear_constraints_collection;
}

// initial primal point
void AMPLModel::initial_primal_point(Vector<double>& x) const {
assert(x.size() >= this->number_variables);
Expand Down Expand Up @@ -379,6 +378,18 @@ namespace uno {
}
}

size_t AMPLModel::number_objective_gradient_nonzeros() const {
return static_cast<size_t>(this->asl->i.nzo_);
}

size_t AMPLModel::number_jacobian_nonzeros() const {
return static_cast<size_t>(this->asl->i.nzc_);
}

size_t AMPLModel::number_hessian_nonzeros() const {
return this->number_asl_hessian_nonzeros;
}

void AMPLModel::generate_constraints() {
for (size_t constraint_index: Range(this->number_constraints)) {
this->constraint_lower_bounds[constraint_index] = (this->asl->i.LUrhs_ != nullptr) ? this->asl->i.LUrhs_[2*constraint_index] : -INF<double>;
Expand Down Expand Up @@ -407,6 +418,20 @@ namespace uno {
}
}

void AMPLModel::compute_lagrangian_hessian_sparsity() {
// compute the maximum number of nonzero elements, provided that all multipliers are non-zero
// int (*Sphset) (ASL*, SputInfo**, int nobj, int ow, int y, int uptri);
const int objective_number = -1;
const int upper_triangular = 1;
this->number_asl_hessian_nonzeros = static_cast<size_t>((*(this->asl)->p.Sphset)(this->asl, nullptr, objective_number, 1, 1, upper_triangular));
this->asl_hessian.reserve(this->number_asl_hessian_nonzeros);

// sparsity pattern
[[maybe_unused]] const fint* asl_column_start = this->asl->i.sputinfo_->hcolstarts;
// check that the column pointers are sorted in increasing order
assert(in_increasing_order(asl_column_start, this->number_variables + 1) && "AMPLModel::evaluate_lagrangian_hessian: column starts are not ordered");
}

void AMPLModel::determine_bounds_types(const std::vector<double>& lower_bounds, const std::vector<double>& upper_bounds, std::vector<BoundType>& status) {
assert(lower_bounds.size() == status.size());
assert(upper_bounds.size() == status.size());
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 @@ -51,6 +51,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
10 changes: 8 additions & 2 deletions uno/optimization/EvaluationErrors.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,21 @@ namespace uno {
[[nodiscard]] const char* what() const noexcept override = 0;
};

struct FunctionEvaluationError : EvaluationError {
[[nodiscard]] const char* what() const noexcept override {
return "A numerical error was encountered while evaluating a function\n";
}
};

struct GradientEvaluationError : EvaluationError {
[[nodiscard]] const char* what() const noexcept override {
return "A numerical error was encountered while evaluating a gradient\n";
}
};

struct FunctionEvaluationError : EvaluationError {
struct HessianEvaluationError : EvaluationError {
[[nodiscard]] const char* what() const noexcept override {
return "A numerical error was encountered while evaluating a function\n";
return "A numerical error was encountered while evaluating the Lagrangian Hessian\n";
}
};
} // 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
Loading

0 comments on commit 8a98563

Please sign in to comment.