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.
782 lines
26 KiB
782 lines
26 KiB
// This file is part of Bertini 2.
|
|
//
|
|
// powerseries_endgame.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.
|
|
//
|
|
// powerseries_endgame.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 powerseries_endgame.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
|
|
// Tim Hodges, Colorado State University
|
|
|
|
#pragma once
|
|
|
|
#include "endgames/base_endgame.hpp"
|
|
|
|
namespace bertini {
|
|
namespace endgame {
|
|
|
|
/**
|
|
\class PowerSeriesEndgame
|
|
|
|
\brief class used to finish tracking paths during Homotopy Continuation
|
|
|
|
\ingroup endgame
|
|
|
|
## Explanation
|
|
|
|
The bertini::PowerSeriesEndgame class enables us to finish tracking on possibly
|
|
singular paths on an arbitrary square homotopy.
|
|
|
|
The intended usage is to:
|
|
|
|
1. Create a system, tracker, and instantiate some settings.
|
|
2. Using the tracker created track to the engame boundary, by default this is t
|
|
= 0.1.
|
|
3. Create a PowerSeriesEndgame, associating it to the tracker you wish to use.
|
|
The tracker knows the system being solved.
|
|
4. For each path being tracked send the PowerSeriesEndgame the time value and
|
|
other variable values that it should use to start the endgame.
|
|
5. The PowerSeriesEndgame, if successful, will store the homotopy solutions at t
|
|
= 0.
|
|
|
|
## Example Usage
|
|
|
|
Below we demonstrate a basic usage of the PowerSeriesEndgame class to find the
|
|
singularity at t = 0.
|
|
|
|
The pattern is as described above: create an instance of the class, feeding it
|
|
the system to be used, and the endgame boundary time and other variable values
|
|
at the endgame boundary.
|
|
|
|
\code{.cpp}
|
|
using namespace bertini::tracking;
|
|
using RealT = tracking::TrackerTraits<TrackerType>::BaseRealType; // Real types
|
|
using ComplexT = tracking::TrackerTraits<TrackerType>::BaseComplexType; Complex
|
|
types
|
|
|
|
// 1. Define the polynomial system that we wish to solve.
|
|
System target_sys;
|
|
Var x = Variable::Make("x"), t = Variable::Make("t"), y = Variable::Make("y");
|
|
|
|
VariableGroup vars{x,y};
|
|
target_sys.AddVariableGroup(vars);
|
|
|
|
target_sys.AddFunction((pow(x-1,3));
|
|
target_sys.AddFunction((pow(y-1,2));
|
|
|
|
// 1b. Homogenize and patch the polynomial system to work over projective space.
|
|
sys.Homogenize();
|
|
sys.AutoPatch();
|
|
|
|
// 2. Create a start system, for us we will use a total degree start system.
|
|
auto TD_start_sys = bertini::start_system::TotalDegree(target_sys);
|
|
|
|
// 2b. Creating homotopy between the start system and system we wish to solve.
|
|
auto my_homotopy = (1-t)*target_sys + t*TD_start_sys*Rational::Rand(); //the
|
|
random number is our gamma for a random path between t = 1 and t = 0.
|
|
my_homotopy.AddPathVariable(t);
|
|
|
|
//Sets up configuration settings for our particular system.
|
|
auto precision_config = PrecisionConfig(my_homotopy);
|
|
|
|
|
|
// 3. Creating a tracker. For us this is an AMPTracker.
|
|
AMPTracker tracker(my_homotopy);
|
|
|
|
//Tracker setup of settings.
|
|
SteppingConfig<RealT> stepping_preferences;
|
|
stepping_preferences.initial_step_size = RealT(1)/RealT(5);// change a stepping
|
|
preference NewtonConfig newton_preferences; tracker.Setup(TestedPredictor,
|
|
RealFromString("1e-6"),
|
|
RealFromString("1e5"),
|
|
stepping_preferences,
|
|
newton_preferences);
|
|
tracker.PrecisionSetup(precision_config);
|
|
|
|
//We start at t = 1, and will stop at t = 0.1 before starting the endgames.
|
|
ComplexT t_start(1), t_endgame_boundary(0.1);
|
|
|
|
//This will hold our solutions at t = 0.1
|
|
std::vector<Vec<ComplexT> > my_homotopy_solutions_at_endgame_boundary;
|
|
|
|
// result holds the value we track to at 0.1, and tracking success will report
|
|
if we are unsucessful. Vec<ComplexT> result;
|
|
|
|
//4. Track all points to 0.1
|
|
for (unsigned ii = 0; ii < TD_start_sys.NumStartPoints(); ++ii)
|
|
{
|
|
DefaultPrecision(ambient_precision);
|
|
my_homotopy.precision(ambient_precision); // making sure our precision is
|
|
all set up auto start_point = TD_start_sys.StartPoint<ComplexT>(ii);
|
|
|
|
tracker.TrackPath(result,t_start,t_endgame_boundary,start_point);
|
|
|
|
my_homotopy_solutions_at_endgame_boundary.push_back(result);
|
|
}
|
|
|
|
|
|
//Settings for the endgames.
|
|
|
|
PowerSeriesConfig power_series_settings;
|
|
power_series_settings.max_cycle_number = 4;
|
|
|
|
|
|
// 5. Create a power series endgame, and use them to get the soutions at t = 0.
|
|
EndgameSelector<TrackerType>::PSEG
|
|
my_pseg_endgame(tracker,power_series_settings,tolerances);
|
|
|
|
|
|
std::vector<Vec<ComplexT> > my_homotopy_solutions;
|
|
|
|
std::vector<Vec<ComplexT> > my_homotopy_divergent_paths;
|
|
|
|
for(auto s : my_homotopy_solutions_at_endgame_boundary)
|
|
{
|
|
SuccessCode endgame_success = my_pseg_endgame.Run(t_endgame_boundary,s);
|
|
|
|
if(endgame_success == SuccessCode::Success)
|
|
{
|
|
my_homotopy_solutions.push_back(my_homotopy.DehomogenizePoint(my_endgame.FinalApproximation<ComplexT>()));
|
|
}
|
|
else
|
|
{
|
|
my_homotopy_divergent_paths.push_back(my_homotopy.DehomogenizePoint(my_endgame.FinalApproximation<ComplexT>()));
|
|
}
|
|
}
|
|
|
|
|
|
\endcode
|
|
|
|
If this documentation is insufficient, please contact the authors with
|
|
suggestions, or get involved! Pull requests welcomed.
|
|
|
|
## Testing
|
|
|
|
Test suite driving this class: endgames_test.
|
|
|
|
File: test/endgames/generic_pseg_test.hpp
|
|
File: test/endgames/amp_powerseries_test.cpp
|
|
File: test/endgames/fixed_double_powerseries_test.cpp
|
|
FIle: test/endgames/fixed_multiple_powerseries_test.cpp
|
|
*/
|
|
|
|
template <typename PrecT>
|
|
class PowerSeriesEndgame
|
|
: public virtual EndgameBase<PowerSeriesEndgame<PrecT>, PrecT> {
|
|
public:
|
|
using BaseEGT = EndgameBase<PowerSeriesEndgame<PrecT>, PrecT>;
|
|
using FinalEGT = PowerSeriesEndgame<PrecT>;
|
|
using TrackerType = typename BaseEGT::TrackerType;
|
|
|
|
using BaseComplexType = typename BaseEGT::BaseComplexType;
|
|
using BaseRealType = typename BaseEGT::BaseRealType;
|
|
|
|
using EmitterType = PowerSeriesEndgame<PrecT>;
|
|
|
|
protected:
|
|
using EndgameBase<PowerSeriesEndgame<PrecT>, PrecT>::NotifyObservers;
|
|
|
|
using TupleOfTimes = typename BaseEGT::TupleOfTimes;
|
|
using TupleOfSamps = typename BaseEGT::TupleOfSamps;
|
|
|
|
using BCT = BaseComplexType;
|
|
using BRT = BaseRealType;
|
|
|
|
using Configs = typename AlgoTraits<FinalEGT>::NeededConfigs;
|
|
using ConfigsAsTuple = typename Configs::ToTuple;
|
|
|
|
/**
|
|
\brief State variable representing a computed upper bound on the cycle number.
|
|
*/
|
|
mutable unsigned upper_bound_on_cycle_number_;
|
|
|
|
/**
|
|
\brief Holds the time values for different space values used in the Power
|
|
series endgame.
|
|
*/
|
|
mutable TupleOfTimes times_;
|
|
|
|
/**
|
|
\brief Holds the space values used in the Power series endgame.
|
|
*/
|
|
mutable TupleOfSamps samples_;
|
|
|
|
/**
|
|
\brief Holds the derivatives at each space point.
|
|
*/
|
|
mutable TupleOfSamps derivatives_;
|
|
|
|
/**
|
|
\brief Random vector used in computing an upper bound on the cycle number.
|
|
*/
|
|
mutable Vec<BCT> rand_vector_;
|
|
|
|
template <typename CT>
|
|
void AssertSizesTimeSpace() const {
|
|
const auto num_sample_points = this->EndgameSettings().num_sample_points;
|
|
assert(std::get<SampCont<CT>>(samples_).size() ==
|
|
std::get<TimeCont<CT>>(times_).size() &&
|
|
"must have same number of samples in times and spaces");
|
|
assert(std::get<SampCont<CT>>(samples_).size() >= num_sample_points &&
|
|
"must have sufficient number of samples");
|
|
}
|
|
|
|
template <typename CT>
|
|
void AssertSizesTimeSpaceDeriv() const {
|
|
const auto num_sample_points = this->EndgameSettings().num_sample_points;
|
|
assert(std::get<SampCont<CT>>(samples_).size() ==
|
|
std::get<TimeCont<CT>>(times_).size() &&
|
|
"must have same number of samples in times and spaces");
|
|
assert(std::get<SampCont<CT>>(samples_).size() ==
|
|
std::get<SampCont<CT>>(derivatives_).size() &&
|
|
"must have same number of samples in derivatives and spaces");
|
|
assert(std::get<SampCont<CT>>(samples_).size() >= num_sample_points &&
|
|
"must have sufficient number of samples");
|
|
}
|
|
|
|
public:
|
|
auto UpperBoundOnCycleNumber() const { return upper_bound_on_cycle_number_; }
|
|
|
|
/**
|
|
\brief Function that clears all samples and times from data members for the
|
|
Power Series endgame
|
|
*/
|
|
template <typename CT>
|
|
void ClearTimesAndSamples() {
|
|
std::get<TimeCont<CT>>(times_).clear();
|
|
std::get<SampCont<CT>>(samples_).clear();
|
|
}
|
|
|
|
/**
|
|
\brief Function to set the times used for the Power Series endgame.
|
|
*/
|
|
template <typename CT>
|
|
void SetTimes(TimeCont<CT> const& times_to_set) {
|
|
std::get<TimeCont<CT>>(times_) = times_to_set;
|
|
}
|
|
|
|
/**
|
|
\brief Function to get the times used for the Power Series endgame.
|
|
*/
|
|
template <typename CT>
|
|
const auto& GetTimes() const {
|
|
return std::get<TimeCont<CT>>(times_);
|
|
}
|
|
|
|
const BCT& LatestTimeImpl() const { return GetTimes<BCT>().back(); }
|
|
|
|
/**
|
|
\brief Function to set the space values used for the Power Series endgame.
|
|
*/
|
|
template <typename CT>
|
|
void SetSamples(SampCont<CT> const& samples_to_set) {
|
|
std::get<SampCont<CT>>(samples_) = samples_to_set;
|
|
}
|
|
|
|
/**
|
|
\brief Function to get the space values used for the Power Series endgame.
|
|
*/
|
|
template <typename CT>
|
|
const auto& GetSamples() const {
|
|
return std::get<SampCont<CT>>(samples_);
|
|
}
|
|
|
|
/**
|
|
\brief Function to set the times used for the Power Series endgame.
|
|
*/
|
|
template <typename CT>
|
|
void SetRandVec(int size) {
|
|
rand_vector_ = Vec<CT>::Random(size);
|
|
}
|
|
|
|
explicit PowerSeriesEndgame(TrackerType const& tr,
|
|
const ConfigsAsTuple& settings)
|
|
: BaseEGT(tr, settings), EndgamePrecPolicyBase<TrackerType>(tr) {}
|
|
|
|
template <typename... Ts>
|
|
explicit PowerSeriesEndgame(TrackerType const& tr, const Ts&... ts)
|
|
: PowerSeriesEndgame(tr, Configs::Unpermute(ts...)) {}
|
|
|
|
/**
|
|
\brief Computes an upper bound on the cycle number. Consult page 53 of \cite
|
|
bertinibook.
|
|
|
|
## Input:
|
|
None: all data needed are class data members.
|
|
|
|
## Output:
|
|
upper_bound_on_cycle_number_: Used for an exhaustive search
|
|
for the best cycle number for approimating the path to t = 0.
|
|
|
|
##Details:
|
|
\tparam CT The complex number type.
|
|
*/
|
|
template <typename CT>
|
|
unsigned ComputeBoundOnCycleNumber() {
|
|
using RT = typename Eigen::NumTraits<CT>::Real;
|
|
using std::abs;
|
|
using std::log;
|
|
|
|
const auto& samples = std::get<SampCont<CT>>(samples_);
|
|
|
|
AssertSizesTimeSpace<CT>();
|
|
|
|
auto num_samples = samples.size();
|
|
const Vec<CT>& sample0 = samples[num_samples - 3];
|
|
const Vec<CT>& sample1 = samples[num_samples - 2];
|
|
const Vec<CT>& sample2 =
|
|
samples[num_samples - 1]; // most recent sample. oldest samples at
|
|
// front of the container
|
|
|
|
// should this only be if the system is homogenized?
|
|
CT rand_sum1 = ((sample1 - sample0).transpose() * rand_vector_).sum();
|
|
CT rand_sum2 = ((sample2 - sample1).transpose() * rand_vector_).sum();
|
|
|
|
if (abs(rand_sum1) == 0 || abs(rand_sum2) == 0) // avoid division by 0
|
|
{
|
|
upper_bound_on_cycle_number_ = 1;
|
|
return upper_bound_on_cycle_number_;
|
|
}
|
|
|
|
RT estimate = log(static_cast<RT>(this->EndgameSettings().sample_factor)) /
|
|
log(abs(rand_sum2 / rand_sum1));
|
|
|
|
if (estimate < 1) // would be nan if sample points are same as each other
|
|
upper_bound_on_cycle_number_ = 1;
|
|
else {
|
|
using std::max;
|
|
auto upper_bound = unsigned(
|
|
round(estimate) *
|
|
this->template Get<PowerSeriesConfig>().cycle_number_amplification);
|
|
upper_bound_on_cycle_number_ =
|
|
max(upper_bound,
|
|
this->template Get<PowerSeriesConfig>().max_cycle_number);
|
|
}
|
|
|
|
return upper_bound_on_cycle_number_;
|
|
} // end ComputeBoundOnCycleNumber
|
|
|
|
/**
|
|
\brief This function computes the cycle number using an exhaustive search up
|
|
the upper bound computed by the above function BoundOnCyleNumber.
|
|
|
|
## Input:
|
|
None: all data needed are class data members.
|
|
|
|
## Output:
|
|
cycle_number_: Used to create a hermite interpolation
|
|
to t = 0.
|
|
|
|
##Details:
|
|
\tparam CT The complex number type.
|
|
This is done by an exhaustive search from 1 to
|
|
upper_bound_on_cycle_number. There is a conversion to the s-space from t-space
|
|
in this function. As a by-product the derivatives at each of the samples is
|
|
returned for further use.
|
|
*/
|
|
|
|
template <typename CT>
|
|
unsigned ComputeCycleNumber(CT const& t0) {
|
|
using RT = typename Eigen::NumTraits<CT>::Real;
|
|
|
|
const auto& samples = std::get<SampCont<CT>>(samples_);
|
|
const auto& times = std::get<TimeCont<CT>>(times_);
|
|
const auto& derivatives = std::get<SampCont<CT>>(derivatives_);
|
|
|
|
AssertSizesTimeSpaceDeriv<CT>();
|
|
|
|
const Vec<CT>& most_recent_sample = samples.back();
|
|
const CT& most_recent_time = times.back();
|
|
|
|
// Compute upper bound for cycle number.
|
|
ComputeBoundOnCycleNumber<CT>();
|
|
|
|
unsigned num_pts;
|
|
if (samples.size() > this->EndgameSettings().num_sample_points)
|
|
num_pts = this->EndgameSettings().num_sample_points;
|
|
else
|
|
num_pts = this->EndgameSettings().num_sample_points - 1;
|
|
|
|
auto min_found_difference = Eigen::NumTraits<RT>::highest();
|
|
|
|
TimeCont<CT> s_times(num_pts);
|
|
SampCont<CT> s_derivatives(num_pts);
|
|
|
|
auto offset = samples.size() - num_pts -
|
|
1; // -1 here to shift away from the back of the container
|
|
for (unsigned int candidate = 1; candidate <= upper_bound_on_cycle_number_;
|
|
++candidate) {
|
|
using std::pow;
|
|
|
|
std::tie(s_times, s_derivatives) =
|
|
TransformToSPlane(candidate, t0, num_pts, ContStart::Front);
|
|
RT cand_power{1 / static_cast<RT>(candidate)};
|
|
RT curr_diff = (HermiteInterpolateAndSolve<CT>(
|
|
pow((most_recent_time - t0) / (times[0] - t0),
|
|
cand_power), // the target time
|
|
num_pts, s_times, samples, s_derivatives,
|
|
ContStart::Front) // the input data
|
|
- most_recent_sample)
|
|
.template lpNorm<Eigen::Infinity>();
|
|
|
|
if (curr_diff < min_found_difference) {
|
|
min_found_difference = curr_diff;
|
|
this->cycle_number_ = candidate;
|
|
}
|
|
|
|
} // end cc loop over cycle number possibilities
|
|
|
|
return this->cycle_number_;
|
|
} // end ComputeCycleNumber
|
|
|
|
/**
|
|
\brief Compute a set of derivatives using internal data to the
|
|
endgame.
|
|
|
|
## Input:
|
|
None: all data needed are class data members.
|
|
|
|
## Output:
|
|
None: Derivatives are members of this class.
|
|
|
|
##Details:
|
|
\tparam CT The complex number type.
|
|
*/
|
|
template <typename CT>
|
|
void ComputeAllDerivatives() {
|
|
auto& samples = std::get<SampCont<CT>>(samples_);
|
|
auto& times = std::get<TimeCont<CT>>(times_);
|
|
auto& derivatives = std::get<SampCont<CT>>(derivatives_);
|
|
|
|
assert((samples.size() == times.size()) &&
|
|
"must have same number of times and samples");
|
|
|
|
if (tracking::TrackerTraits<TrackerType>::IsAdaptivePrec) // known at
|
|
// compile time
|
|
{
|
|
auto max_precision = this->EnsureAtUniformPrecision(times, samples);
|
|
this->GetSystem().precision(max_precision);
|
|
}
|
|
|
|
// Compute dx_dt for each sample.
|
|
derivatives.clear();
|
|
derivatives.resize(samples.size());
|
|
for (unsigned ii = 0; ii < samples.size(); ++ii) {
|
|
derivatives[ii] =
|
|
-this->GetSystem()
|
|
.Jacobian(samples[ii], times[ii])
|
|
.lu()
|
|
.solve(this->GetSystem().TimeDerivative(samples[ii], times[ii]));
|
|
}
|
|
}
|
|
|
|
/**
|
|
\param c The cycle number you want to use
|
|
|
|
This function transforms the times and derivatives into the S-plane, scaled by
|
|
the cycle number.
|
|
|
|
this function also transforms them into the interval [0 1]
|
|
*/
|
|
template <typename CT>
|
|
std::tuple<TimeCont<CT>, SampCont<CT>> TransformToSPlane(
|
|
int cycle_num, CT const& t0, unsigned num_pts, ContStart shift_from) {
|
|
if (cycle_num == 0)
|
|
throw std::runtime_error(
|
|
"cannot transform to s plane with cycle number 0");
|
|
AssertSizesTimeSpaceDeriv<CT>();
|
|
|
|
using RT = typename Eigen::NumTraits<CT>::Real;
|
|
|
|
const auto& times = std::get<TimeCont<CT>>(times_);
|
|
const auto& derivatives = std::get<SampCont<CT>>(derivatives_);
|
|
|
|
RT c = static_cast<RT>(cycle_num);
|
|
RT one_over_c = 1 / c;
|
|
|
|
unsigned offset_t, offset_d;
|
|
if (shift_from == ContStart::Back) {
|
|
offset_t = times.size() - num_pts;
|
|
offset_d = derivatives.size() - num_pts;
|
|
} else
|
|
offset_t = offset_d = 0;
|
|
|
|
TimeCont<CT> s_times(num_pts);
|
|
SampCont<CT> s_derivatives(num_pts);
|
|
|
|
CT time_shift = times[offset_t] - t0;
|
|
|
|
for (unsigned ii = 0; ii < num_pts; ++ii) {
|
|
s_times[ii] = pow((times[ii + offset_t] - t0) / time_shift, one_over_c);
|
|
s_derivatives[ii] = derivatives[ii + offset_d] *
|
|
(c * pow(s_times[ii], cycle_num - 1)) * time_shift;
|
|
}
|
|
|
|
return std::make_tuple(s_times, s_derivatives);
|
|
}
|
|
|
|
/**
|
|
\brief This function computes an approximation of the space value at the time
|
|
time_t0.
|
|
|
|
## Input:
|
|
result: Passed by reference this holds the value of
|
|
the approximation we compute t0: This is the time value for which we wish to
|
|
compute an approximation at.
|
|
|
|
## Output:
|
|
SuccessCode: This reports back if we were successful
|
|
in making an approximation.
|
|
|
|
##Details:
|
|
\tparam CT The complex number type.
|
|
This function handles computing an approximation at
|
|
the origin. We compute the cycle number best for the approximation, and
|
|
convert derivatives and times to the s-plane where s = t^(1/c). We use the
|
|
converted times and derivatives along with the samples to do a Hermite
|
|
interpolation.
|
|
*/
|
|
template <typename CT>
|
|
SuccessCode ComputeApproximationOfXAtT0(Vec<CT>& result, const CT& t0) {
|
|
const auto c = ComputeCycleNumber<CT>(t0);
|
|
|
|
auto num_pts = this->EndgameSettings().num_sample_points;
|
|
|
|
TimeCont<CT> s_times;
|
|
SampCont<CT> s_derivatives;
|
|
|
|
std::tie(s_times, s_derivatives) =
|
|
TransformToSPlane(c, t0, num_pts, ContStart::Back);
|
|
// the data was transformed to be on the interval [0 1] so we can hard-code
|
|
// the time-to-solve as 0 here.
|
|
|
|
Precision(result, Precision(s_derivatives.back()));
|
|
result = HermiteInterpolateAndSolve(CT(0), num_pts, s_times,
|
|
std::get<SampCont<CT>>(samples_),
|
|
s_derivatives, ContStart::Back);
|
|
return SuccessCode::Success;
|
|
} // end ComputeApproximationOfXAtT0
|
|
|
|
/**
|
|
\brief The samples used in the power series endgame are collected by
|
|
advancing time to t = 0, by multiplying the current time by the sample
|
|
factor.
|
|
|
|
## Input:
|
|
target_time: This is the time we are trying to
|
|
approximate, default is t = 0.
|
|
|
|
## Output:
|
|
SuccessCode: This reports back if we were successful
|
|
in advancing time.
|
|
|
|
##Details:
|
|
\tparam CT The complex number type.
|
|
This function computes the next time value for the
|
|
power series endgame. After computing this time value, it will track to it
|
|
and compute the derivative at this time value for further appoximations to
|
|
be made during the endgame.
|
|
*/
|
|
template <typename CT>
|
|
SuccessCode AdvanceTime(const CT& target_time) {
|
|
using RT = typename Eigen::NumTraits<CT>::Real;
|
|
|
|
auto& samples = std::get<SampCont<CT>>(samples_);
|
|
auto& times = std::get<TimeCont<CT>>(times_);
|
|
auto& derivatives = std::get<SampCont<CT>>(derivatives_);
|
|
|
|
AssertSizesTimeSpaceDeriv<CT>();
|
|
|
|
Vec<CT> next_sample;
|
|
CT next_time =
|
|
(times.back() + target_time) *
|
|
static_cast<RT>(this->EndgameSettings()
|
|
.sample_factor); // setting up next time value
|
|
// using the midpoint formula,
|
|
// sample_factor will give us some
|
|
|
|
if (abs(next_time - target_time) <
|
|
this->EndgameSettings()
|
|
.min_track_time) // generalized for target_time not equal to 0.
|
|
{
|
|
NotifyObservers(MinTrackTimeReached<EmitterType>(*this));
|
|
return SuccessCode::MinTrackTimeReached;
|
|
}
|
|
|
|
SuccessCode tracking_success = this->GetTracker().TrackPath(
|
|
next_sample, times.back(), next_time, samples.back());
|
|
if (tracking_success != SuccessCode::Success) return tracking_success;
|
|
|
|
NotifyObservers(InEGOperatingZone<EmitterType>(*this));
|
|
|
|
this->EnsureAtPrecision(next_time, Precision(next_sample));
|
|
|
|
times.push_back(next_time);
|
|
samples.push_back(next_sample);
|
|
|
|
auto refine_success = this->RefineSample(
|
|
samples.back(), next_sample, times.back(),
|
|
this->FinalTolerance() *
|
|
this->EndgameSettings().sample_point_refinement_factor,
|
|
this->EndgameSettings().max_num_newton_iterations);
|
|
if (refine_success != SuccessCode::Success) {
|
|
NotifyObservers(RefiningFailed<EmitterType>(*this));
|
|
return refine_success;
|
|
}
|
|
|
|
this->EnsureAtPrecision(times.back(), Precision(samples.back()));
|
|
|
|
NotifyObservers(SampleRefined<EmitterType>(*this));
|
|
|
|
// we keep one more samplepoint than needed around, for estimating the cycle
|
|
// number
|
|
if (times.size() > this->EndgameSettings().num_sample_points + 1) {
|
|
times.pop_front();
|
|
samples.pop_front();
|
|
}
|
|
|
|
return SuccessCode::Success;
|
|
}
|
|
|
|
/**
|
|
\brief Primary function running the Power Series endgame.
|
|
|
|
## Input:
|
|
start_time: This is the time value for which the
|
|
endgame begins, by default this is t = 0.1 start_point: An approximate
|
|
solution of the homotopy at t = start_time target_time: The time value that we
|
|
are wishing to approximate to. This is default set to t = 0.
|
|
|
|
## Output:
|
|
SuccessCode: This reports back if we were successful
|
|
in advancing time.
|
|
|
|
##Details:
|
|
\tparam CT The complex number type.
|
|
Tracking forward with the number of sample points,
|
|
this function will make approximations using Hermite interpolation. This
|
|
process will continue until two consecutive approximations are withing final
|
|
tolerance of each other.
|
|
*/
|
|
template <typename CT>
|
|
SuccessCode RunImpl(const CT& start_time, const Vec<CT>& start_point,
|
|
CT const& target_time) {
|
|
if (start_point.size() != this->GetSystem().NumVariables()) {
|
|
std::stringstream err_msg;
|
|
err_msg << "number of variables in start point for PSEG, "
|
|
<< start_point.size()
|
|
<< ", must match the number of variables in the system, "
|
|
<< this->GetSystem().NumVariables();
|
|
throw std::runtime_error(err_msg.str());
|
|
}
|
|
|
|
DefaultPrecision(Precision(start_point));
|
|
|
|
using RT = typename Eigen::NumTraits<CT>::Real;
|
|
// Set up for the endgame.
|
|
ClearTimesAndSamples<CT>();
|
|
|
|
// unpack some references for easy use
|
|
auto& samples = std::get<SampCont<CT>>(samples_);
|
|
auto& times = std::get<TimeCont<CT>>(times_);
|
|
auto& derivatives = std::get<SampCont<CT>>(derivatives_);
|
|
Vec<CT>& latest_approx = this->final_approximation_;
|
|
Vec<CT>& prev_approx = this->previous_approximation_;
|
|
|
|
// this is for estimating a ... norm?
|
|
SetRandVec<CT>(start_point.size());
|
|
|
|
auto initial_sample_success = this->ComputeInitialSamples(
|
|
start_time, target_time, start_point, times, samples);
|
|
|
|
if (initial_sample_success != SuccessCode::Success) {
|
|
NotifyObservers(EndgameFailure<EmitterType>(*this));
|
|
return initial_sample_success;
|
|
}
|
|
|
|
this->template RefineAllSamples<CT>(samples, times);
|
|
ComputeAllDerivatives<CT>();
|
|
|
|
auto extrapolation_code =
|
|
ComputeApproximationOfXAtT0(prev_approx, target_time);
|
|
latest_approx = prev_approx;
|
|
|
|
if (extrapolation_code != SuccessCode::Success) return extrapolation_code;
|
|
|
|
RT norm_of_dehom_of_latest_approx;
|
|
RT norm_of_dehom_of_prev_approx;
|
|
if (this->SecuritySettings().level <= 0)
|
|
norm_of_dehom_of_prev_approx = this->GetSystem()
|
|
.DehomogenizePoint(prev_approx)
|
|
.template lpNorm<Eigen::Infinity>();
|
|
|
|
NumErrorT& approx_error = this->approximate_error_;
|
|
approx_error = 1;
|
|
|
|
while (approx_error > this->FinalTolerance()) {
|
|
auto advance_code = AdvanceTime<CT>(target_time);
|
|
if (advance_code != SuccessCode::Success) {
|
|
NotifyObservers(EndgameFailure<EmitterType>(*this));
|
|
return advance_code;
|
|
}
|
|
|
|
// this code is what bertini1 does... it refines all samples, like, all
|
|
// the time.
|
|
this->template RefineAllSamples<CT>(samples, times);
|
|
ComputeAllDerivatives<CT>();
|
|
|
|
extrapolation_code =
|
|
ComputeApproximationOfXAtT0(latest_approx, target_time);
|
|
if (extrapolation_code != SuccessCode::Success) {
|
|
NotifyObservers(EndgameFailure<EmitterType>(*this));
|
|
return extrapolation_code;
|
|
}
|
|
|
|
approx_error = static_cast<NumErrorT>(
|
|
(latest_approx - prev_approx).template lpNorm<Eigen::Infinity>());
|
|
NotifyObservers(ApproximatedRoot<EmitterType>(*this));
|
|
|
|
if (this->SecuritySettings().level <= 0) {
|
|
norm_of_dehom_of_latest_approx =
|
|
this->GetSystem()
|
|
.DehomogenizePoint(latest_approx)
|
|
.template lpNorm<Eigen::Infinity>();
|
|
if (norm_of_dehom_of_latest_approx >
|
|
this->SecuritySettings().max_norm &&
|
|
norm_of_dehom_of_prev_approx > this->SecuritySettings().max_norm) {
|
|
NotifyObservers(SecurityMaxNormReached<EmitterType>(*this));
|
|
return SuccessCode::SecurityMaxNormReached;
|
|
}
|
|
norm_of_dehom_of_prev_approx = norm_of_dehom_of_latest_approx;
|
|
}
|
|
|
|
Precision(prev_approx, Precision(latest_approx));
|
|
prev_approx = latest_approx;
|
|
} // end while
|
|
|
|
NotifyObservers(Converged<EmitterType>(*this));
|
|
return SuccessCode::Success;
|
|
|
|
} // end PSEG
|
|
|
|
virtual ~PowerSeriesEndgame() = default;
|
|
}; // end powerseries class
|
|
|
|
} // namespace endgame
|
|
} // namespace bertini
|
|
|