Bertini 2 - C++ Core
Software for Numerical Algebraic Geometry
Loading...
Searching...
No Matches
powerseries.hpp
1//This file is part of Bertini 2.
2//
3//powerseries_endgame.hpp is free software: you can redistribute it and/or modify
4//it under the terms of the GNU General Public License as published by
5//the Free Software Foundation, either version 3 of the License, or
6//(at your option) any later version.
7//
8//powerseries_endgame.hpp is distributed in the hope that it will be useful,
9//but WITHOUT ANY WARRANTY; without even the implied warranty of
10//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11//GNU General Public License for more details.
12//
13//You should have received a copy of the GNU General Public License
14//along with powerseries_endgame.hpp. If not, see <http://www.gnu.org/licenses/>.
15//
16// Copyright(C) 2015 - 2021 by Bertini2 Development Team
17//
18// See <http://www.gnu.org/licenses/> for a copy of the license,
19// as well as COPYING. Bertini2 is provided with permitted
20// additional terms in the b2/licenses/ directory.
21
22// individual authors of this file include:
23// silviana amethyst, university of wisconsin eau claire
24// Tim Hodges, Colorado State University
25
26
27#pragma once
28
30
31
32namespace bertini{ namespace endgame{
33
34
168
169template<typename PrecT>
170class PowerSeriesEndgame :
171 public virtual EndgameBase<PowerSeriesEndgame<PrecT>, PrecT>
172{
173public:
174 using BaseEGT = EndgameBase<PowerSeriesEndgame<PrecT>, PrecT>;
175 using FinalEGT = PowerSeriesEndgame<PrecT>;
176 using TrackerType = typename BaseEGT::TrackerType;
177
178 using BaseComplexType = typename BaseEGT::BaseComplexType;
179 using BaseRealType = typename BaseEGT::BaseRealType;
180
181 using EmitterType = PowerSeriesEndgame<PrecT>;
182
183protected:
184
185 using EndgameBase<PowerSeriesEndgame<PrecT>, PrecT>::NotifyObservers;
186
187 using TupleOfTimes = typename BaseEGT::TupleOfTimes;
188 using TupleOfSamps = typename BaseEGT::TupleOfSamps;
189
190 using BCT = BaseComplexType;
191 using BRT = BaseRealType;
192
193 using Configs = typename AlgoTraits<FinalEGT>::NeededConfigs;
194 using ConfigsAsTuple = typename Configs::ToTuple;
195
200
204 mutable TupleOfTimes times_;
205
209 mutable TupleOfSamps samples_;
210
214 mutable TupleOfSamps derivatives_;
215
219 mutable Vec<BCT> rand_vector_;
220
221 template<typename CT>
222 void AssertSizesTimeSpace() const
223 {
224 const auto num_sample_points = this->EndgameSettings().num_sample_points;
225 assert(std::get<SampCont<CT> >(samples_).size()==std::get<TimeCont<CT> >(times_).size() && "must have same number of samples in times and spaces");
226 assert(std::get<SampCont<CT> >(samples_).size()>=num_sample_points && "must have sufficient number of samples");
227 }
228
229 template<typename CT>
230 void AssertSizesTimeSpaceDeriv() const
231 {
232 const auto num_sample_points = this->EndgameSettings().num_sample_points;
233 assert(std::get<SampCont<CT> >(samples_).size()==std::get<TimeCont<CT> >(times_).size() && "must have same number of samples in times and spaces");
234 assert(std::get<SampCont<CT> >(samples_).size()==std::get<SampCont<CT> >(derivatives_).size() && "must have same number of samples in derivatives and spaces");
235 assert(std::get<SampCont<CT> >(samples_).size()>=num_sample_points && "must have sufficient number of samples");
236 }
237
238public:
239
240 auto UpperBoundOnCycleNumber() const { return upper_bound_on_cycle_number_;}
241
242
246 template<typename CT>
248 {
249 std::get<TimeCont<CT> >(times_).clear();
250 std::get<SampCont<CT> >(samples_).clear();
251 }
252
256 template<typename CT>
257 void SetTimes(TimeCont<CT> const& times_to_set) { std::get<TimeCont<CT> >(times_) = times_to_set;}
258
262 template<typename CT>
263 const auto& GetTimes() const {return std::get<TimeCont<CT> >(times_);}
264
265
266 const BCT& LatestTimeImpl() const
267 {
268 return GetTimes<BCT>().back();
269 }
270
274 template<typename CT>
275 void SetSamples(SampCont<CT> const& samples_to_set) { std::get<SampCont<CT> >(samples_) = samples_to_set;}
276
280 template<typename CT>
281 const auto& GetSamples() const {return std::get<SampCont<CT> >(samples_);}
282
286 template<typename CT>
287 void SetRandVec(int size) {rand_vector_ = Vec<CT>::Random(size);}
288
289
290
291
292
293 explicit PowerSeriesEndgame(TrackerType const& tr,
294 const ConfigsAsTuple& settings )
295 : BaseEGT(tr, settings), EndgamePrecPolicyBase<TrackerType>(tr)
296 {}
297
298 template< typename... Ts >
299 explicit
300 PowerSeriesEndgame(TrackerType const& tr, const Ts&... ts ) : PowerSeriesEndgame(tr, Configs::Unpermute( ts... ) )
301 {}
302
303
304
317 template<typename CT>
319 {
320 using RT = typename Eigen::NumTraits<CT>::Real;
321 using std::log; using std::abs;
322
323 const auto& samples = std::get<SampCont<CT> >(samples_);
324
325 AssertSizesTimeSpace<CT>();
326
327 auto num_samples = samples.size();
328 const Vec<CT> & sample0 = samples[num_samples-3];
329 const Vec<CT> & sample1 = samples[num_samples-2];
330 const Vec<CT> & sample2 = samples[num_samples-1]; // most recent sample. oldest samples at front of the container
331
332
333// should this only be if the system is homogenized?
334 CT rand_sum1 = ((sample1 - sample0).transpose()*rand_vector_).sum();
335 CT rand_sum2 = ((sample2 - sample1).transpose()*rand_vector_).sum();
336
337 if ( abs(rand_sum1)==0 || abs(rand_sum2)==0) // avoid division by 0
338 {
341 }
342
343 RT estimate = log(static_cast<RT>(this->EndgameSettings().sample_factor))/log(abs(rand_sum2/rand_sum1));
344
345 if (estimate < 1) // would be nan if sample points are same as each other
347 else
348 {
349 using std::max;
350 auto upper_bound = unsigned(round(estimate)*this->template Get<PowerSeriesConfig>().cycle_number_amplification);
351 upper_bound_on_cycle_number_ = max(upper_bound,this->template Get<PowerSeriesConfig>().max_cycle_number);
352 }
353
355 }//end ComputeBoundOnCycleNumber
356
357
358
359
374
375 template<typename CT>
376 unsigned ComputeCycleNumber(CT const& t0)
377 {
378 using RT = typename Eigen::NumTraits<CT>::Real;
379
380 const auto& samples = std::get<SampCont<CT> >(samples_);
381 const auto& times = std::get<TimeCont<CT> >(times_);
382 const auto& derivatives = std::get<SampCont<CT> >(derivatives_);
383
384 AssertSizesTimeSpaceDeriv<CT>();
385
386 const Vec<CT> &most_recent_sample = samples.back();
387 const CT& most_recent_time = times.back();
388
389 //Compute upper bound for cycle number.
391
392
393 unsigned num_pts;
394 if (samples.size() > this->EndgameSettings().num_sample_points)
395 num_pts = this->EndgameSettings().num_sample_points;
396 else
397 num_pts = this->EndgameSettings().num_sample_points-1;
398
399
400 auto min_found_difference = Eigen::NumTraits<RT>::highest();
401
402 TimeCont<CT> s_times(num_pts);
403 SampCont<CT> s_derivatives(num_pts);
404
405 auto offset = samples.size() - num_pts - 1; // -1 here to shift away from the back of the container
406 for(unsigned int candidate = 1; candidate <= upper_bound_on_cycle_number_; ++candidate)
407 {
408 using std::pow;
409
410 std::tie(s_times, s_derivatives) = TransformToSPlane(candidate, t0, num_pts, ContStart::Front);
411 RT cand_power{1/static_cast<RT>(candidate)};
412 RT curr_diff = (HermiteInterpolateAndSolve<CT>(
413 pow((most_recent_time-t0)/(times[0]-t0),cand_power), // the target time
414 num_pts,s_times,samples,s_derivatives, ContStart::Front) // the input data
415 -
416 most_recent_sample).template lpNorm<Eigen::Infinity>();
417
418 if (curr_diff < min_found_difference)
419 {
420 min_found_difference = curr_diff;
421 this->cycle_number_ = candidate;
422 }
423
424 }// end cc loop over cycle number possibilities
425
426 return this->cycle_number_;
427 }//end ComputeCycleNumber
428
429
430
443 template<typename CT>
445 {
446 auto& samples = std::get<SampCont<CT> >(samples_);
447 auto& times = std::get<TimeCont<CT> >(times_);
448 auto& derivatives = std::get<SampCont<CT> >(derivatives_);
449
450 assert((samples.size() == times.size()) && "must have same number of times and samples");
451
452 if (tracking::TrackerTraits<TrackerType>::IsAdaptivePrec) // known at compile time
453 {
454 auto max_precision = this->EnsureAtUniformPrecision(times, samples);
455 this->GetSystem().precision(max_precision);
456 }
457
458 //Compute dx_dt for each sample.
459 derivatives.clear(); derivatives.resize(samples.size());
460 for(unsigned ii = 0; ii < samples.size(); ++ii)
461 {
462 derivatives[ii] = -this->GetSystem().Jacobian(samples[ii],times[ii]).lu().solve(this->GetSystem().TimeDerivative(samples[ii],times[ii]));
463 }
464 }
465
466
474 template <typename CT>
475 std::tuple<TimeCont<CT>, SampCont<CT>> TransformToSPlane(int cycle_num, CT const& t0, unsigned num_pts, ContStart shift_from)
476 {
477 if (cycle_num==0)
478 throw std::runtime_error("cannot transform to s plane with cycle number 0");
479 AssertSizesTimeSpaceDeriv<CT>();
480
481
482 using RT = typename Eigen::NumTraits<CT>::Real;
483
484 const auto& times = std::get<TimeCont<CT> >(times_);
485 const auto& derivatives = std::get<SampCont<CT> >(derivatives_);
486
487 RT c = static_cast<RT>(cycle_num);
488 RT one_over_c = 1/c;
489
490 unsigned offset_t, offset_d;
491 if (shift_from == ContStart::Back)
492 {
493 offset_t = times.size()-num_pts;
494 offset_d = derivatives.size()-num_pts;
495 }
496 else
497 offset_t = offset_d = 0;
498
499
500 TimeCont<CT> s_times(num_pts);
501 SampCont<CT> s_derivatives(num_pts);
502
503 CT time_shift = times[offset_t] - t0;
504
505 for(unsigned ii = 0; ii < num_pts; ++ii){
506 s_times[ii] = pow((times[ii+offset_t]-t0)/time_shift, one_over_c);
507 s_derivatives[ii] = derivatives[ii+offset_d]*( c*pow(s_times[ii],cycle_num-1))*time_shift;
508 }
509
510 return std::make_tuple(s_times, s_derivatives);
511 }
512
513
514
531 template<typename CT>
532 SuccessCode ComputeApproximationOfXAtT0(Vec<CT>& result, const CT & t0)
533 {
534 const auto c = ComputeCycleNumber<CT>(t0);
535
536 auto num_pts = this->EndgameSettings().num_sample_points;
537
538 TimeCont<CT> s_times;
539 SampCont<CT> s_derivatives;
540
541 std::tie(s_times, s_derivatives) = TransformToSPlane(c, t0, num_pts, ContStart::Back);
542 // the data was transformed to be on the interval [0 1] so we can hard-code the time-to-solve as 0 here.
543
544 Precision(result, Precision(s_derivatives.back()));
545 result = HermiteInterpolateAndSolve(CT(0), num_pts, s_times, std::get<SampCont<CT> >(samples_), s_derivatives, ContStart::Back);
546 return SuccessCode::Success;
547 }//end ComputeApproximationOfXAtT0
548
549
550
566 template<typename CT>
567 SuccessCode AdvanceTime(const CT & target_time)
568 {
569 using RT = typename Eigen::NumTraits<CT>::Real;
570
571 auto& samples = std::get<SampCont<CT> >(samples_);
572 auto& times = std::get<TimeCont<CT> >(times_);
573 auto& derivatives = std::get<SampCont<CT> >(derivatives_);
574
575 AssertSizesTimeSpaceDeriv<CT>();
576
577 Vec<CT> next_sample;
578 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
579
580 if (abs(next_time - target_time) < this->EndgameSettings().min_track_time) // generalized for target_time not equal to 0.
581 {
582 NotifyObservers(MinTrackTimeReached<EmitterType>(*this));
583 return SuccessCode::MinTrackTimeReached;
584 }
585
586 SuccessCode tracking_success = this->GetTracker().TrackPath(next_sample,times.back(),next_time,samples.back());
587 if (tracking_success != SuccessCode::Success)
588 return tracking_success;
589
590 NotifyObservers(InEGOperatingZone<EmitterType>(*this));
591
592 this->EnsureAtPrecision(next_time,Precision(next_sample));
593
594
595 times.push_back(next_time);
596 samples.push_back(next_sample);
597
598
599
600
601 auto refine_success = this->RefineSample(samples.back(), next_sample, times.back(),
602 this->FinalTolerance() * this->EndgameSettings().sample_point_refinement_factor,
603 this->EndgameSettings().max_num_newton_iterations);
604 if (refine_success != SuccessCode::Success)
605 {
606 NotifyObservers(RefiningFailed<EmitterType>(*this));
607 return refine_success;
608 }
609
610 this->EnsureAtPrecision(times.back(),Precision(samples.back()));
611
612 NotifyObservers(SampleRefined<EmitterType>(*this));
613
614 // we keep one more samplepoint than needed around, for estimating the cycle number
615 if (times.size() > this->EndgameSettings().num_sample_points+1)
616 {
617 times.pop_front();
618 samples.pop_front();
619 }
620
621 return SuccessCode::Success;
622 }
623
624
641 template<typename CT>
642 SuccessCode RunImpl(const CT & start_time, const Vec<CT> & start_point, CT const& target_time)
643 {
644 if (start_point.size()!=this->GetSystem().NumVariables())
645 {
646 std::stringstream err_msg;
647 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();
648 throw std::runtime_error(err_msg.str());
649 }
650
651 DefaultPrecision(Precision(start_point));
652
653 using RT = typename Eigen::NumTraits<CT>::Real;
654 //Set up for the endgame.
656
657
658 // unpack some references for easy use
659 auto& samples = std::get<SampCont<CT> >(samples_);
660 auto& times = std::get<TimeCont<CT> >(times_);
661 auto& derivatives = std::get<SampCont<CT> >(derivatives_);
662 Vec<CT>& latest_approx = this->final_approximation_;
663 Vec<CT>& prev_approx = this->previous_approximation_;
664
665 // this is for estimating a ... norm?
666 SetRandVec<CT>(start_point.size());
667
668
669 auto initial_sample_success = this->ComputeInitialSamples(start_time, target_time, start_point, times, samples);
670
671 if (initial_sample_success!=SuccessCode::Success)
672 {
673 NotifyObservers(EndgameFailure<EmitterType>(*this));
674 return initial_sample_success;
675 }
676
677 this->template RefineAllSamples<CT>(samples, times);
679
680
681
682 auto extrapolation_code = ComputeApproximationOfXAtT0(prev_approx, target_time);
683 latest_approx = prev_approx;
684
685 if (extrapolation_code != SuccessCode::Success)
686 return extrapolation_code;
687
688 RT norm_of_dehom_of_latest_approx;
689 RT norm_of_dehom_of_prev_approx;
690 if (this->SecuritySettings().level <= 0)
691 norm_of_dehom_of_prev_approx = this->GetSystem().DehomogenizePoint(prev_approx).template lpNorm<Eigen::Infinity>();
692
693
694 NumErrorT& approx_error = this->approximate_error_;
695 approx_error = 1;
696
697 while (approx_error > this->FinalTolerance())
698 {
699 auto advance_code = AdvanceTime<CT>(target_time);
700 if (advance_code!=SuccessCode::Success)
701 {
702 NotifyObservers(EndgameFailure<EmitterType>(*this));
703 return advance_code;
704 }
705
706 // this code is what bertini1 does... it refines all samples, like, all the time.
707 this->template RefineAllSamples<CT>(samples, times);
709
710 extrapolation_code = ComputeApproximationOfXAtT0(latest_approx, target_time);
711 if (extrapolation_code!=SuccessCode::Success)
712 {
713 NotifyObservers(EndgameFailure<EmitterType>(*this));
714 return extrapolation_code;
715 }
716
717 approx_error = static_cast<NumErrorT>((latest_approx - prev_approx).template lpNorm<Eigen::Infinity>());
718 NotifyObservers(ApproximatedRoot<EmitterType>(*this));
719
720
721 if(this->SecuritySettings().level <= 0)
722 {
723 norm_of_dehom_of_latest_approx = this->GetSystem().DehomogenizePoint(latest_approx).template lpNorm<Eigen::Infinity>();
724 if(norm_of_dehom_of_latest_approx > this->SecuritySettings().max_norm && norm_of_dehom_of_prev_approx > this->SecuritySettings().max_norm)
725 {
726 NotifyObservers(SecurityMaxNormReached<EmitterType>(*this));
727 return SuccessCode::SecurityMaxNormReached;
728 }
729 norm_of_dehom_of_prev_approx = norm_of_dehom_of_latest_approx;
730 }
731
732
733
734
735 Precision(prev_approx, Precision(latest_approx));
736 prev_approx = latest_approx;
737 } //end while
738
739 NotifyObservers(Converged<EmitterType>(*this));
740 return SuccessCode::Success;
741
742 } //end PSEG
743
744
745 virtual ~PowerSeriesEndgame() = default;
746}; // end powerseries class
747
748
749
750
751}} // re: namespaces
Contains parent class, Endgame, the parent class for all endgames.
void NotifyObservers(AnyEvent const &e) const
Sends an Event (more particularly, AnyEvent) to all watching observers of this object.
Definition observable.hpp:90
A common base type for various precision types, fixed and adaptive. The purpose of this is to maintai...
Definition prec_base.hpp:46
class used to finish tracking paths during Homotopy Continuation
Definition powerseries.hpp:172
const auto & GetSamples() const
Function to get the space values used for the Power Series endgame.
Definition powerseries.hpp:281
TupleOfSamps derivatives_
Definition powerseries.hpp:214
std::tuple< TimeCont< CT >, SampCont< CT > > TransformToSPlane(int cycle_num, CT const &t0, unsigned num_pts, ContStart shift_from)
Definition powerseries.hpp:475
TupleOfTimes times_
Definition powerseries.hpp:204
Vec< BCT > rand_vector_
Definition powerseries.hpp:219
SuccessCode RunImpl(const CT &start_time, const Vec< CT > &start_point, CT const &target_time)
Primary function running the Power Series endgame.
Definition powerseries.hpp:642
unsigned ComputeBoundOnCycleNumber()
Computes an upper bound on the cycle number. Consult page 53 of bertinibook.
Definition powerseries.hpp:318
const auto & GetTimes() const
Function to get the times used for the Power Series endgame.
Definition powerseries.hpp:263
unsigned ComputeCycleNumber(CT const &t0)
This function computes the cycle number using an exhaustive search up the upper bound computed by the...
Definition powerseries.hpp:376
TupleOfSamps samples_
Definition powerseries.hpp:209
void ClearTimesAndSamples()
Function that clears all samples and times from data members for the Power Series endgame.
Definition powerseries.hpp:247
SuccessCode ComputeApproximationOfXAtT0(Vec< CT > &result, const CT &t0)
This function computes an approximation of the space value at the time time_t0.
Definition powerseries.hpp:532
void SetTimes(TimeCont< CT > const &times_to_set)
Function to set the times used for the Power Series endgame.
Definition powerseries.hpp:257
void ComputeAllDerivatives()
Compute a set of derivatives using internal data to the endgame.
Definition powerseries.hpp:444
unsigned upper_bound_on_cycle_number_
Definition powerseries.hpp:199
void SetSamples(SampCont< CT > const &samples_to_set)
Function to set the space values used for the Power Series endgame.
Definition powerseries.hpp:275
void SetRandVec(int size)
Function to set the times used for the Power Series endgame.
Definition powerseries.hpp:287
SuccessCode AdvanceTime(const CT &target_time)
The samples used in the power series endgame are collected by advancing time to t = 0,...
Definition powerseries.hpp:567
dbl pow(const dbl &z, int power)
Definition double_extensions.hpp:88
unsigned Precision(Eigen::MatrixBase< Derived > const &v)
Get the precision of an Eigen object. If the object is empty, it's the precision of a default-constru...
Definition eigen_extensions.hpp:292
Vec< CT > HermiteInterpolateAndSolve(CT const &target_time, const unsigned int num_sample_points, const TimeCont< CT > &times, const SampCont< CT > &samples, const SampCont< CT > &derivatives, ContStart shift_from=ContStart::Back)
Estimates the root to interpolating polynomial.
Definition interpolation.hpp:63
T max(T const &a, T const &b, T const &c)
Three-argument form of max.
Definition mpfr_extensions.hpp:98