Bertini 2 d882924 (2026-10-06 19:56 UTC)
Numerical algebraic geometry library (C++ core)
Loading...
Searching...
No Matches
powerseries.hpp
Go to the documentation of this file.
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) 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#pragma once
23
25
26
27namespace bertini{ namespace endgame{
28
29
162
163template<typename PrecT>
165 public virtual EndgameBase<PowerSeriesEndgame<PrecT>, PrecT>
166{
167public:
171
173 using BaseRealT = typename BaseEGT::BaseRealT;
174
176
177protected:
178
180
183 using TupOfVec = typename BaseEGT::TupOfVec;
184
186 using BRT = BaseRealT;
187
189 using ConfigsAsTuple = typename Configs::ToTuple;
190
195
200
205
210
219
222 mutable bool announced_operating_zone_ = false;
223
232
234 template<typename ComplexT>
236 {
237#ifndef NDEBUG
238 const auto num_sample_points = this->EndgameSettings().num_sample_points;
239 assert(std::get<SampCont<ComplexT> >(samples_).size()==std::get<TimeCont<ComplexT> >(times_).size() && "must have same number of samples in times and spaces");
240 assert(std::get<SampCont<ComplexT> >(samples_).size()>=num_sample_points && "must have sufficient number of samples");
241#endif
242 }
243
245 template<typename ComplexT>
247 {
248#ifndef NDEBUG
249 const auto num_sample_points = this->EndgameSettings().num_sample_points;
250 assert(std::get<SampCont<ComplexT> >(samples_).size()==std::get<TimeCont<ComplexT> >(times_).size() && "must have same number of samples in times and spaces");
251 assert(std::get<SampCont<ComplexT> >(samples_).size()==std::get<SampCont<ComplexT> >(derivatives_).size() && "must have same number of samples in derivatives and spaces");
252 assert(std::get<SampCont<ComplexT> >(samples_).size()>=num_sample_points && "must have sufficient number of samples");
253#endif
254 }
255
256public:
257
260
261
265 template<typename ComplexT>
267 {
268 std::get<TimeCont<ComplexT> >(times_).clear();
269 std::get<SampCont<ComplexT> >(samples_).clear();
270 // the operating-zone question is asked afresh for each path
271 std::get<TimeCont<ComplexT> >(c_over_k_).clear();
273 }
274
278 template<typename ComplexT>
279 void SetTimes(TimeCont<ComplexT> const& times_to_set) { std::get<TimeCont<ComplexT> >(times_) = times_to_set;}
280
284 template<typename ComplexT>
285 const auto& GetTimes() const {return std::get<TimeCont<ComplexT> >(times_);}
286
287
289
291 const BCT& LatestTimeImpl() const
292 {
293 // In the adaptive-numeric-type endgame the latest time may live in the complex_dbl slot, with
294 // the BCT slot empty. Dispatch on which slot holds data so this is correct in either lane and
295 // both during and after the run. Fixed precision compiles to the original BCT read.
297 {
298 if (GetTimes<BCT>().empty())
299 {
300 auto const& dbl_times = GetTimes<complex_dbl>();
301 latest_time_cache_ = dbl_times.empty() ? BCT(0) : BCT(dbl_times.back());
302 return latest_time_cache_;
303 }
304 }
305 return GetTimes<BCT>().back();
306 }
307
311 template<typename ComplexT>
312 void SetSamples(SampCont<ComplexT> const& samples_to_set) { std::get<SampCont<ComplexT> >(samples_) = samples_to_set;}
313
317 template<typename ComplexT>
318 const auto& GetSamples() const {return std::get<SampCont<ComplexT> >(samples_);}
319
323 template<typename ComplexT>
324 void SetRandVec(int size)
325 {
326 auto& rv = std::get<Vec<ComplexT> >(rand_vector_);
327 rv.resize(size);
328 for (int ii = 0; ii < size; ++ii)
329 rv(ii) = RandomUnit<ComplexT>();
330 }
331
332
333
334
335
341
343 template< typename... Ts >
344 explicit
345 PowerSeriesEndgame(TrackerType const& tr, const Ts&... ts ) : PowerSeriesEndgame(tr, Configs::Unpermute( ts... ) )
346 {}
347
348
349
362 template<typename ComplexT>
364 {
365 using RealT = typename Eigen::NumTraits<ComplexT>::Real;
366 using std::log; using std::abs;
367
368 const auto& samples = std::get<SampCont<ComplexT> >(samples_);
369
371
372 auto num_samples = samples.size();
373 const Vec<ComplexT> & sample0 = samples[num_samples-3];
374 const Vec<ComplexT> & sample1 = samples[num_samples-2];
375 const Vec<ComplexT> & sample2 = samples[num_samples-1]; // most recent sample. oldest samples at front of the container
376
377
378// should this only be if the system is homogenized?
379 const auto& rand_vector = std::get<Vec<ComplexT> >(rand_vector_);
380 ComplexT rand_sum1 = ((sample1 - sample0).transpose()*rand_vector).sum();
381 ComplexT rand_sum2 = ((sample2 - sample1).transpose()*rand_vector).sum();
382
383 if ( abs(rand_sum1)==0 || abs(rand_sum2)==0) // avoid division by 0
384 {
387 }
388
389 RealT estimate = log(static_cast<RealT>(this->EndgameSettings().sample_factor))/log(abs(rand_sum2/rand_sum1));
390
391 const auto& ps_config = this->template Get<PowerSeriesConfig>();
392 if (estimate < 1)
394 else
395 {
396 // max_cycle_number is a CEILING on the candidate search (each candidate costs a full
397 // Hermite solve). Clamp before any conversion to unsigned: near-unity sample ratios
398 // (slow convergence -- high multiplicity, or a slow diverger) drive the estimate toward
399 // +inf, and unsigned(inf) is undefined behavior. The !(a < b) form also routes NaN to
400 // the ceiling. This was `max` -- the ceiling was a floor, and the search was unbounded.
401 using std::round;
402 RealT amplified = round(estimate) * static_cast<RealT>(ps_config.cycle_number_amplification);
403 if (!(amplified < static_cast<RealT>(ps_config.max_cycle_number)))
404 upper_bound_on_cycle_number_ = ps_config.max_cycle_number;
405 else
406 upper_bound_on_cycle_number_ = std::max(1u, static_cast<unsigned>(amplified));
407 }
408
410 }//end ComputeBoundOnCycleNumber
411
412
413
414
429
430 template<typename ComplexT>
431 unsigned ComputeCycleNumber(ComplexT const& t0)
432 {
433 using RealT = typename Eigen::NumTraits<ComplexT>::Real;
434
435 const auto& samples = std::get<SampCont<ComplexT> >(samples_);
436 const auto& times = std::get<TimeCont<ComplexT> >(times_);
437
439
440 const Vec<ComplexT> &most_recent_sample = samples.back();
441 const ComplexT& most_recent_time = times.back();
442
443 //Compute upper bound for cycle number.
445
446
447 unsigned num_pts;
448 if (samples.size() > this->EndgameSettings().num_sample_points)
449 num_pts = this->EndgameSettings().num_sample_points;
450 else
451 num_pts = this->EndgameSettings().num_sample_points-1;
452
453
454 auto min_found_difference = Eigen::NumTraits<RealT>::highest();
455
456 // Default the selection before the candidate loop: if every candidate's difference is
457 // NaN (poisoned samples), the comparisons below are all false and nothing is assigned --
458 // a fresh endgame would otherwise carry cycle number 0 into TransformToSPlane and throw.
459 this->cycle_number_ = 1;
460
461 TimeCont<ComplexT> s_times(num_pts);
462 SampCont<ComplexT> s_derivatives(num_pts);
463
464 for(unsigned int candidate = 1; candidate <= upper_bound_on_cycle_number_; ++candidate)
465 {
466 using std::pow;
467
468 std::tie(s_times, s_derivatives) = TransformToSPlane(static_cast<int>(candidate), t0, num_pts, ContStart::Front);
469 RealT cand_power{1/static_cast<RealT>(candidate)};
470 RealT curr_diff = (HermiteInterpolateAndSolve<ComplexT>(
471 pow((most_recent_time-t0)/(times[0]-t0),cand_power), // the target time
472 num_pts,s_times,samples,s_derivatives, ContStart::Front) // the input data
473 -
474 most_recent_sample).template lpNorm<Eigen::Infinity>();
475
476 if (curr_diff < min_found_difference)
477 {
478 min_found_difference = curr_diff;
479 this->cycle_number_ = candidate;
480 }
481
482 }// end cc loop over cycle number possibilities
483
484 return this->cycle_number_;
485 }//end ComputeCycleNumber
486
487
488
501 template<typename ComplexT>
503 {
504 auto& samples = std::get<SampCont<ComplexT> >(samples_);
505 auto& times = std::get<TimeCont<ComplexT> >(times_);
506 auto& derivatives = std::get<SampCont<ComplexT> >(derivatives_);
507
508 assert((samples.size() == times.size()) && "must have same number of times and samples");
509
510 if constexpr (tracking::TrackerTraits<TrackerType>::IsAdaptivePrec) // known at compile time
511 {
512 auto max_precision = this->EnsureAtUniformPrecision(times, samples);
513 }
514
515 //Compute dx_dt for each sample.
516 derivatives.clear(); derivatives.resize(samples.size());
517 for(unsigned ii = 0; ii < samples.size(); ++ii)
518 {
519 derivatives[ii] = -this->GetSystem().Jacobian(samples[ii],times[ii]).lu().solve(this->GetSystem().TimeDerivative(samples[ii],times[ii]));
520 }
521 }
522
523
534 template <typename ComplexT>
535 std::tuple<TimeCont<ComplexT>, SampCont<ComplexT>> TransformToSPlane(int cycle_num, ComplexT const& t0, unsigned num_pts, ContStart shift_from)
536 {
537 if (cycle_num==0)
538 throw std::runtime_error("cannot transform to s plane with cycle number 0");
540
541
542 using RealT = typename Eigen::NumTraits<ComplexT>::Real;
543
544 const auto& times = std::get<TimeCont<ComplexT> >(times_);
545 const auto& derivatives = std::get<SampCont<ComplexT> >(derivatives_);
546
547 RealT c = static_cast<RealT>(cycle_num);
548 RealT one_over_c = 1/c;
549
550 size_t offset_t, offset_d;
551 if (shift_from == ContStart::Back)
552 {
553 offset_t = times.size()-num_pts;
554 offset_d = derivatives.size()-num_pts;
555 }
556 else
557 offset_t = offset_d = 0;
558
559
560 TimeCont<ComplexT> s_times(num_pts);
561 SampCont<ComplexT> s_derivatives(num_pts);
562
563 ComplexT time_shift = times[offset_t] - t0;
564
565 for(unsigned ii = 0; ii < num_pts; ++ii){
566 s_times[ii] = pow((times[ii+offset_t]-t0)/time_shift, one_over_c);
567 s_derivatives[ii] = derivatives[ii+offset_d]*( c*pow(s_times[ii],cycle_num-1))*time_shift;
568 }
569
570 return std::make_tuple(s_times, s_derivatives);
571 }
572
573
574
591 template<typename ComplexT>
593 {
594 const auto c = ComputeCycleNumber<ComplexT>(t0);
595
596 auto num_pts = this->EndgameSettings().num_sample_points;
597
598 TimeCont<ComplexT> s_times;
599 SampCont<ComplexT> s_derivatives;
600
601 std::tie(s_times, s_derivatives) = TransformToSPlane(static_cast<int>(c), t0, num_pts, ContStart::Back);
602 // the data was transformed to be on the interval [0 1] so we can hard-code the time-to-solve as 0 here.
603
604 Precision(result, Precision(s_derivatives.back()));
605 result = HermiteInterpolateAndSolve(ComplexT(0), num_pts, s_times, std::get<SampCont<ComplexT> >(samples_), s_derivatives, ContStart::Back);
606 // A NaN extrapolation must be a FAILURE code. The run loop converges on
607 // `approx_error > FinalTolerance()` becoming false, and every IEEE comparison against
608 // NaN is false -- so a NaN approximation would exit the loop down the SUCCESS path,
609 // reporting Converged with a poisoned answer.
610 if (bertini::ContainsNaN(result))
613 }//end ComputeApproximationOfXAtT0
614
615
616
632 template<typename ComplexT>
633 SuccessCode AdvanceTime(const ComplexT & target_time)
634 {
635 using RealT = typename Eigen::NumTraits<ComplexT>::Real;
636
637 auto& samples = std::get<SampCont<ComplexT> >(samples_);
638 auto& times = std::get<TimeCont<ComplexT> >(times_);
639
641
642 Vec<ComplexT> next_sample;
643 ComplexT next_time = (times.back() + target_time) * static_cast<RealT>(this->EndgameSettings().sample_factor); //setting up next time value using the midpoint formula, sample_factor will give us some
644
645 if (abs(next_time - target_time) < this->EndgameSettings().min_track_time) // generalized for target_time not equal to 0.
646 {
649 }
650
651 SuccessCode tracking_success = this->EndgameTrackPath(next_sample,times.back(),next_time,samples.back());
652 if (tracking_success != SuccessCode::Success)
653 return tracking_success;
654
655 // Pure-(i) escalation: return BEFORE pushing the new sample so the window stays an untouched
656 // checkpoint; the driver migrates to mpfr and retries this advance. Elided for fixed precision.
658 if (this->adaptive_numeric_type_active_ &&
659 this->GetTracker().GetCurrentPrecision() > this->current_endgame_precision_)
661
662 this->EnsureAtPrecision(next_time,Precision(next_sample));
663
664
665 times.push_back(next_time);
666 samples.push_back(next_sample);
667
668
669
670
671 auto refine_success = this->RefineSample(samples.back(), next_sample, times.back(),
672 this->FinalTolerance() * this->EndgameSettings().sample_point_refinement_factor,
673 this->EndgameSettings().max_num_refinements);
674 if (refine_success != SuccessCode::Success)
675 {
676 // Adaptive-numeric-type: a refine that double cannot satisfy is a request to cross to mpfr.
677 // Roll back the just-pushed sample so the window is a clean checkpoint, then signal the driver.
679 if (this->adaptive_numeric_type_active_ &&
680 (refine_success == SuccessCode::HigherPrecisionNecessary || refine_success == SuccessCode::FailedToConverge))
681 {
682 times.pop_back();
683 samples.pop_back();
685 }
686 NotifyObservers(RefiningFailed<EmitterType>(*this));
687 return refine_success;
688 }
689
690 this->EnsureAtPrecision(times.back(),Precision(samples.back()));
691
692 NotifyObservers(SampleRefined<EmitterType>(*this));
693 // the sample is complete once the new sample has been refined -- emit the
694 // REFINED value, not the freshly-tracked one pushed above
695 // ComputedSamplePoint carries the sample/time at BaseComplexT, like CircleAdvanced.
696 // The fixed/mpfr lane emits directly; the complex_dbl fast lane would have to convert
697 // the point to mpfr for the event, so we only pay that when something is observing
698 // (temporaries live through the synchronous NotifyObservers).
699 if constexpr (std::is_same<ComplexT, BCT>::value)
700 NotifyObservers(ComputedSamplePoint<EmitterType>(*this, samples.back(), times.back()));
701 else if (this->HasObservers())
702 {
703 Vec<BCT> ev_pt(samples.back().size());
704 for (Eigen::Index i = 0; i < samples.back().size(); ++i) ev_pt(i) = BCT(samples.back()(i));
705 BCT ev_t(times.back());
707 }
708
709 // we keep one more samplepoint than needed around, for estimating the cycle number
710 if (times.size() > this->EndgameSettings().num_sample_points+1)
711 {
712 times.pop_front();
713 samples.pop_front();
714 }
715
716 // Has the path entered the endgame operating zone? The same test the Cauchy endgame
717 // uses, over the same kind of window, so the event asserts the same thing in both
718 // (b2#402). It used to fire on every advance here, which made it mean "advanced".
720
722 }
723
724
738 template<typename ComplexT>
740 {
741 if (announced_operating_zone_ || samples.size() < 3)
742 return;
743
744 auto const needed = this->EndgameSettings().num_needed_for_stabilization;
745 auto& c_over_k = std::get<TimeCont<ComplexT> >(c_over_k_);
746 c_over_k.push_back(this->template ComputeCOverK<ComplexT>(samples));
747 while (c_over_k.size() > needed + 1)
748 c_over_k.pop_front();
749
750 if (c_over_k.size() > needed && this->CheckForCOverKStabilization(c_over_k))
751 {
753 NotifyObservers(InEGOperatingZone<EmitterType>(*this));
754 }
755 }
756
757
774 template<typename ComplexT>
775 SuccessCode RunImpl(const ComplexT & start_time, const Vec<ComplexT> & start_point, ComplexT const& target_time)
776 {
777 if (start_point.size()!=static_cast<Eigen::Index>(this->GetSystem().NumVariables()))
778 {
779 std::stringstream err_msg;
780 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();
781 throw std::runtime_error(err_msg.str());
782 }
783
784 // thread-local only: the endgame may run on a std::thread worker.
785 SetThreadPrecision(Precision(start_point));
786
787 using RealT = typename Eigen::NumTraits<ComplexT>::Real;
788 //Set up for the endgame.
790
791
792 // unpack some references for easy use
793 auto& samples = std::get<SampCont<ComplexT> >(samples_);
794 auto& times = std::get<TimeCont<ComplexT> >(times_);
795 Vec<ComplexT>& latest_approx = this->final_approximation_;
796 Vec<ComplexT>& prev_approx = this->previous_approximation_;
797
798 // this is for estimating a ... norm?
799 SetRandVec<ComplexT>(static_cast<int>(start_point.size()));
800
801
802 auto initial_sample_success = this->ComputeInitialSamples(start_time, target_time, start_point, times, samples);
803
804 if (initial_sample_success!=SuccessCode::Success)
805 {
806 NotifyObservers(EndgameFailure<EmitterType>(*this));
807 return initial_sample_success;
808 }
809
810 this->template RefineAllSamples<ComplexT>(samples, times);
812
813
814
815 auto extrapolation_code = ComputeApproximationOfXAtT0(prev_approx, target_time);
816 latest_approx = prev_approx;
817
818 if (extrapolation_code != SuccessCode::Success)
819 return extrapolation_code;
820
821 RealT norm_of_dehom_of_latest_approx(0); // initialized to 0 so the security check never reads indeterminate values
822 RealT norm_of_dehom_of_prev_approx(0);
823 if (this->SecuritySettings().level <= 0)
824 norm_of_dehom_of_prev_approx = this->GetSystem().InfinityNormOfDehomogenized(prev_approx);
825
826
827 NumErrorT& approx_error = this->approximate_error_;
828 approx_error = 1;
829
830 while (approx_error > this->FinalTolerance())
831 {
832 auto advance_code = AdvanceTime<ComplexT>(target_time);
833 if (advance_code!=SuccessCode::Success)
834 {
835 NotifyObservers(EndgameFailure<EmitterType>(*this));
836 return advance_code;
837 }
838
839 // this code is what bertini1 does... it refines all samples, like, all the time.
840 this->template RefineAllSamples<ComplexT>(samples, times);
842
843 extrapolation_code = ComputeApproximationOfXAtT0(latest_approx, target_time);
844 if (extrapolation_code!=SuccessCode::Success)
845 {
846 NotifyObservers(EndgameFailure<EmitterType>(*this));
847 return extrapolation_code;
848 }
849
850 approx_error = static_cast<NumErrorT>((latest_approx - prev_approx).template lpNorm<Eigen::Infinity>());
851 NotifyObservers(ApproximatedRoot<EmitterType>(*this));
852
853
854 if(this->SecuritySettings().level <= 0)
855 {
856 norm_of_dehom_of_latest_approx = this->GetSystem().InfinityNormOfDehomogenized(latest_approx);
857 if(this->BeyondSecurityMaxNorm(norm_of_dehom_of_latest_approx) && this->BeyondSecurityMaxNorm(norm_of_dehom_of_prev_approx))
858 {
861 }
862 norm_of_dehom_of_prev_approx = norm_of_dehom_of_latest_approx;
863 }
864
865
866
867
868 // CONVERGED. Break BEFORE the assignment below so previous_approximation_ keeps
869 // holding the genuine PREDECESSOR of final_approximation_. The Cauchy flavor
870 // already behaves this way -- it returns from its acceptance gate before the
871 // corresponding assignment -- and the two must agree, because the public
872 // accessors FinalApproximation()/PreviousApproximation()/ApproximateError() are
873 // only coherent as a triple: overwriting here made previous == final on every
874 // successful run, so the reported error described a pair the caller could not
875 // see. The loop's own condition is left in place so a FinalTolerance() >= 1
876 // still refuses to enter at all, exactly as before.
877 if (approx_error <= this->FinalTolerance())
878 break;
879
880 Precision(prev_approx, Precision(latest_approx));
881 prev_approx = latest_approx;
882 } //end while
883
884 NotifyObservers(Converged<EmitterType>(*this));
886
887 } //end PSEG
888
889
890 // ================================================================================================
891 // Adaptive-numeric-type (double-first) PowerSeries endgame. Mirrors the Cauchy flavor: compute in
892 // the hardware complex_dbl fast lane while the tracker's authoritative precision stays double, and
893 // cross to complex_mp only when the tracker escalates (pure-(i)). Member templates so the explicit
894 // fixed-precision class instantiations never force-compile them. Fixed precision uses RunImpl<BCT>.
895 // ================================================================================================
896
898 template<typename Dummy = void>
899 SuccessCode RunImplAMP(BCT const& start_time, Vec<BCT> const& start_point, BCT const& target_time)
900 {
901 using bertini::Precision;
902 using RealT = typename Eigen::NumTraits<BCT>::Real;
903
904 if (start_point.size()!=static_cast<Eigen::Index>(this->GetSystem().NumVariables()))
905 {
906 std::stringstream err_msg;
907 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();
908 throw std::runtime_error(err_msg.str());
909 }
910
911 this->adaptive_numeric_type_active_ = true;
912 struct Disarmer { bool& flag; ~Disarmer(){ flag = false; } } disarm{this->adaptive_numeric_type_active_};
913
914 // Both lanes start empty. A segment clears only the lane it runs in, so a path whose
915 // endgame stays in double would otherwise leave an earlier path's multiprecision times in
916 // place -- and LatestTime reads that lane first whenever it is non-empty, reporting the
917 // earlier path's last time as this one's (#378).
920
921 this->current_endgame_precision_ = std::max(DoublePrecision(), Precision(start_point));
922
923 // ---- SETUP (restart-in-mpfr on escalation; bounded) ----
924 while (true)
925 {
926 SuccessCode code = (this->current_endgame_precision_==DoublePrecision())
927 ? SetupSegmentT<complex_dbl>(complex_dbl(start_time), this->DowncastToDouble(start_point), complex_dbl(target_time))
928 : SetupSegmentT<complex_mp>(this->AtActivePrecisionScalar(start_time), this->AtActivePrecisionVec(start_point), this->AtActivePrecisionScalar(target_time));
930 {
931 auto const previous_precision = this->current_endgame_precision_;
932 this->current_endgame_precision_ = this->NextEscalatedPrecision();
933 SetThreadPrecision(this->current_endgame_precision_);
934 // the samples announced at the lower precision are superseded (the window is
935 // tracked again at the new one); say so, so a collector drops them as we do
936 NotifyObservers(PrecisionChanged<EmitterType>(*this, previous_precision, this->current_endgame_precision_));
937 NotifyObservers(SamplesRecomputedAtHigherPrecision<EmitterType>(*this));
938 continue;
939 }
940 if (code != SuccessCode::Success)
941 return code;
942 break;
943 }
944
945 // previous_approximation_ (BCT) holds the first extrapolation, set by SetupSegmentT.
946 RealT norm_prev(0), norm_latest(0);
947 if (this->SecuritySettings().level <= 0)
948 norm_prev = this->GetSystem().InfinityNormOfDehomogenized(this->previous_approximation_);
949
950 this->approximate_error_ = 1;
951
952 // ---- MAIN LOOP: each tracker-touching phase self-heals (migrate-and-retry in mpfr) ----
953 while (this->approximate_error_ > this->FinalTolerance())
954 {
955 auto adv = AdvanceTimeAMP(target_time);
956 if (adv != SuccessCode::Success) { NotifyObservers(EndgameFailure<EmitterType>(*this)); return adv; }
957
958 auto ref = RefineAllSamplesAMP();
959 if (ref != SuccessCode::Success) return ref;
960
962
963 auto ext = ComputeApproxAMP(target_time); // -> final_approximation_ (BCT)
964 if (ext != SuccessCode::Success) { NotifyObservers(EndgameFailure<EmitterType>(*this)); return ext; }
965
967 this->approximate_error_ = static_cast<NumErrorT>((this->final_approximation_ - this->previous_approximation_).template lpNorm<Eigen::Infinity>());
968 NotifyObservers(ApproximatedRoot<EmitterType>(*this));
969
970 if (this->SecuritySettings().level <= 0)
971 {
972 norm_latest = this->GetSystem().InfinityNormOfDehomogenized(this->final_approximation_);
973 if (this->BeyondSecurityMaxNorm(norm_latest) && this->BeyondSecurityMaxNorm(norm_prev))
974 {
977 }
978 norm_prev = norm_latest;
979 }
980
981 // CONVERGED. Break BEFORE the assignment below so previous_approximation_ keeps
982 // holding the genuine PREDECESSOR of final_approximation_. The Cauchy flavor
983 // already behaves this way -- it returns from its acceptance gate before the
984 // corresponding assignment -- and the two must agree, because the public
985 // accessors FinalApproximation()/PreviousApproximation()/ApproximateError() are
986 // only coherent as a triple: overwriting here made previous == final on every
987 // successful run, so the reported error described a pair the caller could not
988 // see. The loop's own condition is left in place so a FinalTolerance() >= 1
989 // still refuses to enter at all, exactly as before.
991 break;
992
994 }
995
996 NotifyObservers(Converged<EmitterType>(*this));
998 }
999
1000
1001 // Pre-loop work + first extrapolation, at one numeric type. Reports HigherPrecisionNecessary up to
1002 // the driver (which restarts setup in mpfr) on escalation.
1004 template<typename ComplexT>
1005 SuccessCode SetupSegmentT(ComplexT const& start_time, Vec<ComplexT> const& start_point, ComplexT const& target_time)
1006 {
1007 SetThreadPrecision(Precision(start_point));
1009
1010 auto& samples = std::get<SampCont<ComplexT> >(samples_);
1011 auto& times = std::get<TimeCont<ComplexT> >(times_);
1012
1013 SetRandVec<ComplexT>(static_cast<int>(start_point.size()));
1014
1015 auto init = this->ComputeInitialSamples(start_time, target_time, start_point, times, samples);
1016 if (init != SuccessCode::Success) { NotifyObservers(EndgameFailure<EmitterType>(*this)); return init; }
1017 if (this->GetTracker().GetCurrentPrecision() > this->current_endgame_precision_)
1019
1020 auto ref = this->template RefineAllSamples<ComplexT>(samples, times);
1023 if (ref != SuccessCode::Success) return ref;
1024
1026
1027 Vec<ComplexT> first_approx;
1028 auto ext = ComputeApproximationOfXAtT0<ComplexT>(first_approx, target_time);
1029 if (ext != SuccessCode::Success) return ext;
1030 this->previous_approximation_ = this->ToBCT(first_approx, this->current_endgame_precision_);
1031 return SuccessCode::Success;
1032 }
1033
1034
1036 template<typename Dummy = void>
1037 SuccessCode AdvanceTimeAMP(BCT const& target_time)
1038 {
1039 unsigned guard = 0;
1040 while (true)
1041 {
1042 SuccessCode code = (this->current_endgame_precision_==DoublePrecision())
1044 : AdvanceTime<complex_mp>(this->AtActivePrecisionScalar(target_time));
1046 {
1048 continue;
1049 }
1050 return code;
1051 }
1052 }
1053
1055 template<typename Dummy = void>
1057 {
1058 unsigned guard = 0;
1059 while (true)
1060 {
1061 SuccessCode code = (this->current_endgame_precision_==DoublePrecision())
1065 {
1067 continue;
1068 }
1069 return code;
1070 }
1071 }
1072
1074 template<typename Dummy = void>
1076 {
1077 if (this->current_endgame_precision_==DoublePrecision())
1079 else
1081 }
1082
1084 template<typename Dummy = void>
1086 {
1087 if (this->current_endgame_precision_==DoublePrecision())
1088 {
1090 auto code = ComputeApproximationOfXAtT0<complex_dbl>(r, complex_dbl(target_time));
1091 if (code == SuccessCode::Success)
1092 this->final_approximation_ = this->ToBCT(r, this->current_endgame_precision_);
1093 return code;
1094 }
1095 else
1096 {
1097 return ComputeApproximationOfXAtT0<complex_mp>(this->final_approximation_, this->AtActivePrecisionScalar(target_time));
1098 }
1099 }
1100
1101
1102 // Flavor-specific: cross every PowerSeries container from the complex_dbl slot to complex_mp (or, if
1103 // already mpfr, raise its precision uniformly), via the shared AMP-policy Cross* / SetPrecision
1104 // helpers. Called by the base EscalateAndMigrate.
1106 template<typename Dummy = void>
1107 void MigrateContainersToPrecision(unsigned newprec)
1108 {
1109 using bertini::Precision;
1110 if (this->current_endgame_precision_ == DoublePrecision())
1111 {
1112 this->CrossTimesUp(times_, newprec);
1113 this->CrossSampsUp(samples_, newprec);
1114 this->CrossSampsUp(derivatives_, newprec);
1115 this->CrossVecUp (rand_vector_, newprec);
1116 }
1117 else
1118 {
1122 auto& rv = std::get<Vec<complex_mp> >(rand_vector_);
1123 if (rv.size() > 0) Precision(rv, newprec);
1124 }
1125 if (this->final_approximation_.size() > 0) Precision(this->final_approximation_, newprec);
1126 if (this->previous_approximation_.size() > 0) Precision(this->previous_approximation_, newprec);
1127 }
1128
1129
1130 virtual ~PowerSeriesEndgame() = default;
1131}; // end powerseries class
1132
1133
1134
1135
1136}} // re: namespaces
Contains parent class, Endgame, the parent class for all endgames.
bool HasObservers() const
Definition observable.hpp:204
void NotifyObservers(AnyEvent const &e) const
Definition observable.hpp:185
A new sample point on the path was computed, adding one point to the sequence of the endgame's approa...
Definition events.hpp:118
bool BeyondSecurityMaxNorm(RealT const &norm) const
Definition base_endgame.hpp:521
SuccessCode EndgameTrackPath(Vec< ComplexT > &result, ComplexT const &start_time, ComplexT const &end_time, Vec< ComplexT > const &start_point) const
Definition base_endgame.hpp:446
typename PrecT::TrackerType TrackerType
The path-tracker type.
Definition base_endgame.hpp:93
auto ComputeCOverK(SampCont< ComplexT > const &samples) const -> typename Eigen::NumTraits< ComplexT >::Real
Definition base_endgame.hpp:237
NumErrorT approximate_error_
Definition base_endgame.hpp:140
typename NeededTypes::template ToTupleOfCont< SampCont > TupleOfSamps
A tuple of sample containers, one per numeric type.
Definition base_endgame.hpp:127
const auto & SecuritySettings() const
Definition base_endgame.hpp:511
SuccessCode ComputeInitialSamples(const ComplexT &start_time, const ComplexT &target_time, const Vec< ComplexT > &x_endgame_start, TimeCont< ComplexT > &times, SampCont< ComplexT > &samples)
Definition base_endgame.hpp:656
Vec< BCT > previous_approximation_
Definition base_endgame.hpp:133
SuccessCode EscalateAndMigrate(unsigned guard)
Definition base_endgame.hpp:481
SuccessCode RefineSample(Vec< ComplexT > &result, Vec< ComplexT > const &current_sample, ComplexT const &current_time, NumErrorT tol, unsigned max_iterations) const
Definition base_endgame.hpp:422
EndgameBase(TrackerType const &tr, const ConfigsAsTuple &settings)
Definition base_endgame.hpp:528
SuccessCode RefineAllSamples(SampCont< ComplexT > &samples, TimeCont< ComplexT > &times)
Definition base_endgame.hpp:390
typename tracking::TrackerTraits< TrackerType >::BaseComplexT BaseComplexT
The complex number type.
Definition base_endgame.hpp:95
Vec< BCT > final_approximation_
Definition base_endgame.hpp:132
typename NeededTypes::template ToTupleOfCont< TimeCont > TupleOfTimes
A tuple of time containers, one per numeric type.
Definition base_endgame.hpp:126
const auto & EndgameSettings() const
Definition base_endgame.hpp:504
typename NeededTypes::ToTupleOfVec TupOfVec
A tuple of vector containers, one per numeric type.
Definition base_endgame.hpp:124
const auto & FinalTolerance() const
Definition base_endgame.hpp:552
unsigned int cycle_number_
Definition base_endgame.hpp:134
typename tracking::TrackerTraits< TrackerType >::BaseRealT BaseRealT
The real number type.
Definition base_endgame.hpp:96
A common base type for various precision types, fixed and adaptive.
Definition prec_base.hpp:40
const auto & GetSamples() const
Function to get the space values used for the Power Series endgame.
Definition powerseries.hpp:318
void MigrateContainersToPrecision(unsigned newprec)
Widen this flavor's PowerSeries containers from the complex_dbl slot up to complex_mp at the new prec...
Definition powerseries.hpp:1107
SuccessCode RefineAllSamplesAMP()
Refine all retained samples at the active numeric type, migrating-and-retrying in mpfr on escalation.
Definition powerseries.hpp:1056
SuccessCode RunImplAMP(BCT const &start_time, Vec< BCT > const &start_point, BCT const &target_time)
Run the power-series endgame via the double-first adaptive-numeric-type driver, migrating up to mpfr ...
Definition powerseries.hpp:899
void NoteWhetherInOperatingZone(SampCont< ComplexT > const &samples) const
Watch the c/k estimates settle, and say so once when they have.
Definition powerseries.hpp:739
TupleOfSamps derivatives_
Definition powerseries.hpp:209
BCT latest_time_cache_
Definition powerseries.hpp:288
typename BaseEGT::TupOfVec TupOfVec
Definition powerseries.hpp:183
BaseComplexT BCT
Definition powerseries.hpp:185
const BCT & LatestTimeImpl() const
Definition powerseries.hpp:291
typename BaseEGT::BaseComplexT BaseComplexT
Definition powerseries.hpp:172
TupleOfTimes times_
Definition powerseries.hpp:199
PowerSeriesEndgame< EGPrecT > EmitterType
Definition powerseries.hpp:175
SuccessCode RunImpl(const ComplexT &start_time, const Vec< ComplexT > &start_point, ComplexT const &target_time)
Primary function running the Power Series endgame.
Definition powerseries.hpp:775
void AssertSizesTimeSpace() const
Debug-assert that the stored time and sample containers have consistent, sufficient sizes.
Definition powerseries.hpp:235
typename BaseEGT::TrackerType TrackerType
Definition powerseries.hpp:170
unsigned ComputeBoundOnCycleNumber()
Computes an upper bound on the cycle number.
Definition powerseries.hpp:363
PowerSeriesEndgame(TrackerType const &tr, const Ts &... ts)
Construct the power-series endgame for a tracker, with configs given in any order.
Definition powerseries.hpp:345
void ComputeAllDerivativesAMP()
Compute all sample derivatives at the active numeric type.
Definition powerseries.hpp:1075
const auto & GetTimes() const
Function to get the times used for the Power Series endgame.
Definition powerseries.hpp:285
SuccessCode ComputeApproxAMP(BCT const &target_time)
Compute the power-series extrapolation at the active numeric type, writing into final_approximation_ ...
Definition powerseries.hpp:1085
TupleOfSamps samples_
Definition powerseries.hpp:204
SuccessCode ComputeApproximationOfXAtT0(Vec< ComplexT > &result, const ComplexT &t0)
This function computes an approximation of the space value at the time time_t0.
Definition powerseries.hpp:592
void ClearTimesAndSamples()
Function that clears all samples and times from data members for the Power Series endgame.
Definition powerseries.hpp:266
TupleOfTimes c_over_k_
Definition powerseries.hpp:218
auto UpperBoundOnCycleNumber() const
Definition powerseries.hpp:259
unsigned ComputeCycleNumber(ComplexT const &t0)
This function computes the cycle number using an exhaustive search up the upper bound computed by the...
Definition powerseries.hpp:431
void ComputeAllDerivatives()
Compute a set of derivatives using internal data to the endgame.
Definition powerseries.hpp:502
BaseRealT BRT
Definition powerseries.hpp:186
SuccessCode AdvanceTimeAMP(BCT const &target_time)
Advance time at the active numeric type, migrating-and-retrying in mpfr on escalation.
Definition powerseries.hpp:1037
void SetSamples(SampCont< ComplexT > const &samples_to_set)
Function to set the space values used for the Power Series endgame.
Definition powerseries.hpp:312
PowerSeriesEndgame(TrackerType const &tr, const ConfigsAsTuple &settings)
Construct the power-series endgame for a tracker, with its configuration as a tuple.
Definition powerseries.hpp:337
PowerSeriesEndgame< EGPrecT > FinalEGT
Definition powerseries.hpp:169
typename AlgoTraits< FinalEGT >::NeededConfigs Configs
Definition powerseries.hpp:188
bool announced_operating_zone_
Definition powerseries.hpp:222
void AssertSizesTimeSpaceDeriv() const
Debug-assert that the time, sample, and derivative containers have consistent, sufficient sizes.
Definition powerseries.hpp:246
SuccessCode SetupSegmentT(ComplexT const &start_time, Vec< ComplexT > const &start_point, ComplexT const &target_time)
Definition powerseries.hpp:1005
typename BaseEGT::TupleOfSamps TupleOfSamps
Definition powerseries.hpp:182
unsigned upper_bound_on_cycle_number_
Definition powerseries.hpp:194
std::tuple< TimeCont< ComplexT >, SampCont< ComplexT > > TransformToSPlane(int cycle_num, ComplexT const &t0, unsigned num_pts, ContStart shift_from)
Transform the stored times and derivatives into the S-plane, scaled by the cycle number.
Definition powerseries.hpp:535
typename BaseEGT::BaseRealT BaseRealT
Definition powerseries.hpp:173
TupOfVec rand_vector_
Definition powerseries.hpp:231
EndgameBase< PowerSeriesEndgame< EGPrecT >, EGPrecT > BaseEGT
Definition powerseries.hpp:168
void SetTimes(TimeCont< ComplexT > const &times_to_set)
Function to set the times used for the Power Series endgame.
Definition powerseries.hpp:279
void SetRandVec(int size)
Function to set the times used for the Power Series endgame.
Definition powerseries.hpp:324
SuccessCode AdvanceTime(const ComplexT &target_time)
The samples used in the power series endgame are collected by advancing time to t = 0,...
Definition powerseries.hpp:633
typename BaseEGT::TupleOfTimes TupleOfTimes
Definition powerseries.hpp:181
typename Configs::ToTuple ConfigsAsTuple
Definition powerseries.hpp:189
Event emitted when the endgame's working precision changes.
Definition events.hpp:220
Definition endgames.hpp:44
Vec< ComplexT > HermiteInterpolateAndSolve(ComplexT const &target_time, const unsigned int num_sample_points, const TimeCont< ComplexT > &times, const SampCont< ComplexT > &samples, const SampCont< ComplexT > &derivatives, ContStart shift_from=ContStart::Back)
Estimates the root to interpolating polynomial.
Definition interpolation.hpp:60
Definition configIni_parse.hpp:78
void SetPrecision(SampCont< complex_mp > &samples, unsigned prec)
Sets the precision of each space sample to be of input precision.
Definition adaptive_precision_utilities.hpp:44
Definition algorithm_builder.hpp:36
constexpr unsigned DoublePrecision()
The number of digits considered "double precision" (16).
Definition num_traits.hpp:152
std::complex< double > complex_dbl
The double-precision complex number type.
Definition double_extensions.hpp:47
Eigen::Matrix< NumT, Eigen::Dynamic, 1 > Vec
A dynamically-sized column vector of NumT.
Definition eigen_extensions.hpp:296
T RandomUnit()
Get a random complex number of unit modulus, in number type T.
std::deque< Vec< T > > SampCont
Definition config.hpp:33
complex_dbl pow(const complex_dbl &z, int power)
Compute +,- integral powers of a std::complex<double> number.
Definition double_extensions.hpp:92
void SetThreadPrecision(unsigned prec)
Set the precision (digits) for the current thread only (does not touch the global default).
Definition mpfr_complex.hpp:176
ContStart
Definition config.hpp:36
@ Back
Definition config.hpp:38
@ Front
Definition config.hpp:37
double NumErrorT
The numeric type used to hold error/tolerance magnitudes throughout Bertini2 – tracking tolerances,...
Definition num_traits.hpp:48
std::deque< T > TimeCont
Definition config.hpp:34
SuccessCode
Definition config.hpp:42
@ FailedToConverge
Definition config.hpp:55
@ Success
Definition config.hpp:51
@ MinTrackTimeReached
Definition config.hpp:64
@ HigherPrecisionNecessary
Definition config.hpp:52
@ SecurityMaxNormReached
Definition config.hpp:65
unsigned Precision(Eigen::MatrixBase< Derived > const &v)
Get the precision of an Eigen object.
Definition eigen_extensions.hpp:339
std::tuple< const UnpermutedT &... > Unpermute(const PermutedPresentT &... present_permuted_args)
Reorder a set of present arguments into the declared UnpermutedT order, default-constructing any abse...
Definition enable_permuted_arguments.hpp:117
bool ContainsNaN(Eigen::MatrixBase< Derived > const &v)
True when any entry of a complex-valued Eigen object has a NaN real or imaginary part.
Definition eigen_extensions.hpp:321
const T & Get() const
Definition configured.hpp:94
Definition config.hpp:176
Trait lookup mapping a tracker type to its numeric types, event-emitter type, precision config,...
Definition config.hpp:387