//This file is part of Bertini 2. // //include/bertini2/system/patch.hpp is free software: you can redistribute it and/or modify //it under the terms of the GNU General Public License as published by //the Free Software Foundation, either version 3 of the License, or //(at your option) any later version. // //include/bertini2/system/patch.hpp is distributed in the hope that it will be useful, //but WITHOUT ANY WARRANTY; without even the implied warranty of //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //GNU General Public License for more details. // //You should have received a copy of the GNU General Public License //along with include/bertini2/system/patch.hpp. If not, see . // // Copyright(C) 2015 - 2021 by Bertini2 Development Team // // See for a copy of the license, // as well as COPYING. Bertini2 is provided with permitted // additional terms in the b2/licenses/ directory. // individual authors of this file include: // silviana amethyst, university of wisconsin eau claire /** \file include/bertini2/system/patch.hpp \brief Provides the bertini::Patch class. */ #ifndef BERTINI_PATCH_HPP #define BERTINI_PATCH_HPP #include "bertini2/mpfr_complex.hpp" #include "bertini2/mpfr_extensions.hpp" #include "bertini2/num_traits.hpp" #include "bertini2/eigen_extensions.hpp" #include #include #include #include #include namespace bertini { /** \class Patch \brief An affine chart on a product of projective spaces. This class provides an adjustable-precision patch on a working space for solving a polynomial system using homotopy continuation. The constant values for the patch are unity. ## Example code, not using a bertini::System to generate the patch. \code std::vector s{2,3}; Patch p(s); Vec v(5); v << mpfr_complex(1), mpfr_complex(1), mpfr_complex(1), mpfr_complex(1), mpfr_complex(1); auto v_rescaled = p.RescalePoint(v); auto f = p.Eval(v_rescaled); \endcode At the end of the above example, the patch evaluates to 0 in the container \f$f\f$. ## Other ways of patching Patches can also be applied to a system, which should first be homogenized. */ class Patch { public: /** \brief Default constructor. Makes an empty patch. */ Patch() : precision_(DefaultPrecision()) {} /** Custom copy constructor, ensuring the max-precision coefficients are copied in highest precision */ Patch(Patch const& other) { variable_group_sizes_ = other.variable_group_sizes_; precision_ = DefaultPrecision(); // a little shorthand unpacking the tuple std::vector >& coefficients_mpfr = std::get > >(this->coefficients_working_); std::vector >& coefficients_dbl = std::get > >(this->coefficients_working_); coefficients_highest_precision_.resize(other.NumVariableGroups()); coefficients_mpfr.resize(variable_group_sizes_.size()); coefficients_dbl.resize(variable_group_sizes_.size()); for (unsigned ii(0); ii const& sizes) : variable_group_sizes_(sizes), coefficients_highest_precision_(sizes.size()), precision_(DefaultPrecision()) { using bertini::Precision; using bertini::multiprecision::RandomComplex; std::vector >& coefficients_mpfr = std::get > >(coefficients_working_); std::vector >& coefficients_dbl = std::get > >(coefficients_working_); coefficients_highest_precision_.resize(sizes.size()); coefficients_dbl.resize(sizes.size()); coefficients_mpfr.resize(sizes.size()); for (size_t ii=0; ii const& sizes) { return Patch(sizes); } /** \brief Construct random REAL patch on a space. */ static Patch RandomReal(std::vector const& sizes) { using bertini::multiprecision::RandomReal; using bertini::Precision; Patch p; p.variable_group_sizes_ = sizes; std::vector >& coefficients_mpfr = std::get > >(p.coefficients_working_); std::vector >& coefficients_dbl = std::get > >(p.coefficients_working_); p.coefficients_highest_precision_.resize(sizes.size()); coefficients_mpfr.resize(sizes.size()); coefficients_dbl.resize(sizes.size()); for (size_t ii=0; ii >& coefficients_mpfr = std::get > >(coefficients_working_); for (unsigned ii = 0; ii < NumVariableGroups(); ++ii) { for (unsigned jj=0; jjprecision_) { coefficients_mpfr[ii](jj) = coefficients_highest_precision_[ii](jj); coefficients_mpfr[ii](jj).precision(new_precision); } else coefficients_mpfr[ii](jj).precision(new_precision); assert(Precision(coefficients_mpfr[ii](jj))==new_precision); } } precision_ = new_precision; } /** \brief Evaluate the patch at a point, in place. \param function_values The vector to populate. Must be at least as long as the number of variable groups. \param x The current space point at which to evaluate. \todo Rewrite this code to use Eigen sub-vectors, if possible. If not, take this off the todo list. See http://eigen.tuxfamily.org/dox/group__TutorialBlockOperations.html */ template void EvalInPlace(Eigen::MatrixBase & function_values, Vec const& x) const { static_assert(std::is_same::value,"scalar types must match"); #ifndef BERTINI_DISABLE_ASSERTS if (! (function_values.size()>=NumVariableGroups()) ) { std::stringstream ss; ss << "container for function values must be of length at least as long as the number of variable groups. the input vector into which to write is of length " << function_values.size(); throw std::runtime_error(ss.str()); } // assert((bertini::Precision(x(0))==DoublePrecision() || bertini::Precision(x(0)) == Precision()) // && "precision of input vector must match current working precision of patch during evaluation" // ); #endif // unpack from the tuple of working coefficients const std::vector >& coefficients = std::get > >(coefficients_working_); unsigned offset(function_values.size() - NumVariableGroups()); // by precondition this number is at least 0. the precondition is ensured by the public wrapper unsigned counter(0); for (unsigned ii = 0; ii < NumVariableGroups(); ++ii) { T& value = function_values(ii+offset); value = T(-1); for (unsigned jj=0; jj Vec Eval(Vec const& x) const { Vec function_values = Vec::Zero(NumVariableGroups()); EvalInPlace(function_values, x); return function_values; } /** \brief Evaluate the Jacobian matrix, in place. \param jacobian Matrix to populate with the Jacobian. Must be large enough (NumVariableGroups x NumVariables). \param x Point at which to evaluate. Not technically needed, because the Jacobian is simply the matrix of coefficients. \todo Rewrite this code to use Eigen sub-vectors, if possible. If not, take this off the todo list. See http://eigen.tuxfamily.org/dox/group__TutorialBlockOperations.html */ template void JacobianInPlace(Eigen::MatrixBase & jacobian, Vec const& x) const { static_assert(std::is_same::value,"scalar types must match"); #ifndef BERTINI_DISABLE_ASSERTS assert(jacobian.rows()>=NumVariableGroups() && "input jacobian must have at least as many rows as variable groups"); assert(jacobian.cols()==NumVariables() && "input jacobian must have as many columns as the patch has variables"); assert( (bertini::Precision(x(0))==DoublePrecision() || bertini::Precision(x(0)) == Precision()) && "precision of input vector must match current working precision of patch during evaluation" ); #endif const std::vector >& coefficients = std::get > >(coefficients_working_); unsigned offset(jacobian.rows() - NumVariableGroups()); // by precondition this number is at least 0. the precondition is ensured by the public wrapper unsigned counter(0); for (unsigned ii = 0; ii < NumVariableGroups(); ++ii) for (unsigned jj=0; jj Mat Jacobian(Vec const& x) const { Mat jacobian = Mat::Zero(NumVariableGroups(), NumVariables()); JacobianInPlace(jacobian, x); return jacobian; } /** \brief Rescale a point so that it satisfies the patch equations herein contained. The point must be as long as there are total variables. This function modifies the vector in place, so ensure you have a copy stored somewhere else if you feel it necessary to still have the pre-rescaled vector around. \param x The point to rescale \tparam T The number type. */ template void RescalePointToFitInPlace(Vec & x) const { #ifndef BERTINI_DISABLE_ASSERTS assert(x.size() == NumVariables() && "input point for rescaling to fit a patch must have same length as total number of variables being patched, in all variable groups."); assert((bertini::Precision(x(0))==DoublePrecision() || bertini::Precision(x(0)) == Precision()) && "precision of input vector must match current working precision of patch during rescaling" ); #endif const std::vector >& coefficients = std::get > >(coefficients_working_); unsigned starting_index_counter(0); for (unsigned ii=0; ii Vec RescalePoint(Vec const& x) const { Vec x_rescaled = x; RescalePointToFitInPlace(x_rescaled); return x_rescaled; } /** \brief Get the number of variable groups \return The number of variable groups in the problem. */ unsigned NumVariableGroups() const { return variable_group_sizes_.size(); } /** \brief Get the number of variables in the patch. */ unsigned NumVariables() const { unsigned num_vars(0); for (auto v : variable_group_sizes_) num_vars += v; return num_vars; } friend std::ostream& operator<<(std::ostream & out, Patch const& p) { out << p.NumVariableGroups() << " variable groups being patched\n"; unsigned counter(0); for (auto& c : p.coefficients_highest_precision_) { out << "patch " << counter++ << " has " << c.size() << " coefficients:\n"; out << c << "\n"; } out << "current patch precision: " << p.Precision() << "\n"; return out; } /** \brief Check whether two patches are the same. \return true If have the same variable structure and coefficients. Otherwise, false. */ bool operator==(Patch const& rhs) const { if (NumVariableGroups()!=rhs.NumVariableGroups()) return false; if (NumVariables()!=rhs.NumVariables()) return false; for (unsigned ii=0; ii > coefficients_highest_precision_; ///< the highest-precision coefficients for the patch mutable std::tuple< std::vector< Vec< mpfr_complex > >, std::vector< Vec< dbl > > > coefficients_working_; ///< the current working coefficients of the patch. changing precision affects these, particularly the mpfr_complex coefficients, which are down-sampled from the highest_precision coefficients. the doubles are only down-sampled at time of creation or modification. std::vector variable_group_sizes_; ///< the sizes of the groups. In principle, these must be at least 2. mutable unsigned precision_; ///< the current working precision of the patch. // add serialization support through boost. friend class boost::serialization::access; template void serialize(Archive& ar, const unsigned version) { ar & precision_; ar & coefficients_highest_precision_; ar & std::get<0>(coefficients_working_); ar & std::get<1>(coefficients_working_); ar & variable_group_sizes_; } }; } #endif // re: include guards