// Copyright (c) 1997-2007 ETH Zurich (Switzerland). // All rights reserved. // // This file is part of CGAL (www.cgal.org). // // $URL: https://github.com/CGAL/cgal/blob/v5.1/QP_solver/include/CGAL/QP_solution.h $ // $Id: QP_solution.h 0779373 2020-03-26T13:31:46+01:00 Sébastien Loriot // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial // // // Author(s) : Kaspar Fischer // : Bernd Gaertner // : Sven Schoenherr // : Franz Wessendorp #ifndef CGAL_QP_SOLUTION_H #define CGAL_QP_SOLUTION_H #include #include #include #include #include #include #include #include #include #include #include #include #include namespace CGAL { // forward references template class QP_solver; namespace QP_solution_detail { template class Quotient_normalizer; template class Value_by_index; template class Unbounded_direction_by_index; template class Lambda_by_index; } // global status type enum Quadratic_program_status { QP_UPDATE, QP_INFEASIBLE, QP_UNBOUNDED, QP_OPTIMAL }; // abstract base class of all QP-solvers // ------------------------------------- template class QP_solver_base { public: // types typedef CGAL::Creator_2< ET, ET, Quotient > U_Quotient_creator; // unnormalized quotient creator ET x ET -> (ET, ET) typedef QP_solution_detail::Quotient_normalizer Quotient_normalizer; // normalizer (ET, ET) -> (ET, ET) typedef boost::function1< Quotient, ET > Quotient_maker; typedef std::vector Indices; typedef Indices::iterator Index_mutable_iterator; typedef Indices::const_iterator Index_const_iterator; typedef typename QP_solution_detail::Value_by_index Value_by_index; typedef typename boost::transform_iterator > Variable_numerator_iterator; typedef boost::transform_iterator Variable_value_iterator; typedef typename QP_solution_detail::Unbounded_direction_by_index Unbounded_direction_by_index; typedef boost::transform_iterator > Unbounded_direction_iterator; typedef typename QP_solution_detail::Lambda_by_index Lambda_by_index; typedef boost::transform_iterator > Lambda_numerator_iterator; typedef boost::transform_iterator Lambda_iterator; public: // virtual access functions to solution that will // be overridden by QP_solver below // Solution // -------- virtual ET solution_numerator() const = 0; virtual ET solution_denominator() const = 0; Quotient solution( ) const { // workaround to please Boost 1.33.1: ET n = solution_numerator(); ET d = solution_denominator(); return boost::bind (Quotient_normalizer(), boost::bind (U_Quotient_creator(), _1, _2)) (n, d); // (solution_numerator(), solution_denominator()); } virtual Quadratic_program_status status() const = 0; virtual int iterations() const = 0; // Variable values // --------------- virtual ET variable_numerator_value (int i) const = 0; virtual const ET& variables_common_denominator( ) const = 0; virtual int number_of_variables() const = 0; // value type ET Variable_numerator_iterator original_variables_numerator_begin( ) const { return Variable_numerator_iterator (boost::counting_iterator(0), Value_by_index(this));} Variable_numerator_iterator original_variables_numerator_end ( ) const { return Variable_numerator_iterator (boost::counting_iterator(number_of_variables()) , Value_by_index(this));} // value type Quotient Variable_value_iterator original_variable_values_begin( ) const { return Variable_value_iterator (original_variables_numerator_begin(), boost::bind (boost::bind (Quotient_normalizer(), boost::bind (U_Quotient_creator(), _1, _2)), _1, variables_common_denominator())); } Variable_value_iterator original_variable_values_end ( ) const { return Variable_value_iterator (original_variables_numerator_end(), boost::bind (boost::bind (Quotient_normalizer(), boost::bind (U_Quotient_creator(), _1, _2)), _1, variables_common_denominator())); } // Basic variables and constraints // ------------------------------- virtual Index_const_iterator basic_original_variable_indices_begin() const = 0; virtual Index_const_iterator basic_original_variable_indices_end() const = 0; virtual int number_of_basic_original_variables() const = 0; virtual Index_const_iterator basic_constraint_indices_begin() const = 0; virtual Index_const_iterator basic_constraint_indices_end() const = 0; virtual int number_of_basic_constraints() const = 0; // Unboundedness // ------------- virtual ET unbounded_direction_value(int i) const = 0; Unbounded_direction_iterator unbounded_direction_begin() const { return Unbounded_direction_iterator (boost::counting_iterator(0), Unbounded_direction_by_index(this));} // Returns the past-the-end iterator corresponding to // unbounded_direction_begin(). Unbounded_direction_iterator unbounded_direction_end() const { return Unbounded_direction_iterator (boost::counting_iterator(number_of_variables()), Unbounded_direction_by_index(this));} // Optimality // ---------- virtual ET lambda_numerator(int i) const = 0; virtual int number_of_constraints() const = 0; // value type ET Lambda_numerator_iterator lambda_numerator_begin() const { return Lambda_numerator_iterator (boost::counting_iterator(0), Lambda_by_index(this));} Lambda_numerator_iterator lambda_numerator_end() const { return Lambda_numerator_iterator (boost::counting_iterator(number_of_constraints()), Lambda_by_index(this));} // value type Quotient Lambda_iterator lambda_begin() const { return Lambda_iterator (lambda_numerator_begin(), boost::bind (boost::bind (Quotient_normalizer(), boost::bind (U_Quotient_creator(), _1, _2)), _1, variables_common_denominator())); } Lambda_iterator lambda_end() const { return Lambda_iterator (lambda_numerator_end(), boost::bind (boost::bind (Quotient_normalizer(), boost::bind (U_Quotient_creator(), _1, _2)), _1, variables_common_denominator())); } // destruction // ----------- virtual ~QP_solver_base() {} }; // Quadratic_program_solution class: a handle for QP_solver_base // ----------------------------------------------------------------- template class Quadratic_program_solution: Handle_for*> { public: typedef ET_ ET; // interface types // =============== // variable values / indices // ------------------------- typedef typename QP_solver_base::Variable_value_iterator Variable_value_iterator; typedef typename QP_solver_base::Variable_numerator_iterator Variable_numerator_iterator; typedef typename QP_solver_base::Index_const_iterator Index_iterator; // certificates // ------------ typedef typename QP_solver_base::Unbounded_direction_iterator Unboundedness_certificate_iterator; typedef typename QP_solver_base::Lambda_numerator_iterator Optimality_certificate_numerator_iterator; typedef typename QP_solver_base::Lambda_iterator Optimality_certificate_iterator; typedef typename QP_solver_base::Lambda_numerator_iterator Infeasibility_certificate_iterator; // methods // ------- Quadratic_program_solution () : Handle_for*>(), et0(0) { *(this->ptr()) = 0; // unitialized solution } Quadratic_program_solution (const QP_solver_base* s) : Handle_for*>(s), et0(0) {} Quadratic_program_solution(const Quadratic_program_solution& rhs) : Handle_for*>(), et0(0) { *this = rhs; } Quadratic_program_solution& operator= (const Quadratic_program_solution& sol) { if (this != &sol) { // delete the old solver if necessary if (!this->is_shared()) delete *(this->ptr()); this->Handle_for*>::operator=(sol); } return *this; } ~Quadratic_program_solution() { if (!this->is_shared()) delete *(this->ptr()); } private: const QP_solver_base* solver() const { return *(this->Ptr()); } public: bool is_void() const { return solver() == 0; } Quotient objective_value() const { CGAL_qpe_assertion_msg(!is_void(), "Solution not initialized"); return solver()->solution(); } ET objective_value_numerator() const { CGAL_qpe_assertion_msg(!is_void(), "Solution not initialized"); return solver()->solution_numerator(); } ET objective_value_denominator() const { CGAL_qpe_assertion_msg(!is_void(), "Solution not initialized"); return solver()->solution_denominator(); } Quadratic_program_status status() const { CGAL_qpe_assertion_msg(!is_void(), "Solution not initialized"); return solver()->status(); } bool is_optimal() const { CGAL_qpe_assertion_msg(!is_void(), "Solution not initialized"); return status() == QP_OPTIMAL; } bool is_infeasible() const { CGAL_qpe_assertion_msg(!is_void(), "Solution not initialized"); return status() == QP_INFEASIBLE; } bool is_unbounded() const { CGAL_qpe_assertion_msg(!is_void(), "Solution not initialized"); return status() == QP_UNBOUNDED; } int number_of_iterations() const { CGAL_qpe_assertion_msg(!is_void(), "Solution not initialized"); return solver()->iterations(); } Variable_value_iterator variable_values_begin() const { CGAL_qpe_assertion_msg(!is_void(), "Solution not initialized"); return solver()->original_variable_values_begin(); } Variable_value_iterator variable_values_end() const { CGAL_qpe_assertion_msg(!is_void(), "Solution not initialized"); return solver()->original_variable_values_end(); } Variable_numerator_iterator variable_numerators_begin() const { CGAL_qpe_assertion_msg(!is_void(), "Solution not initialized"); return solver()->original_variables_numerator_begin(); } Variable_numerator_iterator variable_numerators_end() const { CGAL_qpe_assertion_msg(!is_void(), "Solution not initialized"); return solver()->original_variables_numerator_end(); } const ET& variables_common_denominator() const { CGAL_qpe_assertion_msg(!is_void(), "Solution not initialized"); return solver()->variables_common_denominator(); } Index_iterator basic_variable_indices_begin() const { CGAL_qpe_assertion_msg(!is_void(), "Solution not initialized"); return solver()->basic_original_variable_indices_begin(); } Index_iterator basic_variable_indices_end() const { CGAL_qpe_assertion_msg(!is_void(), "Solution not initialized"); return solver()->basic_original_variable_indices_end(); } int number_of_basic_variables() const { CGAL_qpe_assertion_msg(!is_void(), "Solution not initialized"); return solver()->number_of_basic_original_variables(); } Index_iterator basic_constraint_indices_begin() const { CGAL_qpe_assertion_msg(!is_void(), "Solution not initialized"); return solver()->basic_constraint_indices_begin(); } Index_iterator basic_constraint_indices_end() const { CGAL_qpe_assertion_msg(!is_void(), "Solution not initialized"); return solver()->basic_constraint_indices_end(); } int number_of_basic_constraints() const { CGAL_qpe_assertion_msg(!is_void(), "Solution not initialized"); return solver()->number_of_basic_constraints(); } Optimality_certificate_numerator_iterator optimality_certificate_numerators_begin() const { CGAL_qpe_assertion_msg(!is_void(), "Solution not initialized"); CGAL_qpe_assertion(status() == QP_OPTIMAL); return solver()->lambda_numerator_begin(); } Optimality_certificate_numerator_iterator optimality_certificate_numerators_end() const { CGAL_qpe_assertion_msg(!is_void(), "Solution not initialized"); CGAL_qpe_assertion(status() == QP_OPTIMAL); return solver()->lambda_numerator_end(); } Optimality_certificate_iterator optimality_certificate_begin() const { CGAL_qpe_assertion_msg(!is_void(), "Solution not initialized"); CGAL_qpe_assertion(status() == QP_OPTIMAL); return solver()->lambda_begin(); } Optimality_certificate_iterator optimality_certificate_end() const { CGAL_qpe_assertion_msg(!is_void(), "Solution not initialized"); CGAL_qpe_assertion(status() == QP_OPTIMAL); return solver()->lambda_end(); } // infeasibility // ------------- Infeasibility_certificate_iterator infeasibility_certificate_begin() const { CGAL_qpe_assertion_msg(!is_void(), "Solution not initialized"); CGAL_qpe_assertion(status() == QP_INFEASIBLE); return solver()->lambda_numerator_begin(); } Infeasibility_certificate_iterator infeasibility_certificate_end() const { CGAL_qpe_assertion_msg(!is_void(), "Solution not initialized"); CGAL_qpe_assertion(status() == QP_INFEASIBLE); return solver()->lambda_numerator_end(); } // unboundedness // ------------- Unboundedness_certificate_iterator unboundedness_certificate_begin() const { CGAL_qpe_assertion_msg(!is_void(), "Solution not initialized"); CGAL_qpe_assertion(status() == QP_UNBOUNDED); return solver()->unbounded_direction_begin(); } Unboundedness_certificate_iterator unboundedness_certificate_end() const { CGAL_qpe_assertion_msg(!is_void(), "Solution not initialized"); CGAL_qpe_assertion(status() == QP_UNBOUNDED); return solver()->unbounded_direction_end(); } private: ET et0; // 0 // validity // -------- // error message returned by failing validation std::string err_msg; // the error message is set by the following function bool error (const std::string& message) { err_msg = message; return false; } public: bool is_valid() const { return err_msg.empty(); } const std::string& get_error() const { return err_msg; } // these four methods use the certificates to validate the solution // of all four program types; in case this fails, the solution becomes // invalid (and this explains why the methods are non-const) template bool solves_quadratic_program (const QuadraticProgram& qp) { return solves_program(qp, Tag_false(), Tag_false()); } template bool solves_linear_program (const LinearProgram& lp) { return solves_program(lp, Tag_true(), Tag_false()); } template bool solves_nonnegative_quadratic_program (const NonnegativeQuadraticProgram& qp) { return solves_program(qp, Tag_false(), Tag_true()); } template bool solves_nonnegative_linear_program (const NonnegativeLinearProgram& lp) { return solves_program(lp, Tag_true(), Tag_true()); } // helper used by all four validation methods above; see // QP_solver/QP_solution_impl.h for its implementation template bool solves_program (const Program& p, Is_linear is_linear, Is_nonnegative is_nonnegative); private: // helpers used by the previous method template bool is_feasible (const Program& p, typename std::vector& ax_minus_b, Tag_true /*is_nonnegative*/); template bool is_feasible (const Program& p, typename std::vector& ax_minus_b, Tag_false /*is_nonnegative*/); template bool is_optimal_1 (const Program& p); template bool is_optimal_2 (const Program& p, const typename std::vector& ax_minus_b); template bool is_optimal_3 (const Program& p, typename std::vector& two_Dx, Tag_true /*is_linear*/, Tag_true /*is_nonnegative*/); template bool is_optimal_3 (const Program& p, typename std::vector& two_Dx, Tag_false /*is_linear*/, Tag_true /*is_nonnegative*/); template bool is_optimal_3 (const Program& p, typename std::vector& two_Dx, Tag_true /*is_linear*/, Tag_false /*is_nonnegative*/); template bool is_optimal_3 (const Program& p, typename std::vector& two_Dx, Tag_false /*is_linear*/, Tag_false /*is_nonnegative*/); template bool is_infeasible_1 (const Program& p); template bool is_infeasible_2 (const Program& p, typename std::vector& lambda_a, Tag_true /*is_nonnegative*/); template bool is_infeasible_2 (const Program& p, typename std::vector& lambda_a, Tag_false /*is_nonnegative*/); template bool is_infeasible_3 (const Program& p, const typename std::vector& /*lambda_a*/, Tag_true /*is_nonnegative*/); template bool is_infeasible_3 (const Program& p, const typename std::vector& lambda_a, Tag_false /*is_nonnegative*/); template bool is_unbounded_1 (const Program& p); template bool is_unbounded_2 (const Program& p, Tag_true /*is_nonnegative*/); template bool is_unbounded_2 (const Program& p, Tag_false /*is_nonnegative*/); template bool is_unbounded_3 (const Program& p, Tag_true /*is_linear*/); template bool is_unbounded_3 (const Program& p, Tag_false /*is_linear*/); template bool is_value_correct (const Program& p, typename std::vector& /*two_Dx*/, Tag_true /*is_linear*/); template bool is_value_correct (const Program& p, typename std::vector& two_Dx, Tag_false /*is_linear*/); template bool are_constraints_feasible (const Program& p, typename std::vector& ax); template bool are_bounds_feasible (const Program& p, Tag_true /*is_nonnegative*/); template bool are_bounds_feasible (const Program& p, Tag_false /*is_nonnegative*/); template void add_Az (const Program& p, Z_iterator z, typename std::vector& v); template void add_two_Dz (const Program& p, Z_iterator z, typename std::vector& v); template void add_zA (const Program& p, Z_iterator z, typename std::vector& v); template void add_c (const Program& p, typename std::vector& v); }; // output template std::ostream& operator<< (std::ostream& o, const Quadratic_program_solution& s) { o << "status: "; switch (s.status()) { case QP_INFEASIBLE: return o << "INFEASIBLE\n"; case QP_UNBOUNDED: return o << "UNBOUNDED\n"; case QP_OPTIMAL: o << "OPTIMAL\n"; break; default: CGAL_qpe_assertion(false); } o << "objective value: " << s.objective_value() << "\n"; o << "variable values:\n"; int j=0; for ( typename Quadratic_program_solution::Variable_value_iterator it = s.variable_values_begin(); it < s.variable_values_end(); ++it, ++j) o << " " << j << ": " << *it << "\n"; return o; } // Details namespace QP_solution_detail { // Quotient_normalizer // ------------------- template < typename ET> class Quotient_normalizer { public: typedef CGAL::Quotient result_type; private: typedef CGAL::Algebraic_structure_traits AST; typedef typename AST::Algebraic_category Category; public: typedef CGAL::Boolean_tag< CGAL::is_same_or_derived::value> Has_gcd; typedef CGAL::Boolean_tag< CGAL::is_same_or_derived::value> Has_exact_division; CGAL::Quotient normalize (const CGAL::Quotient& q, Tag_true /*has_gcd*/, Tag_true /*has_exact_division*/) const { if (CGAL::is_zero (q.numerator())) return CGAL::Quotient(ET(0), ET(1)); ET gcd = CGAL::gcd (q.numerator(), q.denominator()); return CGAL::Quotient (CGAL::integral_division (q.numerator(), gcd), CGAL::integral_division (q.denominator(), gcd)); } CGAL::Quotient normalize (const CGAL::Quotient& q, Tag_true /*has_gcd*/, Tag_false /*has_exact_division*/) const { return q; } CGAL::Quotient normalize (const CGAL::Quotient& q, Tag_false /*has_gcd*/, Tag_true /*has_exact_division*/) const { return q; } CGAL::Quotient normalize (const CGAL::Quotient& q, Tag_false /*has_gcd*/, Tag_false /*has_exact_division*/) const { return q; } CGAL::Quotient operator() (const CGAL::Quotient& q) const { return normalize (q, Has_gcd(), Has_exact_division()); } }; // Value_by_index // -------------- template < typename ET> class Value_by_index : public CGAL::cpp98::unary_function< std::size_t, ET> { public: typedef QP_solver_base QP; typedef ET result_type; Value_by_index(const QP* solver) : s (solver) {} // returns value * denominator result_type operator () ( std::size_t i) const { return s->variable_numerator_value(static_cast(i)); } const QP* s; }; // Unbounded_direction_by_index // ---------------------------- template < typename ET> class Unbounded_direction_by_index : public CGAL::cpp98::unary_function< std::size_t, ET> { public: typedef QP_solver_base QP; typedef ET result_type; Unbounded_direction_by_index(const QP* solver) : s (solver) {} result_type operator () ( std::size_t i) const { return s->unbounded_direction_value(static_cast(i)); } const QP* s; }; // Lambda_by_index // --------------- template < typename ET> class Lambda_by_index : public CGAL::cpp98::unary_function< std::size_t, ET> { public: typedef QP_solver_base QP; typedef ET result_type; Lambda_by_index(const QP* solver) : s (solver) {} result_type operator () ( std::size_t i) const { return s->lambda_numerator(static_cast(i)); } const QP* s; }; } } //namespace CGAL #include #include #endif// CGAL_QP_SOLUTION_H