You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1838 lines
58 KiB
1838 lines
58 KiB
// This file is part of Bertini 2.
|
|
//
|
|
// system.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.
|
|
//
|
|
// system.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 system.hpp. If not, see <http://www.gnu.org/licenses/>.
|
|
//
|
|
// Copyright(C) 2015 - 2021 by Bertini2 Development Team
|
|
//
|
|
// See <http://www.gnu.org/licenses/> 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 system/system.hpp
|
|
|
|
\brief Provides the bertini::System class.
|
|
*/
|
|
|
|
#ifndef BERTINI_SYSTEM_HPP
|
|
#define BERTINI_SYSTEM_HPP
|
|
|
|
#include <assert.h>
|
|
|
|
#include <boost/archive/binary_iarchive.hpp>
|
|
#include <boost/archive/binary_oarchive.hpp>
|
|
#include <boost/archive/text_iarchive.hpp>
|
|
#include <boost/archive/text_oarchive.hpp>
|
|
#include <boost/iostreams/device/back_inserter.hpp>
|
|
#include <boost/iostreams/stream.hpp>
|
|
#include <boost/iostreams/stream_buffer.hpp>
|
|
#include <boost/serialization/deque.hpp>
|
|
#include <boost/serialization/export.hpp>
|
|
#include <boost/serialization/shared_ptr.hpp>
|
|
#include <boost/serialization/vector.hpp>
|
|
#include <boost/type_index.hpp>
|
|
#include <vector>
|
|
|
|
#include "eigen_extensions.hpp"
|
|
#include "function_tree.hpp"
|
|
#include "mpfr_complex.hpp"
|
|
#include "mpfr_extensions.hpp"
|
|
#include "system/patch.hpp"
|
|
#include "system/straight_line_program.hpp"
|
|
|
|
namespace bertini {
|
|
|
|
enum class EvalMethod {
|
|
FunctionTree, // using virtual methods and recursion
|
|
SLP // using straight line programs
|
|
// now! 20230714, Eindhoven, Netherlands
|
|
};
|
|
|
|
enum class DerivMethod {
|
|
JacobianNode, // using Jacobian nodes, which are either 1 or 0 when evaluated
|
|
// based on the variable of differentiation
|
|
Derivatives // classic differentiation, using more space in memory but not
|
|
// requiring a variable of differentation when evaluatiing
|
|
};
|
|
|
|
/**
|
|
\brief Gets the default evaluation method for Jacobians. One might be faster...
|
|
*/
|
|
EvalMethod DefaultEvalMethod();
|
|
|
|
DerivMethod DefaultDerivMethod();
|
|
|
|
/**
|
|
\brief Get the default value for whether a system should autosimplify.
|
|
*/
|
|
bool DefaultAutoSimplify();
|
|
|
|
/**
|
|
\brief The fundamental polynomial system class for Bertini2.
|
|
|
|
The fundamental polynomial system class for Bertini2.
|
|
|
|
Other System types are derived from this, but this class is not abstract.
|
|
*/
|
|
class System {
|
|
public:
|
|
// a few local using statements to reduce typing etc.
|
|
using Fn = std::shared_ptr<node::Function>;
|
|
using Var = std::shared_ptr<node::Variable>;
|
|
using Nd = std::shared_ptr<node::Node>;
|
|
using Jac = std::shared_ptr<node::Jacobian>;
|
|
|
|
/**
|
|
\brief The default constructor for a system.
|
|
*/
|
|
System()
|
|
: is_differentiated_(false),
|
|
have_path_variable_(false),
|
|
have_ordering_(false),
|
|
precision_(DefaultPrecision()),
|
|
is_patched_(false) {}
|
|
|
|
/**
|
|
\brief The copy operator, creates a system from a string using the Bertini
|
|
parser for Bertini classic syntax.
|
|
*/
|
|
explicit System(std::string const& input);
|
|
|
|
/**
|
|
\brief The copy operator
|
|
*/
|
|
System(System const& other);
|
|
|
|
/**
|
|
\brief The move copy operator
|
|
*/
|
|
System(System&& other) : System() { swap(*this, other); }
|
|
|
|
/**
|
|
\brief The assignment operator
|
|
*/
|
|
System& operator=(const System& other);
|
|
|
|
/**
|
|
\brief The move assignment operator
|
|
*/
|
|
System& operator=(System&& other) = default;
|
|
|
|
/**
|
|
The free swap function for systems.
|
|
*/
|
|
friend void swap(System& a, System& b);
|
|
|
|
/**
|
|
Change the precision of the entire system's functions, subfunctions, and all
|
|
other nodes.
|
|
|
|
\param new_precision The new precision, in digits, to work in. This only
|
|
affects the mpfr_complex types, not double. To use low-precision (doubles),
|
|
use that number type in the templated functions.
|
|
*/
|
|
void precision(unsigned new_precision) const;
|
|
|
|
/**
|
|
\brief Get the current precision of a system.
|
|
*/
|
|
unsigned precision() const { return precision_; }
|
|
|
|
/**
|
|
\brief Compute and internally store the symbolic Jacobian of the system.
|
|
*/
|
|
void Differentiate() const;
|
|
|
|
/**
|
|
\brief Force re-evaluation of the system next eval of functions. If something
|
|
has changed in the system, call this.
|
|
*/
|
|
void ResetFunctions() const {
|
|
// TODO: it has the unfortunate side effect of resetting constant functions,
|
|
// too.
|
|
switch (eval_method_) {
|
|
case EvalMethod::FunctionTree:
|
|
for (const auto& iter : functions_) iter->Reset();
|
|
break;
|
|
case EvalMethod::SLP:
|
|
// nothing
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
\brief Force re-evaluation of the system next eval of Jacobians. If something
|
|
has changed in the system, call this.
|
|
*/
|
|
void ResetJacobian() const {
|
|
switch (eval_method_) {
|
|
case EvalMethod::FunctionTree: {
|
|
switch (deriv_method_) {
|
|
case DerivMethod::JacobianNode: {
|
|
for (const auto& iter : jacobian_) iter->Reset();
|
|
break;
|
|
}
|
|
case DerivMethod::Derivatives: {
|
|
for (const auto& iter : space_derivatives_) iter->Reset();
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case EvalMethod::SLP: {
|
|
// nothing to do, it's not a resetting kind of thing.
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ResetTimeDerivatives() const {
|
|
switch (eval_method_) {
|
|
case EvalMethod::FunctionTree: {
|
|
switch (deriv_method_) {
|
|
case DerivMethod::JacobianNode: {
|
|
for (const auto& iter : jacobian_) iter->Reset();
|
|
break;
|
|
}
|
|
case DerivMethod::Derivatives: {
|
|
for (const auto& iter : time_derivatives_) iter->Reset();
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case EvalMethod::SLP: {
|
|
// nothing to do, it's not a resetting kind of thing.
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
\brief A complete reset of the system, so that all of functions, space
|
|
derivatives, and time derivatives will all be re-evaluated.
|
|
*/
|
|
void Reset() const {
|
|
ResetFunctions();
|
|
ResetJacobian();
|
|
ResetTimeDerivatives();
|
|
}
|
|
/**
|
|
\brief Evaluate the system using the previously set variable (and time)
|
|
values, in place.
|
|
|
|
It is up to YOU to ensure that the system's variables (and path variable) has
|
|
been set prior to this function call.
|
|
|
|
\return The function values of the system
|
|
*/
|
|
template <typename T>
|
|
void EvalInPlace(Vec<T>& function_values) const {
|
|
if (function_values.size() != NumTotalFunctions()) {
|
|
std::stringstream ss;
|
|
ss << "trying to evaluate system in-place, but number length of vector "
|
|
"into which to write the values ("
|
|
<< function_values.size()
|
|
<< ") doesn't match number of system user-defined functions plus "
|
|
"patches ( "
|
|
<< NumNaturalFunctions() << "+" << NumPatches()
|
|
<< ") = " << NumTotalFunctions()
|
|
<< "). Use System.NumTotalFunctions() to make the container for "
|
|
"in-place evaluation";
|
|
throw std::runtime_error(ss.str());
|
|
}
|
|
|
|
switch (eval_method_) {
|
|
case EvalMethod::FunctionTree: {
|
|
unsigned counter(0);
|
|
for (auto iter = functions_.begin(); iter != functions_.end();
|
|
iter++, counter++) {
|
|
(*iter)->EvalInPlace<T>(function_values(counter));
|
|
}
|
|
}
|
|
|
|
case EvalMethod::SLP: {
|
|
slp_.GetFuncValsInPlace<T>(function_values);
|
|
}
|
|
}
|
|
|
|
if (IsPatched())
|
|
patch_.EvalInPlace(
|
|
function_values,
|
|
std::get<Vec<T> >(
|
|
current_variable_values_)); // does a patch not have a caching
|
|
// mechanism?
|
|
// .segment(NumNaturalFunctions(),NumTotalVariableGroups())
|
|
}
|
|
|
|
/**
|
|
\brief Evaluate the system using the previously set variable (and time)
|
|
values, creating vector of function values.
|
|
|
|
It is up to YOU to ensure that the system's variables (and path variable) has
|
|
been set prior to this function call.
|
|
|
|
\return The function values of the system
|
|
*/
|
|
template <typename T>
|
|
Vec<T> Eval() const {
|
|
Vec<T> function_values(
|
|
NumTotalFunctions()); // create vector with correct number of entries.
|
|
EvalInPlace(function_values);
|
|
|
|
return function_values;
|
|
}
|
|
|
|
/**
|
|
\brief Evaluate the system, provided the system has no path variable defined,
|
|
in place.
|
|
|
|
Causes the current variable values to be set in the system. Resets the
|
|
function tree's stored numbers.
|
|
|
|
|
|
\throws std::runtime_error, if a path variable IS defined, but you didn't
|
|
pass it a value. Also throws if the number of variables doesn't match.
|
|
\tparam T the number-type for return. Probably dbl=std::complex<double>, or
|
|
mpfr_complex=bertini::mpfr_complex. \param variable_values The values of the
|
|
variables, for the evaluation.
|
|
*/
|
|
template <typename T, typename Derived>
|
|
void EvalInPlace(Vec<T>& function_values,
|
|
const Eigen::MatrixBase<Derived>& variable_values) const {
|
|
static_assert(std::is_same<typename Derived::Scalar, T>::value,
|
|
"scalar types must match");
|
|
|
|
if (variable_values.size() != NumVariables()) {
|
|
std::stringstream ss;
|
|
ss << "trying to evaluate system, but number of input variables ("
|
|
<< variable_values.size()
|
|
<< ") doesn't match number of system variables (" << NumVariables()
|
|
<< ").";
|
|
throw std::runtime_error(ss.str());
|
|
}
|
|
if (have_path_variable_)
|
|
throw std::runtime_error(
|
|
"not using a time value for evaluation of system, but path variable "
|
|
"IS defined.");
|
|
|
|
SetVariables(variable_values.eval());
|
|
ResetFunctions();
|
|
EvalInPlace(function_values);
|
|
}
|
|
|
|
/**
|
|
\brief Evaluate the system, provided the system has no path variable defined.
|
|
|
|
Causes the current variable values to be set in the system. Resets the
|
|
function tree's stored numbers.
|
|
|
|
|
|
\throws std::runtime_error, if a path variable IS defined, but you didn't pass
|
|
it a value. Also throws if the number of variables doesn't match. \tparam T
|
|
the number-type for return. Probably dbl=std::complex<double>, or
|
|
mpfr_complex=bertini::mpfr_complex. \param variable_values The values of the
|
|
variables, for the evaluation.
|
|
*/
|
|
template <typename Derived>
|
|
typename Derived::PlainObject Eval(
|
|
const Eigen::MatrixBase<Derived>& variable_values) const {
|
|
typedef typename Derived::Scalar T;
|
|
|
|
Vec<T> function_values(
|
|
NumTotalFunctions()); // create vector with correct number of entries.
|
|
EvalInPlace(function_values, variable_values);
|
|
return function_values;
|
|
}
|
|
|
|
template <typename T>
|
|
Vec<T> Eval(const Vec<T>& variable_values) const {
|
|
Vec<T> function_values(
|
|
NumTotalFunctions()); // create vector with correct number of entries.
|
|
EvalInPlace(function_values, variable_values);
|
|
return function_values;
|
|
}
|
|
|
|
/**
|
|
Evaluate the system, provided a path variable is defined for the system, in
|
|
place.
|
|
|
|
\throws std::runtime_error, if a path variable is NOT defined, and you passed
|
|
it a value. Also throws if the number of variables doesn't match. \tparam T
|
|
the number-type for return. Probably dbl=std::complex<double>, or
|
|
mpfr_complex=bertini::mpfr_complex.
|
|
|
|
\param variable_values The values of the variables, for the evaluation.
|
|
\param path_variable_value The current value of the path variable.
|
|
|
|
\todo The Eval() function for systems has the unfortunate side effect of
|
|
resetting constant functions. Modify the System class so that only certain
|
|
parts of the tree get reset.
|
|
*/
|
|
template <typename Derived, typename T>
|
|
void EvalInPlace(Vec<T>& function_values,
|
|
const Eigen::MatrixBase<Derived>& variable_values,
|
|
const T& path_variable_value) const {
|
|
static_assert(std::is_same<typename Derived::Scalar, T>::value,
|
|
"scalar types must be the same");
|
|
|
|
if (variable_values.size() != NumVariables())
|
|
throw std::runtime_error(
|
|
"trying to evaluate system, but number of variables doesn't match.");
|
|
if (!have_path_variable_)
|
|
throw std::runtime_error(
|
|
"trying to use a time value for evaluation of system, but no path "
|
|
"variable defined.");
|
|
|
|
SetVariables(variable_values.eval()); // TODO: remove this eval
|
|
SetPathVariable(path_variable_value);
|
|
|
|
ResetFunctions(); // todo, elimiante this. i feel like setting the
|
|
// variables or path variable should be enough to set the
|
|
// flag/ take the action
|
|
|
|
EvalInPlace(function_values);
|
|
}
|
|
|
|
/**
|
|
Evaluate the system, provided a path variable is defined for the system.
|
|
|
|
\throws std::runtime_error, if a path variable is NOT defined, and you passed
|
|
it a value. Also throws if the number of variables doesn't match. \tparam T
|
|
the number-type for return. Probably dbl=std::complex<double>, or
|
|
mpfr_complex=bertini::mpfr_complex.
|
|
|
|
\param variable_values The values of the variables, for the evaluation.
|
|
\param path_variable_value The current value of the path variable.
|
|
|
|
\todo The Eval() function for systems has the unfortunate side effect of
|
|
resetting constant functions. Modify the System class so that only certain
|
|
parts of the tree get reset.
|
|
*/
|
|
template <typename Derived, typename T>
|
|
Vec<T> Eval(const Eigen::MatrixBase<Derived>& variable_values,
|
|
const T& path_variable_value) const {
|
|
Vec<T> function_values(
|
|
NumTotalFunctions()); // create vector with correct number of entries.
|
|
EvalInPlace(function_values, variable_values, path_variable_value);
|
|
return function_values;
|
|
}
|
|
|
|
template <typename T>
|
|
Vec<T> Eval(const Vec<T>& variable_values,
|
|
const T& path_variable_value) const {
|
|
Vec<T> function_values(
|
|
NumTotalFunctions()); // create vector with correct number of entries.
|
|
EvalInPlace(function_values, variable_values, path_variable_value);
|
|
return function_values;
|
|
}
|
|
|
|
/**
|
|
\brief Evaluate the Jacobian matrix of the system, using the previous space
|
|
and time values, in place.
|
|
|
|
\tparam T the number-type for return. Probably dbl=std::complex<double>, or
|
|
mpfr_complex=bertini::mpfr_complex.
|
|
|
|
This is analogous to J = sys.JacobianInPlace();
|
|
|
|
The input matrix must have the correct size already
|
|
*/
|
|
template <typename T>
|
|
void JacobianInPlace(Mat<T>& J) const {
|
|
if (J.rows() != NumTotalFunctions() || J.cols() != NumVariables()) {
|
|
throw std::runtime_error(
|
|
"trying to evaluate jacobian of system in place, but input J doesn't "
|
|
"have right number of columns or rows");
|
|
}
|
|
|
|
const auto& vars = Variables();
|
|
|
|
if (!is_differentiated_) Differentiate();
|
|
|
|
switch (eval_method_) {
|
|
case EvalMethod::FunctionTree: {
|
|
switch (deriv_method_) {
|
|
case DerivMethod::JacobianNode: {
|
|
for (int ii = 0; ii < NumNaturalFunctions(); ++ii)
|
|
for (int jj = 0; jj < NumVariables(); ++jj)
|
|
jacobian_[ii]->EvalJInPlace<T>(J(ii, jj), vars[jj]);
|
|
break;
|
|
}
|
|
case DerivMethod::Derivatives: {
|
|
for (int jj = 0; jj < NumVariables(); ++jj)
|
|
for (int ii = 0; ii < NumNaturalFunctions(); ++ii)
|
|
space_derivatives_[ii + jj * NumNaturalFunctions()]
|
|
->EvalInPlace<T>(J(ii, jj));
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
} // function tree branch
|
|
|
|
case EvalMethod::SLP: {
|
|
this->slp_.GetJacobianInPlace<T>(
|
|
J); // the variable values should have been copied into place
|
|
// elsewhere. that's not this function's responsibility.
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (IsPatched())
|
|
patch_.JacobianInPlace(J, std::get<Vec<T> >(current_variable_values_));
|
|
}
|
|
|
|
/**
|
|
Evaluate the Jacobian matrix of the system, using the previous space and time
|
|
values.
|
|
|
|
\tparam T the number-type for return. Probably dbl=std::complex<double>, or
|
|
mpfr_complex=bertini::mpfr_complex.
|
|
*/
|
|
template <typename T>
|
|
Mat<T> Jacobian() const {
|
|
Mat<T> J(NumTotalFunctions(), NumVariables());
|
|
JacobianInPlace(J);
|
|
|
|
return J;
|
|
}
|
|
|
|
/**
|
|
Evaluate the Jacobian matrix of the system, provided the system has no path
|
|
variable defined.
|
|
|
|
\throws std::runtime_error, if a path variable IS defined, but you didn't
|
|
pass it a value. Also throws if the number of variables doesn't match.
|
|
\tparam T the number-type for return. Probably dbl=std::complex<double>, or
|
|
mpfr_complex=bertini::mpfr_complex.
|
|
|
|
\param variable_values The values of the variables, for the evaluation.
|
|
*/
|
|
template <typename T>
|
|
void JacobianInPlace(Mat<T>& J, const Vec<T>& variable_values) const {
|
|
if (variable_values.size() != NumVariables())
|
|
throw std::runtime_error(
|
|
"trying to evaluate jacobian, but number of variables doesn't "
|
|
"match.");
|
|
|
|
if (HavePathVariable())
|
|
throw std::runtime_error(
|
|
"not using a time value for computation of jacobian, but a path "
|
|
"variable is defined.");
|
|
|
|
SetAndReset(variable_values);
|
|
|
|
JacobianInPlace(J);
|
|
}
|
|
|
|
/**
|
|
Evaluate the Jacobian matrix of the system, provided the system has no path
|
|
variable defined.
|
|
|
|
\throws std::runtime_error, if a path variable IS defined, but you didn't pass
|
|
it a value. Also throws if the number of variables doesn't match. \tparam T
|
|
the number-type for return. Probably dbl=std::complex<double>, or
|
|
mpfr_complex=bertini::mpfr_complex.
|
|
|
|
\param variable_values The values of the variables, for the evaluation.
|
|
*/
|
|
template <typename T>
|
|
Mat<T> Jacobian(const Vec<T>& variable_values) const {
|
|
if (variable_values.size() != NumVariables())
|
|
throw std::runtime_error(
|
|
"trying to evaluate jacobian, but number of variables doesn't "
|
|
"match.");
|
|
|
|
if (HavePathVariable())
|
|
throw std::runtime_error(
|
|
"not using a time value for computation of jacobian, but a path "
|
|
"variable is defined.");
|
|
|
|
Mat<T> J(NumTotalFunctions(), NumVariables());
|
|
JacobianInPlace(J, variable_values);
|
|
return J;
|
|
}
|
|
|
|
/**
|
|
Evaluate the Jacobian of the system, provided a path variable is defined for
|
|
the system, in place.
|
|
|
|
\throws std::runtime_error, if a path variable is NOT defined, and you passed
|
|
it a value. Also throws if the number of variables doesn't match. \return
|
|
The Jacobian matrix.
|
|
|
|
\param variable_values The values of the variables, for the evaluation.
|
|
\param path_variable_value The current value of the path variable.
|
|
|
|
\tparam T the number-type for return. Probably dbl=std::complex<double>, or
|
|
mpfr_complex=bertini::mpfr_complex.
|
|
*/
|
|
template <typename Derived, typename T>
|
|
void JacobianInPlace(Mat<T>& J,
|
|
const Eigen::MatrixBase<Derived>& variable_values,
|
|
const T& path_variable_value) const {
|
|
static_assert(std::is_same<typename Derived::Scalar, T>::value,
|
|
"scalar types must be the same");
|
|
|
|
if (variable_values.size() != NumVariables())
|
|
throw std::runtime_error(
|
|
"trying to evaluate jacobian, but number of variables doesn't "
|
|
"match.");
|
|
|
|
if (!HavePathVariable())
|
|
throw std::runtime_error(
|
|
"trying to use a time value for computation of jacobian, but no path "
|
|
"variable defined.");
|
|
|
|
SetVariables(variable_values.eval()); // TODO: remove this eval
|
|
SetPathVariable(path_variable_value);
|
|
ResetJacobian();
|
|
JacobianInPlace(J);
|
|
}
|
|
|
|
/**
|
|
Evaluate the Jacobian of the system, provided a path variable is defined for
|
|
the system.
|
|
|
|
\throws std::runtime_error, if a path variable is NOT defined, and you passed
|
|
it a value. Also throws if the number of variables doesn't match. \return
|
|
The Jacobian matrix.
|
|
|
|
\param variable_values The values of the variables, for the evaluation.
|
|
\param path_variable_value The current value of the path variable.
|
|
|
|
\tparam T the number-type for return. Probably dbl=std::complex<double>, or
|
|
mpfr_complex=bertini::mpfr_complex.
|
|
*/
|
|
template <typename Derived, typename T>
|
|
Mat<T> Jacobian(const Eigen::MatrixBase<Derived>& variable_values,
|
|
const T& path_variable_value) const {
|
|
static_assert(std::is_same<typename Derived::Scalar, T>::value,
|
|
"scalar types must be the same");
|
|
|
|
if (variable_values.size() != NumVariables())
|
|
throw std::runtime_error(
|
|
"trying to evaluate jacobian, but number of variables doesn't "
|
|
"match.");
|
|
|
|
if (!HavePathVariable())
|
|
throw std::runtime_error(
|
|
"trying to use a time value for computation of jacobian, but no path "
|
|
"variable defined.");
|
|
|
|
Mat<T> J(NumTotalFunctions(), NumVariables());
|
|
JacobianInPlace(J, variable_values, path_variable_value);
|
|
return J;
|
|
}
|
|
|
|
template <typename T>
|
|
Mat<T> Jacobian(const Vec<T>& variable_values,
|
|
const T& path_variable_value) const {
|
|
if (variable_values.size() != NumVariables())
|
|
throw std::runtime_error(
|
|
"trying to evaluate jacobian, but number of variables doesn't "
|
|
"match.");
|
|
|
|
if (!HavePathVariable())
|
|
throw std::runtime_error(
|
|
"using a time value for computation of jacobian, but no path "
|
|
"variable is defined.");
|
|
|
|
Mat<T> J(NumTotalFunctions(), NumVariables());
|
|
JacobianInPlace(J, variable_values, path_variable_value);
|
|
return J;
|
|
}
|
|
|
|
/**
|
|
\brief Compute the time-derivative of a system.
|
|
|
|
If \f$S\f$ is the system, and \f$t\f$ is the path variable this computes
|
|
\f$\frac{dS}{dt}\f$.
|
|
|
|
\tparam T The number-type for return. Probably dbl=std::complex<double>, or
|
|
mpfr_complex=bertini::mpfr_complex. \throws std::runtime error if the system
|
|
does not have a path variable defined.
|
|
*/
|
|
template <typename Derived, typename T>
|
|
void TimeDerivativeInPlace(Vec<T>& ds_dt,
|
|
const Eigen::MatrixBase<Derived>& variable_values,
|
|
const T& path_variable_value) const {
|
|
static_assert(std::is_same<typename Derived::Scalar, T>::value,
|
|
"scalar types must be the same");
|
|
|
|
SetVariables(variable_values.eval()); // TODO: remove this eval()
|
|
SetPathVariable(path_variable_value);
|
|
ResetTimeDerivatives();
|
|
TimeDerivativeInPlace(ds_dt);
|
|
}
|
|
|
|
/**
|
|
\brief Compute the time-derivative of a system.
|
|
|
|
If \f$S\f$ is the system, and \f$t\f$ is the path variable this computes
|
|
\f$\frac{dS}{dt}\f$.
|
|
|
|
\tparam T The number-type for return. Probably dbl=std::complex<double>, or
|
|
mpfr_complex=bertini::mpfr_complex. \throws std::runtime error if the system
|
|
does not have a path variable defined.
|
|
*/
|
|
template <typename Derived, typename T>
|
|
Vec<T> TimeDerivative(const Eigen::MatrixBase<Derived>& variable_values,
|
|
const T& path_variable_value) const {
|
|
static_assert(std::is_same<typename Derived::Scalar, T>::value,
|
|
"scalar types must be the same");
|
|
|
|
Vec<T> ds_dt(NumTotalFunctions());
|
|
TimeDerivativeInPlace(ds_dt, variable_values, path_variable_value);
|
|
return ds_dt;
|
|
}
|
|
|
|
template <typename Derived, typename T>
|
|
void TimeDerivativeInPlace(
|
|
Vec<T>& ds_dt, const Eigen::MatrixBase<Derived>& variable_values) const {
|
|
static_assert(std::is_same<typename Derived::Scalar, T>::value,
|
|
"scalar types must be the same");
|
|
|
|
SetVariables(variable_values.eval()); // TODO: remove this eval()
|
|
ResetTimeDerivatives();
|
|
TimeDerivativeInPlace(ds_dt);
|
|
}
|
|
|
|
/**
|
|
\brief Compute the time-derivative of a system.
|
|
|
|
If \f$S\f$ is the system, and \f$t\f$ is the path variable this computes
|
|
\f$\frac{dS}{dt}\f$.
|
|
|
|
\tparam T The number-type for return. Probably dbl=std::complex<double>, or
|
|
mpfr_complex=bertini::mpfr_complex. \throws std::runtime error if the system
|
|
does not have a path variable defined.
|
|
*/
|
|
template <typename Derived, typename T>
|
|
Vec<T> TimeDerivative(
|
|
const Eigen::MatrixBase<Derived>& variable_values) const {
|
|
static_assert(std::is_same<typename Derived::Scalar, T>::value,
|
|
"scalar types must be the same");
|
|
|
|
Vec<T> ds_dt(NumTotalFunctions());
|
|
TimeDerivativeInPlace(ds_dt, variable_values);
|
|
return ds_dt;
|
|
}
|
|
|
|
template <typename T>
|
|
void TimeDerivativeInPlace(Vec<T>& ds_dt) const {
|
|
if (ds_dt.size() < NumNaturalFunctions()) {
|
|
std::stringstream ss;
|
|
ss << "trying to evaluate system in place, but number of input functions "
|
|
"("
|
|
<< ds_dt.size() << ") doesn't match number of system functions ("
|
|
<< NumNaturalFunctions() << ").";
|
|
throw std::runtime_error(ss.str());
|
|
}
|
|
|
|
if (!HavePathVariable())
|
|
throw std::runtime_error(
|
|
"computing time derivative of system with no path variable defined");
|
|
|
|
if (!is_differentiated_) Differentiate();
|
|
|
|
switch (eval_method_) {
|
|
case EvalMethod::FunctionTree: {
|
|
switch (deriv_method_) {
|
|
case DerivMethod::JacobianNode: {
|
|
for (int ii = 0; ii < NumNaturalFunctions(); ++ii)
|
|
jacobian_[ii]->EvalJInPlace<T>(ds_dt(ii), path_variable_);
|
|
break;
|
|
}
|
|
case DerivMethod::Derivatives: {
|
|
for (int ii = 0; ii < NumNaturalFunctions(); ++ii)
|
|
time_derivatives_[ii]->EvalInPlace<T>(ds_dt(ii));
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
} // function tree branch
|
|
|
|
case EvalMethod::SLP: {
|
|
this->slp_.GetTimeDerivInPlace(
|
|
ds_dt); // the variable values should have been copied into place
|
|
// elsewhere. that's not this function's responsibility.
|
|
break;
|
|
}
|
|
}
|
|
|
|
// the patch doesn't move with time. derivatives 0.
|
|
if (IsPatched())
|
|
for (int ii = 0; ii < NumTotalVariableGroups(); ++ii)
|
|
ds_dt(ii + NumNaturalFunctions()) = T(0);
|
|
}
|
|
|
|
/**
|
|
\brief Compute the time-derivative of a system.
|
|
|
|
If \f$S\f$ is the system, and \f$t\f$ is the path variable this computes
|
|
\f$\frac{dS}{dt}\f$.
|
|
|
|
\tparam T The number-type for return. Probably dbl=std::complex<double>, or
|
|
mpfr_complex=bertini::mpfr_complex. \throws std::runtime error if the system
|
|
does not have a path variable defined.
|
|
*/
|
|
template <typename T>
|
|
Vec<T> TimeDerivative() const {
|
|
if (!HavePathVariable())
|
|
throw std::runtime_error(
|
|
"computing time derivative of system with no path variable defined");
|
|
|
|
Vec<T> ds_dt(NumTotalFunctions());
|
|
TimeDerivativeInPlace(ds_dt);
|
|
return ds_dt;
|
|
}
|
|
|
|
/**
|
|
Homogenize the system, adding new homogenizing variables for each
|
|
VariableGroup defined for the system.
|
|
|
|
\throws std::runtime_error, if the system is not polynomial, has a mismatch on
|
|
the number of homogenizing variables and the number of variable groups (this
|
|
would result from a partially homogenized system), or the homogenizing
|
|
variable names somehow get screwed up by having duplicates.
|
|
*/
|
|
void Homogenize();
|
|
|
|
/**
|
|
Checks whether a system is homogeneous, overall. This means with respect to
|
|
each variable group (including homogenizing variable if defined), homogeneous
|
|
variable group, and ungrouped variables, if defined.
|
|
|
|
\throws std::runtime_error, if the number of homogenizing variables does not
|
|
match the number of variable_groups. \return true if homogeneous, false if not
|
|
*/
|
|
bool IsHomogeneous() const;
|
|
|
|
/**
|
|
Checks whether a system is polynomial, overall. This means with respect to
|
|
each variable group (including homogenizing variable if defined), homogeneous
|
|
variable group, and ungrouped variables, if defined.
|
|
|
|
\throws std::runtime_error, if the number of homogenizing variables does not
|
|
match the number of variable_groups. \return true if polynomial, false if not.
|
|
*/
|
|
bool IsPolynomial() const;
|
|
|
|
//////////////////
|
|
//
|
|
// Nummers -- functions which get the numbers of things.
|
|
//
|
|
//////////////////
|
|
|
|
/**
|
|
Get the number of functions in this system, excluding patches.
|
|
*/
|
|
size_t NumNaturalFunctions() const;
|
|
|
|
/**
|
|
Get the number of patches in this system.
|
|
*/
|
|
size_t NumPatches() const { return patch_.NumVariableGroups(); }
|
|
|
|
/**
|
|
Get the total number of functions, including patches
|
|
*/
|
|
size_t NumTotalFunctions() const;
|
|
|
|
/**
|
|
Get the total number of variables in this system, including homogenizing
|
|
variables.
|
|
*/
|
|
size_t NumVariables() const;
|
|
|
|
/**
|
|
Get the number of variables in this system, NOT including homogenizing
|
|
variables.
|
|
*/
|
|
size_t NumNaturalVariables() const;
|
|
|
|
/**
|
|
Get the number of *homogenizing* variables in this system
|
|
*/
|
|
size_t NumHomVariables() const;
|
|
|
|
/**
|
|
Get the total number of variable groups in the system, including both affine
|
|
and homogenous. Ignores the ungrouped variables, because they are not in any
|
|
group.
|
|
*/
|
|
size_t NumTotalVariableGroups() const;
|
|
|
|
/**
|
|
Get the number of affine variable groups in the system
|
|
*/
|
|
size_t NumVariableGroups() const;
|
|
|
|
/**
|
|
get the number of variables which are ungrouped.
|
|
*/
|
|
size_t NumUngroupedVariables() const;
|
|
|
|
/**
|
|
Get the number of homogeneous variable groups in the system
|
|
*/
|
|
size_t NumHomVariableGroups() const;
|
|
|
|
/**
|
|
Get the number of constants in this system
|
|
*/
|
|
size_t NumConstants() const;
|
|
|
|
/**
|
|
Get the number of explicit parameters in this system
|
|
*/
|
|
size_t NumParameters() const;
|
|
|
|
/**
|
|
Get the number of implicit parameters in this system
|
|
*/
|
|
size_t NumImplicitParameters() const;
|
|
|
|
///////////////////
|
|
//
|
|
// Setters -- templated.
|
|
//
|
|
///////////////
|
|
|
|
/**
|
|
Set the values of the variables to be equal to the input values
|
|
|
|
\tparam T the number-type for return. Probably dbl=std::complex<double>, or
|
|
mpfr_complex=bertini::mpfr_complex. \throws std::runtime_error if the number
|
|
of variables doesn't match.
|
|
|
|
The ordering of the variables matters.
|
|
|
|
* The AffHomUng ordering is 1) variable groups, with homogenizing variable
|
|
first. 2) homogeneous variable groups. 3) ungrouped variables.
|
|
* The FIFO ordering uses the order in which the variable groups were added.
|
|
|
|
The path variable is not considered a variable for this operation. It is set
|
|
separately.
|
|
|
|
\param new_values The new updated values for the variables.
|
|
|
|
\see SetPathVariable
|
|
\see Variables
|
|
*/
|
|
template <typename T>
|
|
void SetVariables(const Vec<T>& new_values) const {
|
|
if (new_values.size() != NumVariables())
|
|
throw std::runtime_error(
|
|
"variable vector of different length from system-owned variables in "
|
|
"SetVariables");
|
|
|
|
const auto& vars = Variables();
|
|
|
|
#ifndef BERTINI_DISABLE_PRECISION_CHECKS
|
|
if (!std::is_same<T, dbl>::value &&
|
|
(Precision(new_values) != this->precision()))
|
|
throw std::runtime_error("precision of input point in SetVariables (" +
|
|
std::to_string(Precision(new_values)) +
|
|
") must match the precision of the system (" +
|
|
std::to_string(this->precision()) + ").");
|
|
|
|
if (!std::is_same<T, dbl>::value &&
|
|
(vars[0]->node::NamedSymbol::precision() != this->precision()))
|
|
throw std::runtime_error(
|
|
"internally, precision of variables (" +
|
|
std::to_string(vars[0]->node::NamedSymbol::precision()) +
|
|
") in SetVariables must match the precision of the system (" +
|
|
std::to_string(this->precision()) + ").");
|
|
#endif
|
|
|
|
if (!is_differentiated_) Differentiate();
|
|
|
|
switch (eval_method_) {
|
|
case EvalMethod::FunctionTree: {
|
|
auto counter = 0;
|
|
|
|
for (auto iter = vars.begin(); iter != vars.end(); iter++, counter++) {
|
|
(*iter)->set_current_value(new_values(counter));
|
|
}
|
|
|
|
std::get<Vec<T> >(current_variable_values_) = new_values;
|
|
break;
|
|
}
|
|
case EvalMethod::SLP: {
|
|
std::get<Vec<T> >(current_variable_values_) =
|
|
new_values; // if this isn't here, then patch evaluation breaks.
|
|
slp_.SetVariableValues(new_values);
|
|
break;
|
|
}
|
|
} // switch
|
|
}
|
|
|
|
/**
|
|
Set the current value of the path variable.
|
|
|
|
\throws std::runtime_error, if a path variable is not defined.
|
|
\tparam T the number-type for return. Probably dbl=std::complex<double>, or
|
|
mpfr_complex=bertini::mpfr_complex.
|
|
|
|
\param new_value The new updated values for the path variable.
|
|
*/
|
|
template <typename T>
|
|
void SetPathVariable(T const& new_value) const {
|
|
if (!have_path_variable_)
|
|
throw std::runtime_error(
|
|
"trying to set the value of the path variable, but one is not "
|
|
"defined for this system");
|
|
|
|
if (!is_differentiated_) Differentiate();
|
|
|
|
switch (eval_method_) {
|
|
case EvalMethod::FunctionTree: {
|
|
path_variable_->set_current_value(new_value);
|
|
break;
|
|
}
|
|
case EvalMethod::SLP: {
|
|
path_variable_->set_current_value(new_value);
|
|
slp_.SetPathVariable(new_value);
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
void SetAndReset(Vec<T> const& new_space, T const& new_time) const {
|
|
SetVariables(new_space);
|
|
SetPathVariable(new_time);
|
|
|
|
Reset();
|
|
}
|
|
|
|
template <typename T>
|
|
void SetAndReset(Vec<T> const& new_space) const {
|
|
SetVariables(new_space);
|
|
|
|
Reset();
|
|
}
|
|
|
|
/**
|
|
For a system with implicitly defined parameters, set their values. The
|
|
values are determined externally to the system, and are tracked along with
|
|
the variables. \tparam T the number-type for return. Probably
|
|
dbl=std::complex<double>, or mpfr_complex=bertini::mpfr_complex. \param
|
|
new_values The new updated values for the implicit parameters.
|
|
*/
|
|
template <typename T>
|
|
void SetImplicitParameters(Vec<T> new_values) const {
|
|
if (new_values.size() != implicit_parameters_.size())
|
|
throw std::runtime_error(
|
|
"trying to set implicit parameter values, but there is a size "
|
|
"mismatch");
|
|
|
|
size_t counter = 0;
|
|
for (auto iter = implicit_parameters_.begin();
|
|
iter != implicit_parameters_.end(); iter++, counter++)
|
|
(*iter)->set_current_value(new_values(counter));
|
|
}
|
|
|
|
//////////////////
|
|
//
|
|
// Adders -- functions which add things to the system.
|
|
//
|
|
//////////////////
|
|
|
|
/**
|
|
Add a variable group to the system. The system may be homogenized with
|
|
respect to this variable group, though this is not done at the time of this
|
|
call.
|
|
|
|
\param v The variable group to add.
|
|
*/
|
|
void AddVariableGroup(VariableGroup const& v);
|
|
|
|
/**
|
|
Add a homogeneous (projective) variable group to the system. The system must
|
|
be homogeneous with respect to this group, though this is not verified at the
|
|
time of this call.
|
|
|
|
\param v The variable group to add.
|
|
*/
|
|
void AddHomVariableGroup(VariableGroup const& v);
|
|
|
|
/**
|
|
Add variables to the system which are in neither a regular variable group,
|
|
nor in a homogeneous group. This is likely used for user-defined systems,
|
|
Classic userhomotopy: 1;.
|
|
|
|
\param v The variable to add.
|
|
*/
|
|
void AddUngroupedVariable(Var const& v);
|
|
|
|
/**
|
|
Add variables to the system which are in neither a regular variable group,
|
|
nor in a homogeneous group. This is likely used for user-defined systems,
|
|
Classic userhomotopy: 1;.
|
|
|
|
\param v The variables to add.
|
|
*/
|
|
void AddUngroupedVariables(VariableGroup const& v);
|
|
|
|
/**
|
|
Add an implicit parameter to the system. Implicit parameters are advanced by
|
|
the tracker akin to variable advancement.
|
|
|
|
\param v The implicit parameter to add.
|
|
*/
|
|
void AddImplicitParameter(Var const& v);
|
|
|
|
/**
|
|
Add some implicit parameters to the system. Implicit parameters are advanced
|
|
by the tracker akin to variable advancement.
|
|
|
|
\param v The implicit parameters to add.
|
|
*/
|
|
void AddImplicitParameters(VariableGroup const& v);
|
|
|
|
/**
|
|
Add an explicit parameter to the system. Explicit parameters should depend
|
|
only on the path variable, though this is not checked in this function.
|
|
|
|
\param F The parameter to add.
|
|
*/
|
|
void AddParameter(Fn const& F);
|
|
|
|
/**
|
|
Add some explicit parameters to the system. Explicit parameters should
|
|
depend only on the path variable, though this is not checked in this
|
|
function.
|
|
|
|
\param F The parameters to add.
|
|
*/
|
|
void AddParameters(std::vector<Fn> const& F);
|
|
|
|
/**
|
|
Add a subfunction to the system.
|
|
|
|
Tacks them onto the end of the system.
|
|
|
|
\param F The subfunction to add.
|
|
*/
|
|
void AddSubfunction(Fn const& F);
|
|
|
|
/**
|
|
Add some subfunctions to the system.
|
|
|
|
Tacks them onto the end of the system.
|
|
|
|
\param F The subfunctions to add.
|
|
*/
|
|
void AddSubfunctions(std::vector<Fn> const& F);
|
|
|
|
/**
|
|
Add a function to the system.
|
|
|
|
\param F The function to add.
|
|
*/
|
|
void AddFunction(Fn const& F);
|
|
|
|
/**
|
|
Add a function to the system.
|
|
|
|
\param F The function to add.
|
|
\param name The name of the function
|
|
*/
|
|
void AddFunction(Nd const& F, std::string const& name = "unnamed_function");
|
|
|
|
/**
|
|
Add some functions to the system.
|
|
|
|
\param F The functions to add.
|
|
*/
|
|
void AddFunctions(std::vector<Fn> const& F);
|
|
|
|
/**
|
|
Add a constant function to the system. Constants must not depend on anything
|
|
which can vary -- they're constant!
|
|
|
|
\param C The constant to add.
|
|
*/
|
|
void AddConstant(Fn const& C);
|
|
|
|
/**
|
|
Add some constant functions to the system. Constants must not depend on
|
|
anything which can vary -- they're constant!
|
|
|
|
\param C The constants to add.
|
|
*/
|
|
void AddConstants(std::vector<Fn> const& C);
|
|
|
|
/**
|
|
Add a variable as the Path Variable to a System. Will overwrite any
|
|
previously declared path variable.
|
|
|
|
\param v The new path variable.
|
|
*/
|
|
void AddPathVariable(Var const& v);
|
|
|
|
/**
|
|
Query whether a path variable is set for this system
|
|
|
|
\return true if have a path variable, false if not.
|
|
*/
|
|
bool HavePathVariable() const;
|
|
|
|
auto& GetPathVariable() const {
|
|
if (this->HavePathVariable())
|
|
return this->path_variable_;
|
|
else
|
|
throw std::runtime_error(
|
|
"trying to get path variable for a system which doesn't have a path "
|
|
"variable defined");
|
|
}
|
|
|
|
/**
|
|
Order the variables, by the order in which the groups were added.
|
|
|
|
This function returns the variables in First In First Out (FIFO) ordering.
|
|
|
|
Homogenizing variables precede affine variable groups, so that groups always
|
|
are grouped together.
|
|
|
|
This function freshly constructs the ordering every time. If you want to use
|
|
the cached value, \see Variables.
|
|
|
|
\throws std::runtime_error, if there is a mismatch between the number of
|
|
homogenizing variables and the number of variable_groups. This would happen
|
|
if a system is homogenized, and then more stuff is added to it.
|
|
*/
|
|
VariableGroup VariableOrdering() const;
|
|
|
|
/**
|
|
Get the variables in the problem.
|
|
|
|
Returns the variable ordering, constructing it if necessary.
|
|
*/
|
|
const VariableGroup& Variables() const;
|
|
|
|
/**
|
|
\brief Get an affine variable group the class has defined.
|
|
|
|
It is up to you to ensure this group exists.
|
|
*/
|
|
VariableGroup const& AffineVariableGroup(size_t index) const {
|
|
return variable_groups_[index];
|
|
}
|
|
/**
|
|
\brief Get the sizes of the variable groups, according to the current ordering
|
|
*/
|
|
std::vector<unsigned> VariableGroupSizes() const {
|
|
return VariableGroupSizesFIFO();
|
|
}
|
|
|
|
/**
|
|
\brief Dehomogenize a point, using the variable grouping / structure of the
|
|
system.
|
|
|
|
\tparam T the number-type for return. Probably dbl=std::complex<double>, or
|
|
mpfr_complex=bertini::mpfr_complex.
|
|
|
|
\throws std::runtime_error, if there is a mismatch between the number of
|
|
variables in the input point, and the total number of var
|
|
*/
|
|
template <typename T>
|
|
Vec<T> DehomogenizePoint(Vec<T> const& x) const {
|
|
if (x.size() != NumVariables()) {
|
|
std::stringstream message;
|
|
message << "dehomogenizing point with incorrect number of coordinates. "
|
|
"input has ";
|
|
message << x.size();
|
|
message << " but system expects ";
|
|
message << NumVariables();
|
|
throw std::runtime_error(message.str());
|
|
}
|
|
|
|
if (!have_ordering_) ConstructOrdering();
|
|
|
|
return DehomogenizePointFIFO(x);
|
|
}
|
|
|
|
/**
|
|
\brief Get a function by its index.
|
|
|
|
This is just as scary as you think it is. It is up to you to make sure the
|
|
function at this index exists.
|
|
*/
|
|
auto Function(unsigned index) const { return functions_[index]; }
|
|
|
|
/**
|
|
\brief Get the functions.
|
|
*/
|
|
auto GetNaturalFunctions() const { return functions_; }
|
|
|
|
/**
|
|
Get the affine variable groups in the problem.
|
|
*/
|
|
auto VariableGroups() const { return variable_groups_; }
|
|
|
|
/**
|
|
Get the homogeneous (projective) variable groups in the problem.
|
|
*/
|
|
auto HomVariableGroups() const { return hom_variable_groups_; }
|
|
|
|
const Var& PathVariable() const { return path_variable_; }
|
|
|
|
/**
|
|
\brief Get the space derivatives
|
|
|
|
These are as computed using the Derivatives method, not Jacobian nodes. They
|
|
are stored in column-major order, so stride over variables first.
|
|
|
|
```
|
|
for (int jj = 0; jj < num_vars; ++jj)
|
|
for (int ii = 0; ii < num_functions; ++ii)
|
|
space_derivatives_[ii+jj*num_functions] =
|
|
functions_[ii]->Differentiate(vars[jj]);
|
|
```
|
|
*/
|
|
std::vector<Nd> GetSpaceDerivatives() const;
|
|
|
|
/**
|
|
\brief Get the time derivatives of all functions
|
|
|
|
These are as computed using the Derivatives method, not Jacobian nodes.
|
|
Stored in order of functions.
|
|
*/
|
|
std::vector<Nd> GetTimeDerivatives() const;
|
|
|
|
//////////////////////
|
|
//
|
|
// Functions involving coefficients of the system
|
|
//
|
|
///////////////////////
|
|
|
|
/**
|
|
Compute an estimate of an upper bound of the absolute values of the
|
|
coefficients in the system.
|
|
|
|
\param num_evaluations The number of times to compute this estimate. Default
|
|
is 1. \returns An upper bound on the absolute values of the coefficients.
|
|
*/
|
|
template <typename NumT>
|
|
typename Eigen::NumTraits<NumT>::Real CoefficientBound(
|
|
unsigned num_evaluations = 1) const;
|
|
|
|
/**
|
|
\brief Compute an upper bound on the degree of the system.
|
|
|
|
This number will be wrong if the system is non-polynomial, because degree for
|
|
non-polynomial systems is not defined.
|
|
*/
|
|
int DegreeBound() const;
|
|
|
|
/**
|
|
\brief Get the degrees of the functions in the system, with respect to all
|
|
variables.
|
|
|
|
\return A vector containing the degrees of the functions. Negative numbers
|
|
indicate the function is non-polynomial.
|
|
*/
|
|
std::vector<int> Degrees() const;
|
|
|
|
/**
|
|
\brief Get the degrees of the functions in the system, with respect to a group
|
|
of variables.
|
|
|
|
\return A vector containing the degrees of the functions. Negative numbers
|
|
indicate the function is non-polynomial. \param vars A group of variables with
|
|
respect to which you wish to compute degrees. Needs not be a group with
|
|
respect to the system.
|
|
*/
|
|
std::vector<int> Degrees(VariableGroup const& vars) const;
|
|
|
|
/**
|
|
\brief Sort the functions so they are in DEcreasing order by degree
|
|
*/
|
|
void ReorderFunctionsByDegreeDecreasing();
|
|
|
|
/**
|
|
\brief Sort the functions so they are in INcreasing order by degree
|
|
*/
|
|
void ReorderFunctionsByDegreeIncreasing();
|
|
|
|
/////////////
|
|
//
|
|
// Functions regarding patches.
|
|
//
|
|
//////////////
|
|
|
|
/**
|
|
\brief Let the system patch itself by the selected variable ordering, using
|
|
the current variable groups.
|
|
|
|
Homogeneous variable groups and affine variable groups will be supplied a
|
|
patch equation. Ungrouped variables will not.
|
|
|
|
\todo Add example code for how to use this function.
|
|
*/
|
|
void AutoPatch() { AutoPatchFIFO(); }
|
|
|
|
/**
|
|
\brief Copy the patches from another system into this one.
|
|
*/
|
|
void CopyPatches(System const& other);
|
|
|
|
Patch GetPatch() const { return patch_; }
|
|
|
|
/**
|
|
\brief Query whether a system is patched.
|
|
*/
|
|
bool IsPatched() const { return is_patched_; }
|
|
|
|
template <typename T>
|
|
Vec<T> RescalePointToFitPatch(Vec<T> const& x) const {
|
|
return patch_.RescalePoint(x);
|
|
}
|
|
|
|
template <typename T>
|
|
void RescalePointToFitPatchInPlace(Vec<T>& x) const {
|
|
patch_.RescalePointToFitInPlace(x);
|
|
}
|
|
/**
|
|
\brief Overloaded operator for printing to an arbirtary out stream.
|
|
*/
|
|
friend std::ostream& operator<<(std::ostream& out, const System& s);
|
|
|
|
/**
|
|
\brief Clear the entire structure of variables in a system. Reconstructing
|
|
it is up to you.
|
|
*/
|
|
void ClearVariables();
|
|
|
|
/**
|
|
\brief Copy the entire structure of variables from within one system to
|
|
another.
|
|
|
|
This copies everything -- ungrouped variables, variable groups, homogenizing
|
|
variables, the path variable, the ordering of the variables.
|
|
|
|
\param other Another system from which to copy the variable structure.
|
|
|
|
This operation does NOT affect the functions in any way. It is up to the
|
|
user to make the functions actually depend on these variables.
|
|
*/
|
|
void CopyVariableStructure(System const& other);
|
|
|
|
/**
|
|
\brief One of a family of functions indicating whether we can assume the
|
|
system will always have uniform precision.
|
|
|
|
\see PleaseAssumeUniformPrecision AssumeUniformPrecision
|
|
IsAssumingUniformPrecision
|
|
*/
|
|
void DontAssumeUniformPrecision() { AssumeUniformPrecision(false); }
|
|
|
|
void PleaseAssumeUniformPrecision() { AssumeUniformPrecision(true); }
|
|
|
|
void AssumeUniformPrecision(bool val) { assume_uniform_precision_ = false; }
|
|
|
|
/**
|
|
\brief yon getter for the obvious thing it gets
|
|
*/
|
|
auto IsAssumingUniformPrecision() const { return assume_uniform_precision_; }
|
|
|
|
inline void PleaseAutoSimplify() { SetAutoSimplify(true); }
|
|
|
|
inline void DontAutoSimplify() { SetAutoSimplify(false); }
|
|
|
|
void SetAutoSimplify(bool val) { auto_simplify_ = val; }
|
|
|
|
/**
|
|
\brief Query the state of autosimplification
|
|
*/
|
|
auto IsAutoSimplifying() const { return auto_simplify_; }
|
|
|
|
/**
|
|
\brief Simplify the functions contained in the system.
|
|
|
|
\note This may change any nodes on which the system depends.
|
|
*/
|
|
void SimplifyFunctions();
|
|
|
|
/**
|
|
\brief Simplify the derivatives / jacobian / etc contained in the system.
|
|
|
|
\note This may change any nodes on which the system depends.
|
|
*/
|
|
void SimplifyDerivatives() const;
|
|
|
|
/**
|
|
\brief Simplify as many aspects of the system as possible.
|
|
|
|
\note This may change any nodes on which the system depends.
|
|
*/
|
|
void Simplify();
|
|
|
|
/**
|
|
\brief Set method being used for evaluation
|
|
* */
|
|
void SetEvalMethod(EvalMethod method) { eval_method_ = method; }
|
|
|
|
/**
|
|
\brief Query the current method used for evaluation
|
|
* */
|
|
EvalMethod GetEvalMethod() const { return eval_method_; }
|
|
|
|
/**
|
|
\brief Set method being used for differentiation
|
|
* */
|
|
void SetDerivMethod(DerivMethod method) { deriv_method_ = method; }
|
|
|
|
/**
|
|
\brief Query the current method used for differentiation
|
|
* */
|
|
DerivMethod GetDerivMethod() const { return deriv_method_; }
|
|
|
|
/**
|
|
\brief Add two systems together.
|
|
|
|
\throws std::runtime_error, if the systems are not of compatible size --
|
|
either in number of functions, or variables. Does not check the structure of
|
|
the variables, just the numbers.
|
|
|
|
\throws std::runtime_error, if the patches are not compatible. The patches
|
|
must be either the same, absent, or present in one system. They propagate to
|
|
the resulting system.
|
|
*/
|
|
System& operator+=(System const& rhs);
|
|
|
|
/**
|
|
\brief Add two systems together.
|
|
|
|
\throws std::runtime_error, if the systems are not of compatible size --
|
|
either in number of functions, or variables. Does not check the structure of
|
|
the variables, just the numbers.
|
|
|
|
\see The += operator for System also.
|
|
*/
|
|
friend const System operator+(System lhs, System const& rhs);
|
|
|
|
/**
|
|
\brief Multiply a system by an arbitrary node.
|
|
|
|
Can be used for defining a coupling of a target and start system through a
|
|
path variable. Does not affect path variable declaration, or anything else.
|
|
It is up to you to ensure the system depends on this node properly.
|
|
*/
|
|
System& operator*=(Nd const& N);
|
|
|
|
/**
|
|
\brief Multiply a system by an arbitrary node.
|
|
|
|
Can be used for defining a coupling of a target and start system through a
|
|
path variable. Does not affect path variable declaration, or anything else.
|
|
It is up to you to ensure the system depends on this node properly.
|
|
*/
|
|
friend const System operator*(System s, Nd const& N);
|
|
|
|
/**
|
|
\brief Multiply a system by an arbitrary node.
|
|
|
|
Can be used for defining a coupling of a target and start system through a
|
|
path variable. Does not affect path variable declaration, or anything else.
|
|
It is up to you to ensure the system depends on this node properly.
|
|
*/
|
|
friend const System operator*(Nd const& N, System const& s);
|
|
|
|
private:
|
|
/**
|
|
\brief Get the sizes according to the FIFO ordering.
|
|
*/
|
|
std::vector<unsigned> VariableGroupSizesFIFO() const;
|
|
|
|
/**
|
|
\brief Set up patches automatically for a system using the FIFO ordering.
|
|
*/
|
|
void AutoPatchFIFO();
|
|
|
|
/**
|
|
\brief Dehomogenize a point according to the FIFO variable ordering.
|
|
|
|
\tparam T the number-type for return. Probably dbl=std::complex<double>, or
|
|
mpfr_complex=bertini::mpfr_complex.
|
|
|
|
\see FIFOVariableOrdering
|
|
*/
|
|
template <typename T>
|
|
Vec<T> DehomogenizePointFIFO(Vec<T> const& x) const {
|
|
#ifndef BERTINI_DISABLE_ASSERTS
|
|
assert(homogenizing_variables_.size() == 0 ||
|
|
homogenizing_variables_.size() == NumVariableGroups() &&
|
|
"must have either 0 homogenizing variables, or the number of "
|
|
"homogenizing variables must match the number of affine "
|
|
"variable groups.");
|
|
#endif
|
|
|
|
bool is_homogenized = homogenizing_variables_.size() != 0;
|
|
Vec<T> x_dehomogenized(NumNaturalVariables());
|
|
|
|
unsigned affine_group_counter = 0;
|
|
unsigned hom_group_counter = 0;
|
|
unsigned ungrouped_variable_counter = 0;
|
|
|
|
unsigned hom_index = 0; // index into x, the point we are dehomogenizing
|
|
unsigned dehom_index =
|
|
0; // index into x_dehomogenized, the point we are computing
|
|
|
|
for (auto& iter : time_order_of_variable_groups_) {
|
|
switch (iter) {
|
|
case VariableGroupType::Affine: {
|
|
if (is_homogenized) {
|
|
auto h = x(hom_index++);
|
|
for (unsigned ii = 0;
|
|
ii < variable_groups_[affine_group_counter].size(); ++ii)
|
|
x_dehomogenized(dehom_index++) = x(hom_index++) / h;
|
|
affine_group_counter++;
|
|
} else {
|
|
for (unsigned ii = 0;
|
|
ii < variable_groups_[affine_group_counter].size(); ++ii)
|
|
x_dehomogenized(dehom_index++) = x(hom_index++);
|
|
}
|
|
break;
|
|
}
|
|
case VariableGroupType::Homogeneous: {
|
|
for (unsigned ii = 0;
|
|
ii < hom_variable_groups_[hom_group_counter].size(); ++ii)
|
|
x_dehomogenized(dehom_index++) = x(hom_index++);
|
|
break;
|
|
}
|
|
case VariableGroupType::Ungrouped: {
|
|
x_dehomogenized(dehom_index++) = x(hom_index++);
|
|
ungrouped_variable_counter++;
|
|
break;
|
|
}
|
|
default: {
|
|
throw std::runtime_error(
|
|
"unacceptable VariableGroupType in FIFOVariableOrdering");
|
|
}
|
|
}
|
|
}
|
|
|
|
return x_dehomogenized;
|
|
}
|
|
|
|
void DifferentiateUsingDerivatives() const;
|
|
void DifferentiateUsingJacobianNode() const;
|
|
|
|
/**
|
|
Puts together the ordering of variables, and stores it internally.
|
|
*/
|
|
void ConstructOrdering() const;
|
|
|
|
VariableGroup
|
|
ungrouped_variables_; ///< ungrouped variable nodes. Not in an affine
|
|
///< variable group, not in a projective group.
|
|
///< Just hanging out, being a variable.
|
|
std::vector<VariableGroup>
|
|
variable_groups_; ///< Affine variable groups. When system is
|
|
///< homogenized, will have a corresponding
|
|
///< homogenizing variable.
|
|
std::vector<VariableGroup>
|
|
hom_variable_groups_; ///< Homogeneous or projective variable groups.
|
|
///< System SHOULD be homogeneous with respect to
|
|
///< these.
|
|
|
|
VariableGroup homogenizing_variables_; ///< homogenizing variables for the
|
|
///< variable_groups.
|
|
|
|
bool have_path_variable_ = false; ///< Whether we have the variable or not.
|
|
Var path_variable_; ///< the single path variable for this system. Sometimes
|
|
///< called time.
|
|
|
|
VariableGroup
|
|
implicit_parameters_; ///< Implicit parameters. These don't depend on
|
|
///< anything, and will be moved from one parameter
|
|
///< point to another by the tracker. They should
|
|
///< be algebraically constrained by some
|
|
///< equations.
|
|
std::vector<Fn> explicit_parameters_; ///< Explicit parameters. These should
|
|
///< be functions of the path variable
|
|
///< only, NOT of other variables.
|
|
|
|
std::vector<Fn>
|
|
constant_subfunctions_; ///< degree-0 functions, depending on neither
|
|
///< variables nor the path variable.
|
|
std::vector<Fn>
|
|
subfunctions_; ///< Any declared subfunctions for the system. Can use
|
|
///< these to ensure that complicated repeated structures
|
|
///< are only created and evaluated once.
|
|
std::vector<Fn> functions_; ///< The system's functions.
|
|
|
|
class Patch patch_; ///< Patch on the variable groups. Assumed to be in the
|
|
///< same order as the time_order_of_variable_groups_ if
|
|
///< the system uses FIFO ordering, or in same order as
|
|
///< the AffHomUng variable groups if that is set.
|
|
bool is_patched_ =
|
|
false; ///< Indicator of whether the system has been patched.
|
|
|
|
mutable std::vector<Jac>
|
|
jacobian_; ///< The generated functions from differentiation. Created
|
|
///< when first call for a Jacobian matrix evaluation.
|
|
|
|
mutable std::vector<Nd>
|
|
space_derivatives_; ///< The generated functions from differentiation
|
|
///< with respect to space. in column-major order to
|
|
///< be consistent with Eigen default order. Created
|
|
///< when first call for a Jacobian matrix
|
|
///< evaluation.
|
|
|
|
mutable std::vector<Nd>
|
|
time_derivatives_; ///< The generated functions from differentiation with
|
|
///< respect to time. in column-major order to be
|
|
///< consistent with Eigen default order. Created
|
|
///< when first call for a Jacobian matrix evaluation.
|
|
|
|
mutable bool is_differentiated_ =
|
|
false; ///< indicator for whether the jacobian tree has been populated.
|
|
|
|
mutable StraightLineProgram
|
|
slp_; ///< The straight line program. Is mutable since it's a has-a,
|
|
///< not is-a relationship.
|
|
|
|
std::vector<VariableGroupType> time_order_of_variable_groups_;
|
|
|
|
mutable std::tuple<Vec<dbl>, Vec<mpfr_complex> > current_variable_values_;
|
|
|
|
mutable VariableGroup variable_ordering_; ///< The assembled ordering of the
|
|
///< variables in the system.
|
|
mutable bool have_ordering_ = false;
|
|
|
|
mutable unsigned precision_; ///< the current working precision of the system
|
|
|
|
bool assume_uniform_precision_ =
|
|
false; ///< a bit, setting whether we can assume the system is in uniform
|
|
///< precision. if you are doing things that will allow pieces of
|
|
///< the system to drift in terms of precision, then you should
|
|
///< not assume this. \see AssumeUniformPrecision
|
|
|
|
EvalMethod eval_method_ =
|
|
DefaultEvalMethod(); ///< an enum class value, indicating which method of
|
|
///< evaluation should be used.
|
|
DerivMethod deriv_method_ =
|
|
DefaultDerivMethod(); ///< an enum class value, indicating which method
|
|
///< of evaluation should be used.
|
|
|
|
bool auto_simplify_ = DefaultAutoSimplify();
|
|
|
|
friend class boost::serialization::access;
|
|
|
|
/*definition of serialize function*/
|
|
template <class Archive>
|
|
void serialize(Archive& ar, const unsigned int version) {
|
|
ar & ungrouped_variables_;
|
|
ar & variable_groups_;
|
|
ar & hom_variable_groups_;
|
|
|
|
ar & homogenizing_variables_;
|
|
|
|
ar & have_path_variable_;
|
|
ar & path_variable_;
|
|
|
|
ar & implicit_parameters_;
|
|
ar & explicit_parameters_;
|
|
|
|
ar & constant_subfunctions_;
|
|
ar & subfunctions_;
|
|
ar & functions_;
|
|
|
|
ar & patch_;
|
|
ar & is_patched_;
|
|
|
|
ar & assume_uniform_precision_;
|
|
|
|
ar & eval_method_;
|
|
ar & deriv_method_;
|
|
|
|
ar & auto_simplify_;
|
|
|
|
// now for the cached / mutable things
|
|
ar & precision_;
|
|
|
|
// if (Archive::is_loading::value == true){
|
|
// is_differentiated_ = false;}
|
|
// // else
|
|
// // {
|
|
ar & is_differentiated_;
|
|
ar & jacobian_;
|
|
ar & space_derivatives_;
|
|
ar & time_derivatives_;
|
|
// }
|
|
|
|
ar & slp_; // does this need to be re-constructed after de-serialization?
|
|
|
|
ar & time_order_of_variable_groups_;
|
|
|
|
// if (Archive::is_loading::value == true){
|
|
// have_ordering_ = false;}
|
|
// else
|
|
// {
|
|
ar & have_ordering_;
|
|
ar & variable_ordering_;
|
|
// }
|
|
|
|
// if (Archive::is_loading::value == true){
|
|
// std::get<Vec<dbl>>(current_variable_values_).resize(NumVariables());
|
|
// std::get<Vec<mpfr_complex>>(current_variable_values_).resize(NumVariables());
|
|
// }
|
|
// // next two lines no matter what
|
|
// ar & std::get<Vec<dbl>>(current_variable_values_);
|
|
// ar & std::get<Vec<mpfr_complex>>(current_variable_values_);
|
|
}
|
|
|
|
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
|
|
};
|
|
|
|
/**
|
|
\brief Concatenate two compatible systems.
|
|
|
|
Two systems are compatible for concatenation if they have the same variable
|
|
structure, and if they have the same patch (if patched).
|
|
|
|
\param sys1 The top system.
|
|
\param sys2 The bottom system.
|
|
|
|
If both patched both must have same patch. If not both are patched, then the
|
|
patch will propagate to the returned system.
|
|
|
|
If the two patches have differing variable orderings, the call to Concatenate
|
|
will throw.
|
|
*/
|
|
System Concatenate(System sys1, System const& sys2);
|
|
|
|
/**
|
|
\brief Do a deep clone of the system. This includes the entire structure,
|
|
variables, etc. everything.
|
|
*/
|
|
System Clone(System const& sys);
|
|
|
|
/**
|
|
\brief Free form function for simplifying systems.
|
|
*/
|
|
void Simplify(System& sys);
|
|
} // namespace bertini
|
|
|
|
#endif // for the ifndef include guards
|
|
|