Bertini 2 d882924 (2026-10-06 19:56 UTC)
Numerical algebraic geometry library (C++ core)
Loading...
Searching...
No Matches
cauchy.hpp
Go to the documentation of this file.
1//This file is part of Bertini 2.
2//
3//cauchy_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//cauchy_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 cauchy_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#include "bertini2/trackers/adaptive_precision_utilities.hpp" // tracking::adaptive::SetPrecision, for container migration
26
27
34
35namespace bertini{ namespace endgame{
36
37
163template<typename PrecT>
165 public virtual EndgameBase<CauchyEndgame<PrecT>, PrecT>
166{
167public:
170 using TrackerType = typename PrecT::TrackerType;
171
174
176
177protected:
178
180
181
182
183
186 using TupOfVec = typename BaseEGT::TupOfVec;
187
189 using BRT = BaseRealT;
190
192 using ConfigsAsTuple = typename Configs::ToTuple;
193
201 mutable TupleOfSamps pseg_samples_; //samples used for the first approximation.
210
211 // The c/k probe lives on the base endgame now: the operating-zone test is shared with the
212 // power series endgame (b2#402).
213
214 // Scratch for LatestTimeImpl to return a BCT reference when the endgame is computing in the
215 // complex_dbl fast lane (the latest time then lives in the complex_dbl slot, not the BCT slot).
217
218
219
220
221
222public:
223
224
225
226
230 template<typename ComplexT>
232 {
233 std::get<TimeCont<ComplexT> >(pseg_times_).clear();
234 std::get<TimeCont<ComplexT> >(cauchy_times_).clear();
235 std::get<SampCont<ComplexT> >(pseg_samples_).clear();
236 std::get<SampCont<ComplexT> >(cauchy_samples_).clear();}
237
240 template<typename ComplexT>
241 void SetPSEGTimes(TimeCont<ComplexT> pseg_times_to_set)
242 { std::get<TimeCont<ComplexT> >(pseg_times_) = pseg_times_to_set;}
243
247 template<typename ComplexT>
248 TimeCont<ComplexT>& GetPSEGTimes() {return std::get<TimeCont<ComplexT> >(pseg_times_);}
250 template<typename ComplexT>
251 const TimeCont<ComplexT>& GetPSEGTimes() const {return std::get<TimeCont<ComplexT> >(pseg_times_);}
252
256 template<typename ComplexT>
257 void SetPSEGSamples(SampCont<ComplexT> const& pseg_samples_to_set) { std::get<SampCont<ComplexT> >(pseg_samples_) = pseg_samples_to_set;}
258
264 template<typename ComplexT>
265 SampCont<ComplexT>& GetPSEGSamples() {return std::get<SampCont<ComplexT> >(pseg_samples_);}
267 template<typename ComplexT>
268 const SampCont<ComplexT>& GetPSEGSamples() const {return std::get<SampCont<ComplexT> >(pseg_samples_);}
272 template<typename ComplexT>
273 void SetCauchySamples(SampCont<ComplexT> const& cauchy_samples_to_set)
274 {
275 std::get<SampCont<ComplexT> >(cauchy_samples_) = cauchy_samples_to_set;
276 }
277
283 template<typename ComplexT>
285 {
286 return std::get<SampCont<ComplexT> >(cauchy_samples_);
287 }
288
289 template<typename ComplexT>
290 const SampCont<ComplexT>& GetCauchySamples() const { return std::get<SampCont<ComplexT> >(cauchy_samples_); }
291
292
296 template<typename ComplexT>
297 void SetCauchyTimes(TimeCont<ComplexT> const& cauchy_times_to_set)
298 {
299 std::get<TimeCont<ComplexT> >(cauchy_times_) = cauchy_times_to_set;
300 }
301
305 template<typename ComplexT>
307 {
308 return std::get<TimeCont<ComplexT> >(cauchy_times_);
309 }
310
311 template<typename ComplexT>
313 {
314 return std::get<TimeCont<ComplexT> >(cauchy_times_);
315 }
316
317
319 const BCT& LatestTimeImpl() const
320 {
321 // In the adaptive-numeric-type endgame the latest time may live in the complex_dbl slot (the
322 // fast lane), with the BCT slot empty. Dispatch on which slot actually holds data, so this is
323 // correct both during the run (observer events) and after it (solution metadata), regardless of
324 // the adaptive_numeric_type_active_ flag. Fixed precision compiles to the original BCT read.
326 {
327 if (GetPSEGTimes<BCT>().empty())
328 {
329 auto const& dbl_times = GetPSEGTimes<complex_dbl>();
330 latest_time_cache_ = dbl_times.empty() ? BCT(0) : BCT(dbl_times.back());
331 return latest_time_cache_;
332 }
333 }
334 return GetPSEGTimes<BCT>().back();
335 }
336
337
341 void SetCauchySettings(CauchyConfig const& new_cauchy_settings)
342 {
343 this->template Set<CauchyConfig>(new_cauchy_settings);
344 }
345
349 const auto& GetCauchySettings() const
350 {
351 return this->template Get<CauchyConfig>();
352 }
353
354
356 explicit CauchyEndgame(TrackerType const& tr,
357 const ConfigsAsTuple& settings )
359 { }
360
362 template< typename... Ts >
363 CauchyEndgame(TrackerType const& tr, const Ts&... ts ) : CauchyEndgame(tr, Configs::Unpermute( ts... ) )
364 {}
365
366
367 virtual ~CauchyEndgame() = default;
368
371 {
372 if (this->EndgameSettings().num_sample_points < 3) // need to make sure we won't track right through the origin.
373 {
374 std::stringstream err_msg;
375 err_msg << "ERROR: The number of sample points " << this->EndgameSettings().num_sample_points << " for circle tracking must be >= 3";
376 throw std::runtime_error(err_msg.str());
377 }
378 }
379
397 template<typename ComplexT>
398 SuccessCode CircleTrack(ComplexT const& target_time)
399 {
400 using bertini::Precision;
401 using RealT = typename Eigen::NumTraits<ComplexT>::Real;
402 using std::acos;
403
405
406 auto& circle_times = std::get<TimeCont<ComplexT> >(cauchy_times_);
407 auto& circle_samples = std::get<SampCont<ComplexT> >(cauchy_samples_);
408
409 ComplexT starting_time = circle_times.back(); // take a COPY here, so won't invalidate it later
410
411 // the initial sample has already been added to the sample repo... so don't do that here, please
412
413 const auto num_vars = this->GetSystem().NumVariables();
414
415 for (unsigned ii = 0; ii < this->EndgameSettings().num_sample_points; ++ii)
416 {
417 const Vec<ComplexT>& current_sample = circle_samples.back();
418 const ComplexT& current_time = circle_times.back();
419
420#ifndef BERTINI_DISABLE_PRECISION_CHECKS
421 if (Precision(current_time)!=Precision(current_sample)){
422 std::stringstream err_msg;
423 err_msg << "current time and sample for circle track must be of same precision. respective precisions: " << Precision(current_time) << " " << Precision(current_sample) << std::endl;
424 throw std::runtime_error(err_msg.str());
425 }
426#endif
427
428 //set up the time value for the next sample.
429 using std::polar;
430
431#ifndef USE_BMP_COMPLEX
432 using bertini::polar;
433#endif
434
435 //Generalized since we could have a nonzero target time.
436 using std::arg;
437 RealT radius = abs(starting_time - target_time), angle = arg(starting_time - target_time); // generalized for nonzero target_time.
438
439 auto next_sample = Vec<ComplexT>(num_vars);
440 ComplexT next_time = (ii==this->EndgameSettings().num_sample_points-1)
441 ?
442 starting_time
443 :
444 polar(radius, (ii+1)*2*acos(static_cast<RealT>(-1)) / (this->EndgameSettings().num_sample_points) + angle) + target_time;
445 // If we are tracking to a nonzero target time we need to shift our values to track to. This is a step that may not be needed if target_time = 0
446 ;
447
448
449 auto tracking_success = this->EndgameTrackPath(next_sample, current_time, next_time, current_sample);
450 if (tracking_success != SuccessCode::Success)
451 {
452 return tracking_success;
453 }
454
455 // Pure-(i) numeric-type escalation: if the tracker's authoritative precision climbed above
456 // the precision this endgame is computing in, double no longer suffices for this circle.
457 // Bail to the migrate-and-retry driver (RunImplAMP), which discards this partial circle,
458 // migrates the durable state to mpfr, and re-tracks the circle in mpfr. Compile-time elided
459 // for fixed precision (and shields GetCurrentPrecision(), which fixed trackers lack).
461 if (this->adaptive_numeric_type_active_ &&
462 this->GetTracker().GetCurrentPrecision() > this->current_endgame_precision_)
464
465 // CircleAdvanced carries the new point/time at BaseComplexT. The fixed/mpfr lane emits exactly
466 // as before. The complex_dbl fast lane would have to convert the point to mpfr for the event,
467 // so we only pay that when something is actually observing (temporaries live through the
468 // synchronous NotifyObservers).
469 if constexpr (std::is_same<ComplexT, BCT>::value)
470 NotifyObservers(CircleAdvanced<EmitterType>(*this, next_sample, next_time));
471 else if (this->HasObservers())
472 {
473 Vec<BCT> ev_pt(next_sample.size());
474 for (Eigen::Index i = 0; i < next_sample.size(); ++i) ev_pt(i) = BCT(next_sample(i));
475 BCT ev_t(next_time);
476 NotifyObservers(CircleAdvanced<EmitterType>(*this, ev_pt, ev_t));
477 }
478
479
480 this->EnsureAtPrecision(next_time,Precision(next_sample)); assert(Precision(next_time)==Precision(next_sample));
481
482 // auto refinement_success = this->RefineSample(next_sample, next_sample, next_time,
483 // this->FinalTolerance() * this->EndgameSettings().sample_point_refinement_factor,
484 // this->EndgameSettings().max_num_refinements);
485 // if (refinement_success != SuccessCode::Success)
486 // {
487 // return refinement_success;
488 // }
489
490 // this->EnsureAtPrecision(next_time,Precision(next_sample));
491 // assert(Precision(next_time)==Precision(next_sample));
492
493 AddToCauchyData(next_time, next_sample);
494
495 // NotifyObservers(SampleRefined<EmitterType>(*this));
496 }
497
499
500 }//end CircleTrack
501
503 template<typename ComplexT>
504 void AddToCauchyData(ComplexT const& time, Vec<ComplexT> const& sample)
505 {
506 std::get<TimeCont<ComplexT>>(cauchy_times_).push_back(time);
507 std::get<SampCont<ComplexT>>(cauchy_samples_).push_back(sample);
508 }
509
511 template<typename ComplexT>
512 void AddToPSData(ComplexT const& time, Vec<ComplexT> const& sample)
513 {
514 std::get<TimeCont<ComplexT>>(pseg_times_).push_back(time);
515 std::get<SampCont<ComplexT>>(pseg_samples_).push_back(sample);
516 }
517
518 // GetCOverKProbe, ComputeCOverK and CheckForCOverKStabilization now live on the base endgame:
519 // the estimate reads only the geometric approach samples, which every flavor maintains, so the
520 // operating-zone test is shared rather than Cauchy's alone (b2#402). The Cauchy endgame
521 // passes its own pseg_samples_ to ComputeCOverK.
522
523
524
525 /*
526 Input: A time value and the space value above that time.
527
528 Output: An real_mp representing a tolerance threshold for declaring a loop to be closed.
529 Details: Used in Bertini 1 as a heuristic for computing separatedness of roots. Decided to not be used since assumptions for this tolerance are not usually met.
530 template<typename ComplexT>
531 real_mp FindToleranceForClosedLoop(ComplexT x_time, Vec<ComplexT> x_sample)
532 {
533 auto degree_max = std::max(this->GetTracker().AMP_config_.degree_bound,real_mp("2.0"));
534 auto K = this->GetTracker().AMP_config_.coefficient_bound;
535 real_mp N;
536 real_mp M;
537 real_mp L;
538 if(max_closed_loop_tolerance_ < min_closed_loop_tolerance_)
539 {
540 max_closed_loop_tolerance_ = min_closed_loop_tolerance_;
541 }
542 auto error_tolerance = real_mp("1e-13");
543 if(x_sample.size() <= 1)
544 {
545 N = degree_max;
546 }
547 else
548 {
549 N = ComputeCombination(degree_max + x_sample[0].precision() - 1, x_sample[0].precision() - 1);
550 }
551 M = degree_max * (degree_max - 1) * N;
552 auto jacobian_at_current_time = this->GetSystem().Jacobian(x_sample,x_time);
553 auto minimum_singular_value = Eigen::JacobiSVD< Mat<ComplexT> >(jacobian_at_current_time).singularValues()(this->GetSystem().NumVariables() - 1 );
554 auto norm_of_sample = x_sample.norm();
555 L = pow(norm_of_sample,degree_max - 2);
556 auto tol = K * L * M;
557 if (tol == 0) // fail-safe
558 tol = minimum_singular_value;
559 else
560 {
561 tol = real_mp("2.0") / tol;
562 tol = tol * minimum_singular_value;
563 }
564 // make sure that tol is between min_tol & max_tol
565 if (tol > max_closed_loop_tolerance_) // tol needs to be <= max_tol
566 tol = max_closed_loop_tolerance_;
567 if (tol < min_closed_loop_tolerance_) // tol needs to be >= min_tol
568 tol = min_closed_loop_tolerance_;
569 return tol;
570 }// end FindToleranceForClosedLoop
571 */
572
573
574
589 template<typename ComplexT>
591 {
592 auto& times = std::get<TimeCont<ComplexT> >(cauchy_times_);
593 auto& samples = std::get<SampCont<ComplexT> >(cauchy_samples_);
594
595 if((samples.front() - samples.back()).template lpNorm<Eigen::Infinity>() < this->GetTracker().TrackingTolerance())
596 {
597 return true;
598 }
599
601 {
602 //Ensure all samples are of the same precision.
603 auto new_precision = this->EnsureAtUniformPrecision(times, samples);
604 }
605
606 this->GetTracker().Refine(samples.front(),samples.front(),times.front(),this->FinalTolerance(),this->EndgameSettings().max_num_refinements);
607 this->GetTracker().Refine(samples.back(),samples.back(),times.back(),this->FinalTolerance(),this->EndgameSettings().max_num_refinements);
608
609 if((samples.front() - samples.back()).template lpNorm<Eigen::Infinity>() < this->GetTracker().TrackingTolerance())
610 {
611 return true;
612 }
613 return false;
614
615 }//end CheckClosedLoop
616
617
618
619
638 template<typename ComplexT>
639 bool RatioEGOperatingZoneTest(ComplexT const& target_time) const
640 {
641 using RealT = typename Eigen::NumTraits<ComplexT>::Real;
642 RealT min(1e300);
643 RealT max(0);
644 auto& times = std::get<TimeCont<ComplexT> >(cauchy_times_);
645 auto& samples = std::get<SampCont<ComplexT> >(cauchy_samples_);
646 if(norm(times.front() - target_time) < GetCauchySettings().ratio_cutoff_time)
647 {
648 return true;
649 }
650 else
651 {
652 RealT norm;
653 for(unsigned int ii=0; ii < this->EndgameSettings().num_sample_points; ++ii)
654 {
655 norm = samples[ii].template lpNorm<Eigen::Infinity>();
656 if(norm > max)
657 {
658 max = norm;
659 }
660 if(norm < min)
661 {
662 min = norm;
663 }
664 }
665
666 if(min > this->FinalTolerance() && max > this->FinalTolerance())
667 {
668 norm = min / max;
669 if(norm < GetCauchySettings().maximum_cauchy_ratio && (max - min) > this->FinalTolerance())
670 {
671 return false; // bad ratio and too far apart.
672 }
673 }
674 }
675 return true;
676
677 }//end RatioEGOperatingZoneTest
678
679
680 /*
681 Input: All input needed is available as class data members.
682 Output: SuccessCode declaring if we are successful or not.
683 Details: Computes cauchy samples using Circle Track. Compares the ratios of the maximum and minimum norms of cauchy samples
684 using RatioEGOperatingZoneTest. Then attempts to see if we have closed our loop, otherwise it continues the process outlined.
685 */
686
687
688
704 template<typename ComplexT>
705 SuccessCode InitialCauchyLoops(ComplexT const& target_time)
706 {
707 using std::max;
708 // auto fail_safe_max_cycle_number = max(GetCauchySettings().fail_safe_maximum_cycle_number,this->CycleNumber());
709 auto fail_safe_max_cycle_number = GetCauchySettings().fail_safe_maximum_cycle_number;
710
711 auto initial_cauchy_loop_success = SuccessCode::Success;
712
713 bool loop_hasnt_closed = true;
714 while (loop_hasnt_closed)
715 {
716 this->CycleNumber(0);
718
719 // track around a circle once. we'll use it to measure whether we believe we are in the eg operating zone, based on the ratio of norms of sample points around the circle
720 auto tracking_success = CircleTrack(target_time);
721 this->IncrementCycleNumber(1);
722 if (tracking_success != SuccessCode::Success)
723 return tracking_success;
724
725 // find the ratio of the maximum and minimum coordinate wise for the loop.
726 if (RatioEGOperatingZoneTest<ComplexT>(target_time))
727 { // then we believe we are in the EG operating zone, since the path is relatively flat. i still disbelieve this is a good test (dab 20160310)
728 while (true)
729 {
731 {//error is small enough, exit the loop with success.
732 NotifyObservers(ClosedLoop<EmitterType>(*this));
733 initial_cauchy_loop_success = SuccessCode::Success;
734 loop_hasnt_closed = false;
735 break;
736 }
737 else if(this->CycleNumber() > fail_safe_max_cycle_number)
738 {// too many iterations
739 initial_cauchy_loop_success = SuccessCode::CycleNumTooHigh;
740 loop_hasnt_closed = false;
741 break;
742 }
743
744 //compute next loop, the last sample in times and samples is the sample our loop ended on. Either where we started or on another sheet at the same time value.
745 tracking_success = CircleTrack(target_time);
746 this->IncrementCycleNumber(1);
747 if(tracking_success != SuccessCode::Success)
748 return tracking_success;
749 }
750 }//end if (RatioEGOperatingZoneTest())
751 else
752 {
753 auto advance_success = AdvanceTime<ComplexT>(target_time);
754 if (advance_success!=SuccessCode::Success)
755 return advance_success;
756 }
757 } //end while(loop_hasnt_closed)
758
759 return initial_cauchy_loop_success;
760 }//end InitialCauchyLoops
761
762
763
765 template <typename ComplexT>
766 void RotateOntoPS(ComplexT const& next_time, Vec<ComplexT> const& next_sample)
767 {
768 auto& ps_times = std::get<TimeCont<ComplexT> >(pseg_times_);
769 auto& ps_samples = std::get<SampCont<ComplexT> >(pseg_samples_);
770
771 ps_times.pop_front();
772 ps_samples.pop_front();
773
774 ps_times.push_back(next_time);
775 ps_samples.push_back(next_sample);
776 }
777
779 template <typename ComplexT>
781 {
782 auto& cau_times = std::get<TimeCont<ComplexT> >(cauchy_times_);
783 auto& cau_samples = std::get<SampCont<ComplexT> >(cauchy_samples_);
784 auto& ps_times = std::get<TimeCont<ComplexT> >(pseg_times_);
785 auto& ps_samples = std::get<SampCont<ComplexT> >(pseg_samples_);
786
787 cau_times.clear();
788 cau_samples.clear();
789 cau_samples.push_back(ps_samples.back());
790 cau_times.push_back(ps_times.back());
791 }
792
793
817 template<typename ComplexT>
818 SuccessCode InitialApproximation(ComplexT const& start_time, Vec<ComplexT> const& start_point,
819 ComplexT const& target_time, Vec<ComplexT> & approximation)
820 {
821 auto init_success = GetIntoEGZone(start_time, start_point, target_time);
822 if (init_success!= SuccessCode::Success)
823 return init_success;
824
825 auto cauchy_loop_success = InitialCauchyLoops<ComplexT>(target_time);
826 if (cauchy_loop_success != SuccessCode::Success)
827 return cauchy_loop_success;
828
829 return ComputeCauchyApproximationOfXAtT0(approximation);
830
831 }//end InitialApproximation
832
833
834
836 template<typename ComplexT>
837 SuccessCode GetIntoEGZone(ComplexT const& start_time, Vec<ComplexT> const& start_point, ComplexT const& target_time)
838 {
839 using RealT = typename Eigen::NumTraits<ComplexT>::Real;
840
841 //initialize array holding c_over_k estimates
842 std::deque<RealT> c_over_k;
843
844 auto& ps_times = std::get<TimeCont<ComplexT> >(pseg_times_);
845 auto& ps_samples = std::get<SampCont<ComplexT> >(pseg_samples_);
846
847 //Compute initial samples for pseg
848 auto initial_sample_success = this->ComputeInitialSamples(start_time, target_time, start_point, ps_times, ps_samples);
849 if (initial_sample_success!=SuccessCode::Success)
850 return initial_sample_success;
851
852 c_over_k.push_back(this->template ComputeCOverK<ComplexT>(std::get<SampCont<ComplexT>>(pseg_samples_)));
853
854
855 //track until for more c_over_k estimates or until we reach a cutoff time.
856 for (unsigned ii = 0; ii < this->EndgameSettings().num_needed_for_stabilization; ++ii)
857 {
858 auto advance_success = AdvanceTime<ComplexT>(target_time);
859 if (advance_success!=SuccessCode::Success)
860 return advance_success;
861 c_over_k.push_back(this->template ComputeCOverK<ComplexT>(std::get<SampCont<ComplexT>>(pseg_samples_)));
862 }//end while
863
864
865 //have we stabilized yet?
866 while(!this->CheckForCOverKStabilization(c_over_k) && abs(ps_times.back()-target_time) > GetCauchySettings().cycle_cutoff_time)
867 {
868 auto advance_success = AdvanceTime<ComplexT>(target_time);
869 if (advance_success!=SuccessCode::Success)
870 return advance_success;
871
872 c_over_k.pop_front();
873 c_over_k.push_back(this->template ComputeCOverK<ComplexT>(std::get<SampCont<ComplexT>>(pseg_samples_)));
874
875 }//end while
876
877 NotifyObservers(InEGOperatingZone<EmitterType>(*this));
878
880 }
881
897 template<typename ComplexT>
899 {
900 auto& cau_times = std::get<TimeCont<ComplexT> >(cauchy_times_);
901 auto& cau_samples = std::get<SampCont<ComplexT> >(cauchy_samples_);
902
903 if (cau_samples.size() != this->CycleNumber() * this->EndgameSettings().num_sample_points+1)
904 {
905 std::stringstream err_msg;
906 err_msg << "to compute cauchy approximation, cau_samples must be of size " << this->CycleNumber() * this->EndgameSettings().num_sample_points+1 << " but is of size " << cau_samples.size() << '\n';
907 throw std::runtime_error(err_msg.str());
908 }
909
910
911 //Ensure all samples are of the same precision.
913 {
914 auto new_precision = this->EnsureAtUniformPrecision(cau_times, cau_samples);
915 }
916
917
918 auto total_num_pts = this->CycleNumber() * this->EndgameSettings().num_sample_points;
919 auto refine_code = this->template RefineAllSamples<ComplexT>(cau_samples, cau_times);
920 // Pure-(i): when the adaptive-numeric-type driver is orchestrating, a refine that double cannot
921 // satisfy is a request to cross to mpfr -- propagate it so RunImplAMP migrates and retries.
922 // Fixed precision (and the AMP-PowerSeries path) keep ignoring the code, exactly as before.
924 if (this->adaptive_numeric_type_active_ &&
926 return refine_code;
927
928 Precision(result, Precision(cau_samples.back()));
929
930 result = Vec<ComplexT>::Zero(static_cast<Eigen::Index>(this->GetSystem().NumVariables()));
931 for(unsigned int ii = 0; ii < total_num_pts; ++ii)
932 result += cau_samples[ii];
933 result /= this->CycleNumber() * this->EndgameSettings().num_sample_points;
934
935 // A NaN mean must be a FAILURE code, never Success: NaN compares false against
936 // everything, so downstream convergence and security comparisons are blind to it.
937 if (bertini::ContainsNaN(result))
939
941
942 }
943
957 template<typename ComplexT>
959 {
960 const auto& cau_samples = std::get<SampCont<ComplexT> >(cauchy_samples_);
961 using std::min; using std::isnan;
962 NumErrorT floor_of_loop = std::numeric_limits<NumErrorT>::max();
963 for (const auto& s : cau_samples)
964 {
965 auto n = static_cast<NumErrorT>(this->GetSystem().InfinityNormOfDehomogenized(s));
966 if (!isnan(n))
967 floor_of_loop = min(floor_of_loop, n);
968 }
969 return floor_of_loop;
970 }
971
990 template<typename ComplexT>
991 SuccessCode ComputeCauchySamples(ComplexT const& target_time)
992 {
993 using bertini::Precision;
994
996 this->CycleNumber(0);
997
998
999 while( this->CycleNumber() < GetCauchySettings().fail_safe_maximum_cycle_number )
1000 {
1001 //track around the origin once.
1002 auto tracking_success = CircleTrack(target_time);
1003 this->IncrementCycleNumber(1);
1004
1005 if(tracking_success != SuccessCode::Success)
1006 {
1007 return tracking_success;
1008 }
1009 else if(CheckClosedLoop<ComplexT>())
1010 {
1011 return SuccessCode::Success;
1012 }
1013 }
1016 }//end ComputeCauchySamples
1017
1018
1026 template<typename ComplexT>
1027 SuccessCode AdvanceTime(ComplexT const& target_time)
1028 {
1029 using RealT = typename Eigen::NumTraits<ComplexT>::Real;
1030
1031 auto& ps_times = std::get<TimeCont<ComplexT> >(pseg_times_);
1032 auto& ps_samples = std::get<SampCont<ComplexT> >(pseg_samples_);
1033
1034 auto& current_time = ps_times.back();
1035 auto& current_sample = ps_samples.back();
1036
1037 //Generalized next_time in case if we are not trying to converge to the t = 0.
1038 ComplexT next_time = (target_time-current_time) * static_cast<RealT>(this->EndgameSettings().sample_factor)+current_time;
1039
1040 if (abs(next_time - target_time) < this->EndgameSettings().min_track_time)//we are too close to t = 0 but we do not have the correct tolerance - so we exit
1042
1043 // advance in time
1044 Vec<ComplexT> next_sample;
1045 auto time_advance_success = this->EndgameTrackPath(next_sample,current_time, next_time, current_sample);
1046 if (time_advance_success != SuccessCode::Success)
1047 {
1048 NotifyObservers(EndgameFailure<EmitterType>(*this));
1049 return time_advance_success;
1050 }
1051
1052 // Pure-(i) escalation: return BEFORE RotateOntoPS so the PSEG window stays an untouched
1053 // checkpoint -- RunImplAMP migrates it to mpfr and retries this advance. Elided for fixed prec.
1055 if (this->adaptive_numeric_type_active_ &&
1056 this->GetTracker().GetCurrentPrecision() > this->current_endgame_precision_)
1058
1059 this->EnsureAtPrecision(next_time,Precision(next_sample));
1060 RotateOntoPS(next_time, next_sample);
1061
1062 NotifyObservers(TimeAdvanced<EmitterType>(*this));
1063 // the sample is complete here: Cauchy does not refine the new power-series
1064 // sample at this site (RefineAllSamples does the window later), so the
1065 // event carries the sample as tracked
1066 // ComputedSamplePoint carries the sample/time at BaseComplexT, like CircleAdvanced.
1067 // The fixed/mpfr lane emits directly; the complex_dbl fast lane would have to convert
1068 // the point to mpfr for the event, so we only pay that when something is observing
1069 // (temporaries live through the synchronous NotifyObservers).
1070 if constexpr (std::is_same<ComplexT, BCT>::value)
1071 NotifyObservers(ComputedSamplePoint<EmitterType>(*this, next_sample, next_time));
1072 else if (this->HasObservers())
1073 {
1074 Vec<BCT> ev_pt(next_sample.size());
1075 for (Eigen::Index i = 0; i < next_sample.size(); ++i) ev_pt(i) = BCT(next_sample(i));
1076 BCT ev_t(next_time);
1078 }
1079 return SuccessCode::Success;
1080 }
1081
1112 template<typename ComplexT>
1114 {
1115 using RealT = typename Eigen::NumTraits<ComplexT>::Real;
1116 auto const& samples = std::get<SampCont<ComplexT>>(cauchy_samples_);
1117 auto const& times = std::get<TimeCont<ComplexT>>(cauchy_times_);
1118 auto const c = this->CycleNumber();
1119 auto const N = this->EndgameSettings().num_sample_points;
1120 auto const M = c * N;
1121 if (c == 0 || samples.size() < M + 1 || times.size() < 2)
1122 return NumErrorT(0);
1123
1124 // orientation of the loop (which way theta advances); the frequency sign follows it
1125 RealT const orientation_test = imag(times[1] * conj(times[0]));
1126 RealT const orientation = (orientation_test > RealT(0)) ? RealT(1) : RealT(-1);
1127
1128 RealT const two_pi = RealT(2) * acos(RealT(-1));
1129 NumErrorT mass(0);
1130 for (unsigned m = 1; m <= c; ++m)
1131 {
1132 Vec<ComplexT> twisted = Vec<ComplexT>::Zero(samples[0].size());
1133 for (unsigned j = 0; j < M; ++j)
1134 {
1135 // weight e^{+i m theta_j / c} with theta_j = orientation * 2 pi j / N:
1136 // picks out exactly the k = -m Puiseux mode
1137 RealT const angle = orientation * two_pi * RealT(j * m) / RealT(N * c);
1138 twisted += samples[j] * ComplexT(cos(angle), sin(angle));
1139 }
1140 twisted /= RealT(M);
1141 auto const twisted_norm = static_cast<NumErrorT>(twisted.template lpNorm<Eigen::Infinity>());
1142 if (twisted_norm > mass)
1143 mass = twisted_norm;
1144 }
1145 return mass;
1146 }
1147
1150 template<typename Dummy = void>
1152 {
1153 if (this->current_endgame_precision_ == DoublePrecision())
1156 }
1157
1166 bool PoleMassSignificant(NumErrorT mass, NumErrorT approx_scale) const
1167 {
1168 return mass > NumErrorT(1e3) * this->FinalTolerance() * (NumErrorT(1) + approx_scale);
1169 }
1170
1192 template<typename ComplexT>
1193 SuccessCode RunImpl(ComplexT const& start_time, Vec<ComplexT> const& start_point, ComplexT const& target_time)
1194 {
1195 if (start_point.size()!=static_cast<Eigen::Index>(this->GetSystem().NumVariables()))
1196 {
1197 std::stringstream err_msg;
1198 err_msg << "number of variables in start point for CauchyEG, " << start_point.size() << ", must match the number of variables in the system, " << this->GetSystem().NumVariables();
1199 throw std::runtime_error(err_msg.str());
1200 }
1201
1202 if (Precision(start_time)!=Precision(start_point))
1203 {
1204 std::stringstream ss;
1205 ss << "CauchyEG Run time and point must be of matching precision. (" << Precision(start_time) << "!=" << Precision(start_point) << ")";
1206 throw std::runtime_error(ss.str());
1207 }
1208
1209 using RealT = typename Eigen::NumTraits<ComplexT>::Real;
1210
1211 Vec<ComplexT>& latest_approx = this->final_approximation_;
1212 Vec<ComplexT>& prev_approx = this->previous_approximation_;
1213 NumErrorT& approx_error = this->approximate_error_;
1214
1215
1216 ClearTimesAndSamples<ComplexT>(); //clear times and samples before we begin.
1217 this->CycleNumber(0);
1218 prev_approx = start_point;
1219
1220 auto init_success = GetIntoEGZone(start_time, start_point, target_time);
1221 if (init_success!= SuccessCode::Success)
1222 return init_success;
1223
1224 auto cauchy_loop_success = InitialCauchyLoops<ComplexT>(target_time);
1225 if (cauchy_loop_success != SuccessCode::Success)
1226 return cauchy_loop_success;
1227
1228
1229 // The security check watches TWO honest divergence signals, taking the stronger:
1230 // (1) the extrapolated ENDPOINT -- a genuinely-infinite endpoint (a patched
1231 // projective path leaving the affine chart) has a dehomogenized norm growing to
1232 // infinity FASTER than any finite loop sample, the earliest signal there; and
1233 // (2) the loop FLOOR (min over loop samples) -- a fractional-pole diverger's
1234 // branches cancel around the closed loop, so its mean/endpoint stays ~0 while
1235 // every sample sits at ~r^(-p/q); only the floor sees it. The floor is safe
1236 // where watching samples DIRECTLY was not (any single sample transiently spiking
1237 // above max_norm truncated finite cyclic-6 paths, 156 -> 153): a transient spike
1238 // lifts the max, never the min. The pole-annihilation case -- a Laurent pole
1239 // whose Cauchy mean is a STATIONARY FINITE number -- is handled separately by
1240 // the pole-component operating-zone check below. Tracked across consecutive
1241 // ARMED rounds only (see below); initialized to 0 so the check never reads
1242 // indeterminate values.
1243 RealT norm_of_dehom_prev(0), norm_of_dehom_latest(0);
1244
1245 // Cycle-number consistency: refuse to accept a converged approximation until the cycle number
1246 // has reported the SAME value for num_consecutive_same_cycle_number consecutive approximations.
1247 // When the working precision is too low to close the loop accurately (NOT monodromy -- a
1248 // nonsingular endpoint is cycle 1 at every radius in high precision), the cycle number thrashes
1249 // (41, 14, 36, ...) and a coincidental approx_error dip must not be mistaken for convergence.
1250 // prev_cycle == 0 means "no prior measurement".
1251 unsigned prev_cycle = 0, same_cycle_count = 0;
1252
1253 do
1254 {
1255 //Compute a cauchy approximation. Uses the previously computed samples,
1256 //either from InitialCauchyLoops, or ComputeCauchySamples
1257 auto extrapolation_success = ComputeCauchyApproximationOfXAtT0<ComplexT>(latest_approx);
1258 if (extrapolation_success!=SuccessCode::Success)
1259 return extrapolation_success;
1260
1261 unsigned cur_cycle = this->CycleNumber();
1262 if (cur_cycle == prev_cycle)
1263 ++same_cycle_count;
1264 else
1265 same_cycle_count = 1;
1266 prev_cycle = cur_cycle;
1267
1268 approx_error = static_cast<NumErrorT>((latest_approx - prev_approx).template lpNorm<Eigen::Infinity>());
1269 NotifyObservers(ApproximatedRoot<EmitterType>(*this));
1270
1271 // the operating-zone ACCEPTANCE GATE: negative-mode (pole) mass in the loop.
1272 // Nonzero mass means the disk between here and the target time is not clean
1273 // -- a pole at the target, or another branch point inside the loop -- and
1274 // the mean is not to be trusted, however stationary it looks. So convergence
1275 // is refused until the zone is reached (pole mass insignificant). This alone
1276 // cures the junk-success bug: an affine Laurent pole has a stationary FINITE
1277 // Cauchy mean (roots-of-unity annihilation), so no norm check can catch it,
1278 // but its pole mass never vanishes, so this gate never accepts it -- the path
1279 // instead runs to the endgame's natural terminal condition (min track time /
1280 // fail-safe max cycle) and returns non-Success. There is deliberately NO
1281 // active pole-growth truncation: distinguishing a genuine pole (mass grows
1282 // unboundedly) from a branch point merely inside the loop (mass grows a round
1283 // or two, then dies as the radius shrinks past it) by counting growing rounds
1284 // produced false positives that truncated CLEAN convergent paths -- cyclic-6
1285 // lost genuine nonsingular solutions (156 -> 153). The gate suffices.
1286 auto const pole_mass = PoleComponentMass<ComplexT>();
1287 bool const in_operating_zone = !PoleMassSignificant(pole_mass,
1288 static_cast<NumErrorT>(latest_approx.template lpNorm<Eigen::Infinity>()));
1289 // B1's CycleTimeCutoff semantics (manual E.5.9): below cycle_cutoff_time the
1290 // zone heuristics stop being consulted and the endgame behaves as if in the
1291 // operating zone. Applied here to the SECURITY VALVE ONLY: a slowly-diverging
1292 // path (fractional-order blowup) carries significant pole mass at every radius,
1293 // so it is never pole-mass-in-zone and the valve never arms -- it crawls to the
1294 // tracker's far-larger truncation threshold (measured: a t^(-1/3) diverger took
1295 // ~117k extra steps over 3 more decades of |t| at up to 70 digits). Below the
1296 // cutoff a genuinely convergent path's mean has long stabilized at a finite
1297 // value, so arming the valve cannot reintroduce the cyclic-6 false truncations.
1298 // Deliberately NOT applied to acceptance: forcing the zone there would re-open
1299 // the junk-success hole (a Laurent pole's stationary FINITE mean would be
1300 // accepted once below the cutoff); pole junk still runs to its honest
1301 // non-Success terminal.
1302 using std::abs;
1303 bool const below_cycle_cutoff =
1304 static_cast<NumErrorT>(abs(this->LatestTime() - target_time))
1305 < GetCauchySettings().cycle_cutoff_time;
1306
1307 // SECURITY BEFORE ACCEPTANCE, the order the power series endgame uses. At
1308 // security level 0, two consecutive endpoint approximations whose dehomogenized
1309 // infinity norm is above max_norm truncate the path, whether or not the second
1310 // would also have been accepted. Asked the other way round, a path that
1311 // converged to a finite point above the ceiling came back Success here and
1312 // SecurityMaxNormReached from the power series endgame -- the two endgames must
1313 // differ in how they estimate the root and in nothing else.
1314 //
1315 // Security truncation happens ONLY in the operating zone. Outside it the
1316 // loop may encircle a pole or branch point and the Cauchy mean is garbage --
1317 // its norm means nothing, and truncating on it killed clean convergent paths
1318 // (the cyclic-6 residual, 156 -> 155). Inside the zone the mean is
1319 // trustworthy: a genuinely-infinite endpoint is in-zone AND large (clean
1320 // homogeneous convergence, the endpoint growing to infinity faster than any
1321 // sample), so two consecutive IN-ZONE rounds above max_norm truncate honestly.
1322 // An out-of-zone round restarts the two-consecutive count.
1323 if (this->SecuritySettings().level <= 0)
1324 {
1325 if (in_operating_zone || below_cycle_cutoff)
1326 {
1327 // Watch the STRONGER of two honest divergence signals: the endpoint (the
1328 // earliest signal for a clean infinite endpoint) and the loop FLOOR (min
1329 // over loop samples) -- the mean is structurally blind to fractional-pole
1330 // divergers, whose branches cancel around the closed loop while every
1331 // sample sits at ~r^(-p/q). See MinDehomNormOverLoopSamples.
1332 using std::max;
1333 norm_of_dehom_latest = max(
1334 this->GetSystem().InfinityNormOfDehomogenized(latest_approx),
1335 static_cast<RealT>(MinDehomNormOverLoopSamples<ComplexT>()));
1336 if (this->BeyondSecurityMaxNorm(norm_of_dehom_prev) &&
1337 this->BeyondSecurityMaxNorm(norm_of_dehom_latest))
1338 {
1341 }
1342 norm_of_dehom_prev = norm_of_dehom_latest;
1343 }
1344 else
1345 norm_of_dehom_prev = RealT(0); // out of zone: restart the count
1346 }
1347
1348 // An approximation above the ceiling is never ACCEPTED at security level 0: it is
1349 // the first of the two that truncate, or the second. Without this the count above
1350 // (armed rounds only) could still be at one when acceptance, which needs a single
1351 // in-zone round, let the path through as a Success above max_norm.
1352 bool const above_ceiling = this->SecuritySettings().level <= 0
1353 && this->BeyondSecurityMaxNorm(
1354 this->GetSystem().InfinityNormOfDehomogenized(latest_approx));
1355
1356 if (in_operating_zone
1357 && !above_ceiling
1358 && approx_error < this->FinalTolerance()
1359 && same_cycle_count >= GetCauchySettings().num_consecutive_same_cycle_number)
1360 {
1361 NotifyObservers(Converged<EmitterType>(*this));
1362 return SuccessCode::Success;
1363 }
1364
1365 prev_approx = latest_approx;
1366
1367 auto advance_success = AdvanceTime<ComplexT>(target_time);
1368 if (advance_success != SuccessCode::Success)
1369 return advance_success;
1370
1371 // then compute the next set of cauchy samples used for extrapolating the point at target time
1372 auto cauchy_samples_success = ComputeCauchySamples(target_time);
1373 if (cauchy_samples_success != SuccessCode::Success)
1374 return cauchy_samples_success;
1375
1376 } while (true);
1377
1378 return SuccessCode::Success;
1379 } //end main CauchyEG function
1380
1381
1382 // ================================================================================================
1383 // Adaptive-numeric-type (double-first) Cauchy endgame.
1384 //
1385 // Computes in the hardware-complex_dbl fast lane while the AMP tracker's authoritative precision
1386 // stays double, and crosses to complex_mp only when the tracker escalates a TrackPath/Refine
1387 // (pure-(i)). Fixed precision never enters here -- base Run() sends it to RunImpl<BCT>. Every
1388 // method below is a member template so the explicit fixed-precision class instantiations do not
1389 // force-compile them (which would std::get a complex_mp/complex_dbl slot a single-type endgame
1390 // does not have). The complex_dbl->complex_mp container migration is the one piece with no
1391 // pre-existing analog; mpfr->higher-mpfr co-vary already happens via EnsureAtUniformPrecision.
1392 // ================================================================================================
1393
1395 template<typename Dummy = void>
1396 SuccessCode RunImplAMP(BCT const& start_time, Vec<BCT> const& start_point, BCT const& target_time)
1397 {
1398 using bertini::Precision;
1399 using RealT = typename Eigen::NumTraits<BCT>::Real;
1400
1401 if (start_point.size()!=static_cast<Eigen::Index>(this->GetSystem().NumVariables()))
1402 {
1403 std::stringstream err_msg;
1404 err_msg << "number of variables in start point for CauchyEG, " << start_point.size() << ", must match the number of variables in the system, " << this->GetSystem().NumVariables();
1405 throw std::runtime_error(err_msg.str());
1406 }
1407
1408 // Arm the escalation hooks in the shared phase methods (CircleTrack / AdvanceTime / refine);
1409 // disarm on every exit path.
1410 this->adaptive_numeric_type_active_ = true;
1411 struct Disarmer { bool& flag; ~Disarmer(){ flag = false; } } disarm{this->adaptive_numeric_type_active_};
1412
1413 // Both lanes start empty: a phase clears only the lane it runs in, so a path whose endgame
1414 // stays in double would otherwise leave an earlier path's multiprecision samples and times
1415 // in place for anything that reads that lane first (#378).
1418
1419 // Start in the precision the tracker handed us at the endgame boundary: double for the easy
1420 // majority, already-mpfr for the few paths that escalated before the endgame. Migrate UP only.
1421 this->current_endgame_precision_ = std::max(DoublePrecision(), Precision(start_point));
1422 this->CycleNumber(0);
1423
1424 // ---- INITIALIZATION (GetIntoEGZone + InitialCauchyLoops). On escalation, restart init from the
1425 // boundary at the higher precision: init lives at the well-conditioned large-|t| end where
1426 // escalation is rare, and a fresh init clears its own containers so it is self-consistent. ----
1427 while (true)
1428 {
1429 SuccessCode init_code;
1430 if (this->current_endgame_precision_ == DoublePrecision())
1431 init_code = RunInitSegmentT<complex_dbl>(complex_dbl(start_time), this->DowncastToDouble(start_point), complex_dbl(target_time));
1432 else
1433 init_code = RunInitSegmentT<complex_mp>(this->AtActivePrecisionScalar(start_time), this->AtActivePrecisionVec(start_point), this->AtActivePrecisionScalar(target_time));
1434
1436 {
1437 auto const previous_precision = this->current_endgame_precision_;
1438 this->current_endgame_precision_ = this->NextEscalatedPrecision();
1439 SetThreadPrecision(this->current_endgame_precision_);
1440 // the samples announced at the lower precision are superseded (the window is
1441 // tracked again at the new one); say so, so a collector drops them as we do
1442 NotifyObservers(PrecisionChanged<EmitterType>(*this, previous_precision, this->current_endgame_precision_));
1443 NotifyObservers(SamplesRecomputedAtHigherPrecision<EmitterType>(*this));
1444 continue;
1445 }
1446 if (init_code != SuccessCode::Success)
1447 return init_code;
1448 break;
1449 }
1450
1451 // ---- MAIN CONVERGENCE LOOP. Each phase self-heals: on escalation it migrates the durable state
1452 // up to mpfr (widening the retained PSEG window, never re-tracking it) and retries in mpfr.
1453 // The approximations live in BCT, so the per-iteration bookkeeping arithmetic is mpfr -- one
1454 // small vector op next to the tracking, which itself stays in the fast lane. ----
1455 // the security check watches the extrapolated ENDPOINT for honest divergence to
1456 // infinity (which grows faster than any finite loop sample), but ONLY in the
1457 // operating zone (a trustworthy mean) -- see RunImpl for the full reasoning.
1458 // Acceptance is gated on the same operating-zone measurement (the finite-mean
1459 // pole case). Tracked across consecutive in-zone rounds; starts at 0.
1460 RealT norm_of_dehom_prev(0), norm_of_dehom_latest(0);
1461
1462 unsigned prev_cycle = 0, same_cycle_count = 0;
1463
1464 while (true)
1465 {
1466 auto extrap_code = ComputeCauchyApproxAMP();
1467 if (extrap_code != SuccessCode::Success)
1468 return extrap_code;
1469
1470 unsigned cur_cycle = this->CycleNumber();
1471 if (cur_cycle == prev_cycle) ++same_cycle_count; else same_cycle_count = 1;
1472 prev_cycle = cur_cycle;
1473
1475 this->approximate_error_ = static_cast<NumErrorT>((this->final_approximation_ - this->previous_approximation_).template lpNorm<Eigen::Infinity>());
1476 NotifyObservers(ApproximatedRoot<EmitterType>(*this));
1477
1478 // the operating-zone ACCEPTANCE GATE (see RunImpl for the full reasoning):
1479 // convergence is refused while pole mass is significant, which alone cures
1480 // junk-success; there is deliberately no active pole-growth truncation (it
1481 // false-positived on clean convergent paths -- cyclic-6, 156 -> 153).
1482 auto const pole_mass = PoleComponentMassAMP();
1483 bool const in_operating_zone = !PoleMassSignificant(pole_mass,
1484 static_cast<NumErrorT>(this->final_approximation_.template lpNorm<Eigen::Infinity>()));
1485 // B1's CycleTimeCutoff semantics (manual E.5.9): below cycle_cutoff_time the
1486 // zone heuristics stop being consulted and the endgame behaves as if in the
1487 // operating zone. Applied here to the SECURITY VALVE ONLY: a slowly-diverging
1488 // path (fractional-order blowup) carries significant pole mass at every radius,
1489 // so it is never pole-mass-in-zone and the valve never arms -- it crawls to the
1490 // tracker's far-larger truncation threshold (measured: a t^(-1/3) diverger took
1491 // ~117k extra steps over 3 more decades of |t| at up to 70 digits). Below the
1492 // cutoff a genuinely convergent path's mean has long stabilized at a finite
1493 // value, so arming the valve cannot reintroduce the cyclic-6 false truncations.
1494 // Deliberately NOT applied to acceptance: forcing the zone there would re-open
1495 // the junk-success hole (a Laurent pole's stationary FINITE mean would be
1496 // accepted once below the cutoff); pole junk still runs to its honest
1497 // non-Success terminal.
1498 using std::abs;
1499 bool const below_cycle_cutoff =
1500 static_cast<NumErrorT>(abs(this->LatestTime() - this->AtActivePrecisionScalar(target_time)))
1501 < GetCauchySettings().cycle_cutoff_time;
1502
1503 // security BEFORE acceptance, as in the power series endgame; truncation ONLY in
1504 // the operating zone (a trustworthy mean); an out-of-zone round restarts the
1505 // two-consecutive count. See RunImpl.
1506 if (this->SecuritySettings().level <= 0)
1507 {
1508 if (in_operating_zone || below_cycle_cutoff)
1509 {
1510 // The stronger of endpoint norm and loop floor; the loop samples live in
1511 // whichever lane is active. See RunImpl and MinDehomNormOverLoopSamples.
1512 NumErrorT loop_floor = (this->current_endgame_precision_ == DoublePrecision())
1515 using std::max;
1516 norm_of_dehom_latest = max(
1517 this->GetSystem().InfinityNormOfDehomogenized(this->final_approximation_),
1518 static_cast<RealT>(loop_floor));
1519 if (this->BeyondSecurityMaxNorm(norm_of_dehom_prev) &&
1520 this->BeyondSecurityMaxNorm(norm_of_dehom_latest))
1521 {
1524 }
1525 norm_of_dehom_prev = norm_of_dehom_latest;
1526 }
1527 else
1528 norm_of_dehom_prev = RealT(0); // out of zone: restart the count
1529 }
1530
1531 // never accept an approximation above the ceiling at security level 0 (see RunImpl)
1532 bool const above_ceiling = this->SecuritySettings().level <= 0
1533 && this->BeyondSecurityMaxNorm(
1534 this->GetSystem().InfinityNormOfDehomogenized(this->final_approximation_));
1535
1536 if (in_operating_zone
1537 && !above_ceiling
1539 && same_cycle_count >= GetCauchySettings().num_consecutive_same_cycle_number)
1540 {
1541 NotifyObservers(Converged<EmitterType>(*this));
1542 return SuccessCode::Success;
1543 }
1544
1546
1547 auto advance_code = AdvanceTimeAMP(target_time);
1548 if (advance_code != SuccessCode::Success)
1549 return advance_code;
1550
1551 auto samples_code = ComputeCauchySamplesAMP(target_time);
1552 if (samples_code != SuccessCode::Success)
1553 return samples_code;
1554 }
1555
1556 return SuccessCode::Success;
1557 }
1558
1559
1560 // Initialization at one numeric type: seed the PSEG window and reach the EG operating zone, exactly
1561 // as the head of RunImpl does, but reporting HigherPrecisionNecessary up to the driver on escalation.
1563 template<typename ComplexT>
1564 SuccessCode RunInitSegmentT(ComplexT const& start_time, Vec<ComplexT> const& start_point, ComplexT const& target_time)
1565 {
1567 this->CycleNumber(0);
1568 this->previous_approximation_ = this->ToBCT(start_point, this->current_endgame_precision_);
1569
1570 auto init_success = GetIntoEGZone(start_time, start_point, target_time);
1571 if (init_success != SuccessCode::Success)
1572 return init_success;
1573
1574 return InitialCauchyLoops<ComplexT>(target_time);
1575 }
1576
1577
1578 // Cauchy mean (extrapolation) at the active numeric type, written into final_approximation_ (BCT).
1580 template<typename Dummy = void>
1582 {
1583 unsigned guard = 0;
1584 while (true)
1585 {
1586 SuccessCode code;
1587 if (this->current_endgame_precision_ == DoublePrecision())
1588 {
1591 if (code == SuccessCode::Success)
1592 this->final_approximation_ = this->ToBCT(r, this->current_endgame_precision_);
1593 }
1594 else
1595 {
1597 }
1598
1600 {
1601 if (this->template EscalateAndMigrate<>(++guard) != SuccessCode::Success)
1603 continue;
1604 }
1605 return code;
1606 }
1607 }
1608
1609
1610 // Advance time at the active numeric type; migrate-and-retry in mpfr on escalation. AdvanceTime
1611 // returns HigherPrecisionNecessary BEFORE it rotates the PSEG window, so the window stays a clean
1612 // checkpoint and the retry continues from the widened window with no double-advance.
1614 template<typename Dummy = void>
1615 SuccessCode AdvanceTimeAMP(BCT const& target_time)
1616 {
1617 unsigned guard = 0;
1618 while (true)
1619 {
1620 SuccessCode code = (this->current_endgame_precision_ == DoublePrecision())
1622 : AdvanceTime<complex_mp>(this->AtActivePrecisionScalar(target_time));
1623
1625 {
1626 if (this->template EscalateAndMigrate<>(++guard) != SuccessCode::Success)
1628 continue;
1629 }
1630 return code;
1631 }
1632 }
1633
1634
1635 // Build a closed Cauchy loop's samples at the active numeric type; migrate-and-retry in mpfr on
1636 // escalation. ComputeCauchySamples clears and re-seeds its cauchy data from the PSEG window, so the
1637 // mpfr retry simply re-tracks the circle from the (migrated) window -- it never reuses the lossy,
1638 // double-tracked partial circle that triggered the escalation.
1640 template<typename Dummy = void>
1642 {
1643 unsigned guard = 0;
1644 while (true)
1645 {
1646 SuccessCode code = (this->current_endgame_precision_ == DoublePrecision())
1648 : ComputeCauchySamples<complex_mp>(this->AtActivePrecisionScalar(target_time));
1649
1651 {
1652 if (this->template EscalateAndMigrate<>(++guard) != SuccessCode::Success)
1654 continue;
1655 }
1656 return code;
1657 }
1658 }
1659
1660
1661 // Flavor-specific: cross every Cauchy container from the complex_dbl slot to complex_mp (or, if
1662 // already mpfr, raise its precision uniformly), via the shared base Cross* / SetPrecision helpers.
1663 // Widen-only by default -- retained samples were already tracked/refined to final_tolerance
1664 // (pure-(i)/B). Called by the base EscalateAndMigrate.
1666 template<typename Dummy = void>
1667 void MigrateContainersToPrecision(unsigned newprec)
1668 {
1669 using bertini::Precision;
1670 if (this->current_endgame_precision_ == DoublePrecision())
1671 {
1672 this->template CrossTimesUp<>(pseg_times_, newprec);
1673 this->template CrossSampsUp<>(pseg_samples_, newprec);
1674 this->template CrossTimesUp<>(cauchy_times_, newprec);
1675 this->template CrossSampsUp<>(cauchy_samples_, newprec);
1676 }
1677 else
1678 {
1683 }
1684
1685 if (this->final_approximation_.size() > 0) Precision(this->final_approximation_, newprec);
1686 if (this->previous_approximation_.size() > 0) Precision(this->previous_approximation_, newprec);
1687
1688 if (this->EndgameSettings().refine_when_increasing_precision)
1689 {
1690 auto& cau_t = std::get<TimeCont<complex_mp>>(cauchy_times_);
1691 auto& cau_s = std::get<SampCont<complex_mp>>(cauchy_samples_);
1692 if (!cau_s.empty())
1693 this->template RefineAllSamples<complex_mp>(cau_s, cau_t);
1694 }
1695 }
1696
1697};
1698
1699
1700}} // namespaces
Functions for dealing with precisions of objects, particularly in the context of adaptive precision.
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
TupleOfSamps pseg_samples_
Definition cauchy.hpp:201
void MigrateContainersToPrecision(unsigned newprec)
Widen this flavor's Cauchy containers from the complex_dbl slot up to complex_mp at the new precision...
Definition cauchy.hpp:1667
const SampCont< ComplexT > & GetPSEGSamples() const
Const overload returning the power-series sample values.
Definition cauchy.hpp:268
NumErrorT PoleComponentMass() const
The pole-component mass of the current Cauchy loop: the endgame operating-zone measurement.
Definition cauchy.hpp:1113
virtual ~CauchyEndgame()=default
CauchyEndgame(TrackerType const &tr, const Ts &... ts)
Construct the Cauchy endgame for a tracker, with configs given in any order.
Definition cauchy.hpp:363
BCT latest_time_cache_
Definition cauchy.hpp:216
typename BaseEGT::TupleOfTimes TupleOfTimes
A tuple of time containers, one per precision.
Definition cauchy.hpp:184
const TimeCont< ComplexT > & GetCauchyTimes() const
Const overload returning the Cauchy time values.
Definition cauchy.hpp:312
void RotateOntoPS(ComplexT const &next_time, Vec< ComplexT > const &next_sample)
Shift the power-series sample window forward by one (time, sample) pair.
Definition cauchy.hpp:766
void AddToPSData(ComplexT const &time, Vec< ComplexT > const &sample)
Append a (time, sample) pair to the power-series data containers.
Definition cauchy.hpp:512
const SampCont< ComplexT > & GetCauchySamples() const
Const overload returning the Cauchy sample values.
Definition cauchy.hpp:290
void ClearAndSeedCauchyData()
Clear the Cauchy data and re-seed it from the current power-series samples.
Definition cauchy.hpp:780
void SetPSEGSamples(SampCont< ComplexT > const &pseg_samples_to_set)
Setter for the space values for the power series approximation of the Cauchy endgame.
Definition cauchy.hpp:257
const TimeCont< ComplexT > & GetPSEGTimes() const
Const overload returning the power-series time values.
Definition cauchy.hpp:251
void AddToCauchyData(ComplexT const &time, Vec< ComplexT > const &sample)
Append a (time, sample) pair to the Cauchy data containers.
Definition cauchy.hpp:504
typename tracking::TrackerTraits< TrackerType >::BaseComplexT BaseComplexT
The complex number type.
Definition cauchy.hpp:172
SuccessCode CircleTrack(ComplexT const &target_time)
Function to track around the origin.
Definition cauchy.hpp:398
BaseComplexT BCT
The complex number type.
Definition cauchy.hpp:188
SuccessCode InitialApproximation(ComplexT const &start_time, Vec< ComplexT > const &start_point, ComplexT const &target_time, Vec< ComplexT > &approximation)
Definition cauchy.hpp:818
const BCT & LatestTimeImpl() const
Definition cauchy.hpp:319
bool PoleMassSignificant(NumErrorT mass, NumErrorT approx_scale) const
Is a pole-component mass SIGNIFICANT, i.e.
Definition cauchy.hpp:1166
bool CheckClosedLoop()
Function that determines if we have closed a loop after calling CircleTrack().
Definition cauchy.hpp:590
typename AlgoTraits< FinalEGT >::NeededConfigs Configs
The configuration bundle (Configured base).
Definition cauchy.hpp:191
SampCont< ComplexT > & GetCauchySamples()
Getter for the space values for the Cauchy endgame.
Definition cauchy.hpp:284
typename tracking::TrackerTraits< TrackerType >::BaseRealT BaseRealT
The real number type.
Definition cauchy.hpp:173
SuccessCode ComputeCauchySamples(ComplexT const &target_time)
Collects samples while tracking around the target time, until we close the loop, or exceed the limit ...
Definition cauchy.hpp:991
SuccessCode RunImplAMP(BCT const &start_time, Vec< BCT > const &start_point, BCT const &target_time)
Run the Cauchy endgame via the double-first adaptive-numeric-type driver, migrating up to mpfr only o...
Definition cauchy.hpp:1396
bool RatioEGOperatingZoneTest(ComplexT const &target_time) const
After we have used CircleTrack and have successfully closed the loop using CheckClosedLoop we need to...
Definition cauchy.hpp:639
TupleOfTimes pseg_times_
Definition cauchy.hpp:197
SuccessCode AdvanceTimeAMP(BCT const &target_time)
Advance time at the active numeric type, migrating-and-retrying in mpfr on escalation.
Definition cauchy.hpp:1615
TimeCont< ComplexT > & GetPSEGTimes()
Getter for the time values for the power series approximation of the Cauchy endgame.
Definition cauchy.hpp:248
SuccessCode RunImpl(ComplexT const &start_time, Vec< ComplexT > const &start_point, ComplexT const &target_time)
Primary function that runs the Cauchy endgame.
Definition cauchy.hpp:1193
typename BaseEGT::TupleOfSamps TupleOfSamps
A tuple of sample containers, one per precision.
Definition cauchy.hpp:185
NumErrorT MinDehomNormOverLoopSamples() const
The FLOOR of the current Cauchy loop: the smallest dehomogenized infinity-norm over the loop samples.
Definition cauchy.hpp:958
SuccessCode GetIntoEGZone(ComplexT const &start_time, Vec< ComplexT > const &start_point, ComplexT const &target_time)
Track from the boundary inward until the path enters the endgame (Cauchy) operating zone.
Definition cauchy.hpp:837
TimeCont< ComplexT > & GetCauchyTimes()
Getter for the time values for the Cauchy endgame.
Definition cauchy.hpp:306
CauchyEndgame(TrackerType const &tr, const ConfigsAsTuple &settings)
Construct the Cauchy endgame for a tracker, with its configuration as a tuple.
Definition cauchy.hpp:356
typename PrecT::TrackerType TrackerType
The path-tracker type.
Definition cauchy.hpp:170
void ClearTimesAndSamples()
Function that clears all samples and times from data members for the Cauchy endgame.
Definition cauchy.hpp:231
SuccessCode ComputeCauchySamplesAMP(BCT const &target_time)
Build a closed Cauchy loop's samples at the active numeric type, migrating-and-retrying in mpfr on es...
Definition cauchy.hpp:1641
EndgameBase< CauchyEndgame< PrecT >, PrecT > BaseEGT
The base endgame type.
Definition cauchy.hpp:168
void SetCauchySettings(CauchyConfig const &new_cauchy_settings)
Setter for the specific settings in tracking_conifg.hpp under Cauchy.
Definition cauchy.hpp:341
SuccessCode AdvanceTime(ComplexT const &target_time)
Advances time, marching toward the target time.
Definition cauchy.hpp:1027
CauchyEndgame< PrecT > EmitterType
The event-emitter type.
Definition cauchy.hpp:175
SampCont< ComplexT > & GetPSEGSamples()
Getter for the space values for the power series approximation of the Cauchy endgame.
Definition cauchy.hpp:265
const auto & GetCauchySettings() const
Getter for the specific settings in tracking_conifg.hpp under Cauchy.
Definition cauchy.hpp:349
void SetPSEGTimes(TimeCont< ComplexT > pseg_times_to_set)
Setter for the time values for the power series approximation of the Cauchy endgame.
Definition cauchy.hpp:241
void SetCauchyTimes(TimeCont< ComplexT > const &cauchy_times_to_set)
Setter for the time values for the Cauchy endgame.
Definition cauchy.hpp:297
TupleOfSamps cauchy_samples_
Definition cauchy.hpp:209
NumErrorT PoleComponentMassAMP() const
PoleComponentMass read from whichever numeric lane the adaptive (double-first) endgame is currently c...
Definition cauchy.hpp:1151
typename Configs::ToTuple ConfigsAsTuple
The configuration structs as a tuple.
Definition cauchy.hpp:192
SuccessCode RunInitSegmentT(ComplexT const &start_time, Vec< ComplexT > const &start_point, ComplexT const &target_time)
Run the initialization segment (seed the PSEG window and reach the EG zone) in a given numeric type.
Definition cauchy.hpp:1564
void SetCauchySamples(SampCont< ComplexT > const &cauchy_samples_to_set)
Setter for the space values for the Cauchy endgame.
Definition cauchy.hpp:273
CauchyEndgame< PrecT > FinalEGT
The final (derived) endgame type.
Definition cauchy.hpp:169
SuccessCode ComputeCauchyApproximationOfXAtT0(Vec< ComplexT > &result)
Function that computes the mean of the samples that were collected while tracking around the origin.
Definition cauchy.hpp:898
void ValidateConfigs()
Validate the endgame configuration (e.g. require >= 3 sample points for circle tracking).
Definition cauchy.hpp:370
typename BaseEGT::TupOfVec TupOfVec
A tuple of vector containers, one per precision.
Definition cauchy.hpp:186
SuccessCode ComputeCauchyApproxAMP()
Compute the Cauchy mean (extrapolation) at the active numeric type, migrating-and-retrying in mpfr on...
Definition cauchy.hpp:1581
SuccessCode InitialCauchyLoops(ComplexT const &target_time)
Definition cauchy.hpp:705
BaseRealT BRT
The real number type.
Definition cauchy.hpp:189
TupleOfTimes cauchy_times_
Definition cauchy.hpp:205
Advanced around the circle around target time.
Definition events.hpp:157
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
auto ComputeCOverK(SampCont< ComplexT > const &samples) const -> typename Eigen::NumTraits< ComplexT >::Real
Definition base_endgame.hpp:237
bool CheckForCOverKStabilization(std::deque< T > const &c_over_k_array) const
Definition base_endgame.hpp:276
NumErrorT approximate_error_
Definition base_endgame.hpp:140
const BCT & LatestTime() const
Definition base_endgame.hpp:610
unsigned CycleNumber() const
Definition base_endgame.hpp:541
typename NeededTypes::template ToTupleOfCont< SampCont > TupleOfSamps
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
void IncrementCycleNumber(unsigned inc)
Definition base_endgame.hpp:545
SuccessCode EscalateAndMigrate(unsigned guard)
Definition base_endgame.hpp:481
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
Vec< BCT > final_approximation_
Definition base_endgame.hpp:132
typename NeededTypes::template ToTupleOfCont< TimeCont > TupleOfTimes
Definition base_endgame.hpp:126
const auto & EndgameSettings() const
Definition base_endgame.hpp:504
typename NeededTypes::ToTupleOfVec TupOfVec
Definition base_endgame.hpp:124
const auto & FinalTolerance() const
Definition base_endgame.hpp:552
A common base type for various precision types, fixed and adaptive.
Definition prec_base.hpp:40
Event emitted when the endgame's working precision changes.
Definition events.hpp:220
Definition endgames.hpp:44
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
bool isnan(std::complex< double > const &z)
an overload of isnan, for std::complex<double>
Definition double_extensions.hpp:63
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
std::deque< Vec< T > > SampCont
Definition config.hpp:33
void SetThreadPrecision(unsigned prec)
Set the precision (digits) for the current thread only (does not touch the global default).
Definition mpfr_complex.hpp:176
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
@ CycleNumTooHigh
Definition config.hpp:66
@ SecurityMaxNormReached
Definition config.hpp:65
unsigned Precision(Eigen::MatrixBase< Derived > const &v)
Get the precision of an Eigen object.
Definition eigen_extensions.hpp:339
T max(T const &a, T const &b, T const &c)
Three-argument form of max.
Definition mpfr_extensions.hpp:96
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
void Set(T const &t)
Definition configured.hpp:101
Definition config.hpp:176
Cauchy-endgame configuration.
Definition config.hpp:142
Trait lookup mapping a tracker type to its numeric types, event-emitter type, precision config,...
Definition config.hpp:387