Bertini 2 - C++ Core
Software for Numerical Algebraic Geometry
Loading...
Searching...
No Matches
straight_line_program.hpp
Go to the documentation of this file.
1
2//This file is part of Bertini 2.
3//
4//straight_line_program.hpp is free software: you can redistribute it and/or modify
5//it under the terms of the GNU General Public License as published by
6//the Free Software Foundation, either version 3 of the License, or
7//(at your option) any later version.
8//
9//straight_line_program.hpp is distributed in the hope that it will be useful,
10//but WITHOUT ANY WARRANTY; without even the implied warranty of
11//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12//GNU General Public License for more details.
13//
14//You should have received a copy of the GNU General Public License
15//along with straight_line_program.hpp. If not, see <http://www.gnu.org/licenses/>.
16//
17// Copyright(C) 2021 by Bertini2 Development Team
18//
19// See <http://www.gnu.org/licenses/> for a copy of the license,
20// as well as COPYING. Bertini2 is provided with permitted
21// additional terms in the b2/licenses/ directory.
22
23// individual authors of this file include:
24// silviana amethyst, university of wisconsin eau claire
25// michael mumm, university of wisconsin eau claire
26
32
33#ifndef BERTINI_SLP_HPP
34#define BERTINI_SLP_HPP
35
36#pragma once
37
38#include <assert.h>
39#include <vector>
40#include <map>
41
47
48#include <boost/serialization/utility.hpp>
49
50// code copied from Bertini1's file include/bertini.h
51
52
53/*
54
55typedef struct
56{
57 int num_funcs;
58 int num_hom_var_gp;
59 int num_var_gp;
60 int *type; // 0 - hom_var_gp, 1 - var_gp
61 int *size; // size of the group of the user listed variables (total size = size + type)
62} preproc_data;
63
64
65typedef struct
66{
67 point_d funcVals;
68 point_d parVals;
69 vec_d parDer;
70 mat_d Jv;
71 mat_d Jp;
72} eval_struct_d;
73
74typedef struct
75{
76 point_mp funcVals;
77 point_mp parVals;
78 vec_mp parDer;
79 mat_mp Jv;
80 mat_mp Jp;
81} eval_struct_mp;
82
83
84The straight-line program structure. This is the way that polynomials are stored internally.
85typedef struct {
86 int *prog; // The program instructions. (a big integer array)
87 int size; // size of the instruction program.
88 int memSize; // Amount of memory it needs in workspace (for temp and final results).
89 num_t *nums; // The array of real numbers.
90 int precision; // The precision at which evaluation should occur
91
92 // INFO NEEDED FOR M-HOM:
93 int num_var_gps; // The total number of variable groups (i.e., m from m-hom).
94 int *var_gp_sizes; // The size of each of the groups.
95 int index_of_first_number_for_proj_trans; // The address of the first number used in the projective transformation polynomials.
96
97 // STOP LOCATIONS:
98 int numInstAtEndUpdate; // instruction number at end of update. i.e. i = 0; while (i < numInstAtEndUpdate) ..
99 int numInstAtEndParams; // instruction number at end of params. i.e. i = numInstAtEndUpdate; while (i < numInstAtEndParams) ..
100 int numInstAtEndFnEval; // instruction number at end of function eval. i.e. i = numInstAtEndParams; while (i < numInstAtEndFnEval) ..
101 int numInstAtEndPDeriv; // instruction number at end of param diff. i.e. i = numInstAtEndFnEval; while (i < numInstAtEndPDeriv) ..
102 int numInstAtEndJvEval; // instruction number at end of Jv eval. i.e. i = numInstAtEndPDeriv; while (i < numInstAtEndJvEval) ..
103 // for Jp eval: i = numInstAtEndJvEval; while (i < size) ..
104
105 // INPUT AMOUNTS:
106 int numVars; // Number of variables in the function being computed.
107 int numPathVars; // Number of path variables. Ought to be 1 usually.
108 int numNums; // Number of real numbers used in evaluation.
109 int numConsts; // Number of constants.
110
111 // OUTPUT AMOUNTS:
112 int numPars; // Number of parameters
113 int numFuncs; // Number of coordinate functions in the homotopy.
114 int numSubfuncs; // Number of subfunctions.
115
116 // INPUT LOCATIONS:
117 int inpVars; // Where the input variable values are stored.
118 int inpPathVars; // Where the values of the path variables are stored.
119 int IAddr; // Where the constant I is stored.
120 int numAddr; // Where the first num_t is stored.
121 int constAddr; // Where the first constant is stored.
122
123 // OUTPUT LOCATIONS:
124 int evalPars; // Where U(t), for given t, is stored.
125 int evalDPars; // Where the derivatives of the parameters are stored.
126 int evalFuncs; // Where H(x,t) is stored.
127 int evalJVars; // Where the Jacobian w.r.t. vars is stored.
128 int evalJPars; // Where the Jacobian w.r.t. pars is stored.
129 int evalSubs; // Where the subfunctions are stored
130 int evalJSubsV; // Where the derivatives of the subfunctions w.r.t. vars are stored.
131 int evalJSubsP; // Where the derivatives of the subfunctions w.r.t. pars are stored.
132} prog_t;
133*/
134
135
136
137namespace bertini {
138
139 class SLPCompiler;
140 class System; // a forward declaration, solving the circular inclusion problem
141
142
143 enum Operation { // we'll start with the binary ones
144 Add= 1 << 0,
145 Subtract= 1 << 1,
146 Multiply= 1 << 2,
147 Divide= 1 << 3,
148 Power= 1 << 4,
149 Exp= 1 << 5,
150 Log= 1 << 6,
151 Negate= 1 << 7,
152 Sqrt= 1 << 8,
153 Sin= 1 << 9,
154 Cos= 1 << 10,
155 Tan= 1 << 11,
156 Asin= 1 << 12,
157 Acos= 1 << 13,
158 Atan= 1 << 14,
159 Assign= 1 << 15,
160 IntPower= 1 << 16,
161 };
162
163 const int BinaryOperations = Add|Subtract | Multiply|Divide | Power | IntPower;
164 const int TrigOperations = Sin|Cos|Tan | Asin|Acos|Atan;
165 const int UnaryOperations = Exp|Log | Negate | Assign | TrigOperations | Sqrt;
166
167 constexpr bool IsUnary(Operation op)
168 {
169 return op & UnaryOperations;
170 }
171
172 constexpr bool IsBinary(Operation op)
173 {
174 return op & BinaryOperations;
175 }
176
177 std::string OpcodeToString(Operation op);
178
198 friend SLPCompiler;
199
200 private:
201 using Nd = std::shared_ptr<const node::Node>;
202
203 public:
204
211 size_t Functions{0};
212 size_t Jacobian{0};
213 size_t TimeDeriv{0};
214
215 friend class boost::serialization::access;
216
217 template <typename Archive>
218 void serialize(Archive& ar, const unsigned version) {
219 ar & Functions;
220 ar & Jacobian;
221 ar & TimeDeriv;
222 }
223
224 };
225
232 size_t Variables{0};
233 size_t Time{0};
234
235 friend class boost::serialization::access;
236
237 template <typename Archive>
238 void serialize(Archive& ar, const unsigned version) {
239 ar & Variables;
240 ar & Time;
241 }
242 };
243
249 struct NumberOf{
250 size_t Functions{0};
251 size_t Variables{0};
252 size_t Jacobian{0};
253 size_t TimeDeriv{0};
254
255 friend class boost::serialization::access;
256
257 template <typename Archive>
258 void serialize(Archive& ar, const unsigned version) {
259 ar & Functions;
260 ar & Variables;
261 ar & Jacobian;
262 ar & TimeDeriv;
263 }
264 };
265
269 StraightLineProgram(System const & sys);
270
271 StraightLineProgram() = default;
272
273 template<typename Derived>
274 void Eval(Eigen::MatrixBase<Derived> const& variable_values) const
275 {
276
277 using NumT = typename Derived::Scalar;
278 SetVariableValues(variable_values);
279
280 Eval<NumT>();
281
282
283 }
284
297 template<typename Derived, typename ComplexT>
298 void Eval(Eigen::MatrixBase<Derived> const& variable_values, ComplexT const& time) const
299 {
300 using NumT = typename Derived::Scalar;
301 static_assert(std::is_same<NumT, ComplexT>::value, "scalar types must be the same");
302
303 // 1. copy variable values into memory locations they're supposed to go in
304 SetVariableValues(variable_values);
305 SetPathVariable(time);
306 Eval<NumT>();
307 }
308
309
319 template<typename NumT>
320 void Eval() const; // this definition is in cpp, along with the lines that instantiate the needed versions.
321
322
323
324 // a placeholder function that needs to be written. now just calls eval, since the eval functionality is both functions and jacobian wrapped together -- we don't keep arrays of their locations separately yet, so that would be the starting point.
325 template <typename T>
326 void EvalFunctions() const{
327 this->Eval<T>();
328 }
329
330
331
332 // a placeholder function that needs to be written. now just calls eval, since the eval functionality is both functions and jacobian wrapped together -- we don't keep arrays of their locations separately yet, so that would be the starting point.
333 template <typename T>
334 void EvalJacobian() const{
335 this->Eval<T>();
336 }
337
338
339 // a placeholder function that needs to be written. now just calls eval, since the eval functionality is both functions and jacobian wrapped together -- we don't keep arrays of their locations separately yet, so that would be the starting point.
340 template <typename T>
341 void EvalTimeDeriv() const{
342 this->Eval<T>();
343 }
344
345
356 template<typename NumT>
357 void GetFuncValsInPlace(Vec<NumT> & result) const{
358 if (!is_evaluated_)
359 this->EvalFunctions<NumT>();
360
361 auto& memory = std::get<std::vector<NumT>>(memory_);
362
363 // copy content
364 for (int ii = 0; ii < number_of_.Functions; ++ii) {
365 result(ii) = memory[ii + output_locations_.Functions];
366 }
367 }
368
379
380 template<typename NumT>
381 void GetJacobianInPlace(Mat<NumT> & result) const{
382 if (!is_evaluated_)
383 this->EvalJacobian<NumT>();
384
385 auto& memory = std::get<std::vector<NumT>>(memory_);
386
387 // copy content
388 for (int jj =0; jj < number_of_.Variables; ++jj) {
389 for (int ii = 0; ii < number_of_.Functions; ++ii) {
390 result(ii, jj) = memory[ii+jj*number_of_.Functions + output_locations_.Jacobian];
391 }
392 }
393 }
394
405
406 template<typename NumT>
407 void GetTimeDerivInPlace(Vec<NumT> & result) const{
408 if (!is_evaluated_)
409 this->EvalTimeDeriv<NumT>();
410
411 auto& memory = std::get<std::vector<NumT>>(memory_);
412 // 1. make container, size correctly.
413 // 2. copy content
414 for (int ii = 0; ii < number_of_.Functions; ++ii) {
415 result(ii) = memory[ii + output_locations_.TimeDeriv];
416 }
417 }
418
425 template<typename NumT>
426 Vec<NumT> GetFuncVals() const{
427 Vec<NumT> return_me(this->NumFunctions());
428 GetFuncValsInPlace(return_me);
429 return return_me;
430 }
431
437 template<typename NumT>
438 Mat<NumT> GetJacobian() const{
439 Mat<NumT> return_me(this->NumFunctions(), this->NumVariables());
440 GetJacobianInPlace(return_me);
441 return return_me;
442 }
443
449 template<typename NumT>
450 Vec<NumT> GetTimeDeriv() const{
451 Vec<NumT> return_me(this->NumFunctions());
452 GetTimeDerivInPlace(return_me);
453 return return_me;
454 }
455
456
457 inline unsigned NumFunctions() const{ return number_of_.Functions;}
458
459 inline unsigned NumVariables() const{ return number_of_.Variables;}
460
461
467 inline
468 unsigned precision() const
469 {
470 return precision_;
471 }
472
480 void precision(unsigned new_precision) const;
481
487 bool HavePathVariable() const {
488 return this->has_path_variable_;
489 }
490
494 friend std::ostream& operator <<(std::ostream& out, const StraightLineProgram & s);
495
496
497
498
504 template<typename Derived>
505 void SetVariableValues(Eigen::MatrixBase<Derived> const& variable_values) const{
506 using NumT = typename Derived::Scalar;
507
508#ifndef BERTINI_DISABLE_PRECISION_CHECKS
509 if (!std::is_same<NumT,dbl_complex>::value && Precision(variable_values)!=this->precision_){
510 std::stringstream err_msg;
511 err_msg << "variable_values and SLP must be of same precision. respective precisions: " << Precision(variable_values) << " " << this->precision_ << std::endl;
512 throw std::runtime_error(err_msg.str());
513 }
514#endif
515
516 using NumT = typename Derived::Scalar;
517 auto& memory = std::get<std::vector<NumT>>(memory_); // unpack for local reference
518
519 for (int ii = 0; ii < number_of_.Variables; ++ii) {
520 //assign to memory
521 memory[ii + input_locations_.Variables] = variable_values(ii);
522 }
523 is_evaluated_ = false;
524 }
525
534 template<typename ComplexT>
535 void SetPathVariable(ComplexT const& time) const{
536
537#ifndef BERTINI_DISABLE_PRECISION_CHECKS
538 if (Precision(time)!= DoublePrecision() && Precision(time)!=this->precision_){
539 std::stringstream err_msg;
540 err_msg << "time value and SLP must be of same precision. respective precisions: " << Precision(time) << " " << this->precision_ << std::endl;
541 throw std::runtime_error(err_msg.str());
542 }
543#endif
544
545 if (!this->HavePathVariable())
546 throw std::runtime_error("calling Eval with path variable, but this StraightLineProgram doesn't have one.");
547 // then actually copy the path variable into where it goes in memory
548
549 auto& memory = std::get<std::vector<ComplexT>>(memory_); // unpack for local reference
550
551 memory[input_locations_.Time] = time;
552 is_evaluated_ = false;
553 }
554
555
556
557
558
559
560 using IntT = int; // this needs to co-vary on the stored type inside the node. node should stop using mpz, it's slow.
561
562 private:
563
572 void AddInstruction(Operation binary_op, size_t in_loc1, size_t in_loc2, size_t out_loc);
573
581 void AddInstruction(Operation unary_op, size_t in_loc, size_t out_loc);
582
583
587 void AddNumber(Nd const num, size_t loc);
588
589 template<typename NumT>
590 auto& GetMemory() const{
591 return std::get<std::vector<NumT>>(this->memory_);
592 }
593
594
595 template<typename NumT>
596 void CopyNumbersIntoMemory() const;
597
598
599 mutable unsigned precision_ = 16; //< The current working number of digits
600 bool has_path_variable_ = false; //< Does this SLP have a path variable?
601
602 NumberOf number_of_; //< Quantities of things
603 OutputLocations output_locations_; //< Where to find outputs, like functions and derivatives
604 InputLocations input_locations_; //< Where to find inputs, like variables and time
605
606 mutable std::tuple< std::vector<dbl_complex>, std::vector<mpfr_complex> > memory_; //< The memory of the object. Numbers and variables, plus temp results and output locations. It's all one block. That's why it's called a SLP!
607 std::vector<IntT> integers_;
608
609 std::vector<size_t> instructions_; //< The instructions. The opcodes are stored as size_t's, as well as the locations of operands and results.
610 std::vector< std::pair<Nd,size_t> > true_values_of_numbers_; //< the size_t is where in memory to downsample to.
611
612 mutable bool is_evaluated_ = false;
613
614
615
616 friend class boost::serialization::access;
617
618 template <typename Archive>
619 void serialize(Archive& ar, const unsigned version) {
620
621 ar & precision_;
622 ar & has_path_variable_;
623
624 ar & number_of_;
625 ar & output_locations_;
626 ar & input_locations_;
627
628 ar & std::get<std::vector<dbl_complex>>(memory_);
629 ar & std::get<std::vector<mpfr_complex>>(memory_);
630 ar & integers_;
631
632 ar & instructions_;
633 ar & true_values_of_numbers_;
634
635 ar & is_evaluated_;
636 }
637
638 };
639
640
641 class SLPCompiler : public VisitorBase,
642
643 // IF YOU ADD A THING HERE, YOU MUST ADD IT ABOVE AND IN THE CPP SOURCE
644
645
646 // symbols and roots
647 public Visitor<node::Variable>,
648 public Visitor<node::Integer>,
649 public Visitor<node::Float>,
650 public Visitor<node::Rational>,
651 public Visitor<node::Function>,
652 public Visitor<node::Jacobian>,
653 public Visitor<node::Differential>,
654
655 // arithmetic
656 public Visitor<node::SumOperator>,
657 public Visitor<node::MultOperator>,
658 public Visitor<node::IntegerPowerOperator>,
659 public Visitor<node::PowerOperator>,
660 public Visitor<node::ExpOperator>,
661 public Visitor<node::LogOperator>,
662 public Visitor<node::NegateOperator>,
663 public Visitor<node::SqrtOperator>,
664
665 // the trig operators
666 public Visitor<node::SinOperator>,
667 public Visitor<node::ArcSinOperator>,
668 public Visitor<node::CosOperator>,
669 public Visitor<node::ArcCosOperator>,
670 public Visitor<node::TanOperator>,
671 public Visitor<node::ArcTanOperator>,
672
673 public Visitor<node::special_number::Pi>,
674 public Visitor<node::special_number::E>
675
676 // also missing -- linears and difflinears.
677
678 // these abstract base types left out,
679
680 // but commented here to explain why
681 // public Visitor<node::Operator>,// abstract
682 // public Visitor<node::UnaryOperator>,// abstract
683 // public Visitor<node::NaryOperator>,// abstract
684 // public Visitor<node::TrigOperator>,// abstract
685 {
686 private:
687 using Nd = std::shared_ptr<const node::Node>;
688 using SLP = StraightLineProgram;
689
690 public:
691
692 SLP Compile(System const& sys);
693
694
695 // IF YOU ADD A THING HERE, YOU MUST ADD IT ABOVE AND IN THE CPP SOURCE
696
697 // symbols and roots
698 virtual void Visit(node::Variable const& n);
699 virtual void Visit(node::Integer const& n);
700 virtual void Visit(node::Float const& n);
701 virtual void Visit(node::Rational const& n);
702 virtual void Visit(node::Function const& n);
703 virtual void Visit(node::Jacobian const& n);
704 virtual void Visit(node::Differential const& n);
705
706 // arithmetic
707 virtual void Visit(node::SumOperator const& n);
708 virtual void Visit(node::MultOperator const& n);
709 virtual void Visit(node::IntegerPowerOperator const& n);
710 virtual void Visit(node::PowerOperator const& n);
711 virtual void Visit(node::ExpOperator const& n);
712 virtual void Visit(node::LogOperator const& n);
713 virtual void Visit(node::NegateOperator const& n);
714 virtual void Visit(node::SqrtOperator const& n);
715
716
717 // the trig operators
718 virtual void Visit(node::SinOperator const& n);
719 virtual void Visit(node::ArcSinOperator const& n);
720 virtual void Visit(node::CosOperator const& n);
721 virtual void Visit(node::ArcCosOperator const& n);
722 virtual void Visit(node::TanOperator const& n);
723 virtual void Visit(node::ArcTanOperator const& n);
724
725 virtual void Visit(node::special_number::Pi const& n);
726 virtual void Visit(node::special_number::E const& n);
727 // missing -- linear and difflinear
728 private:
729
730
734 template<typename NodeT>
735 void DealWithNumber(NodeT const& n){
736 auto nd=n.shared_from_this(); // make a shared pointer to the node, so that it survives, and we get polymorphism
737 this->slp_under_construction_.AddNumber(nd, next_available_complex_); // register the number with the SLP
738 this->locations_encountered_nodes_[nd] = next_available_complex_++; // add to found symbols in the compiler, increment counter.
739 }
740
744 void Clear();
745
746 size_t next_available_complex_ = 0; //< Where should the next complex number go in memory?
747 size_t next_available_int_ = 0; //< Where should the next integer go?
748
749 using IntT = int; // this needs to co-vary on the stored type inside the node. node should stop using mpz, it's slow.
750
751 std::map<Nd, size_t> locations_encountered_nodes_; //< A registry of pointers-to-nodes and location in memory on where to find *their results*
752 std::map<IntT, size_t> locations_integers_;
753 std::map<Nd, size_t> locations_top_level_functions_and_derivatives_;
754
755 SLP slp_under_construction_; //< the under-construction SLP. will be returned at end of `compile`
756 };
757
758
759
760} // namespace bertini
761
762
763
764
765
766
767
768
769
770#endif // for the ifndef include guards
Definition straight_line_program.hpp:197
StraightLineProgram(System const &sys)
Definition straight_line_program.cpp:62
void Eval() const
loops through the instructions in memory and evaluates each operation
Definition straight_line_program.cpp:207
friend std::ostream & operator<<(std::ostream &out, const StraightLineProgram &s)
Overloaded operator for printing to an arbirtary out stream.
Definition straight_line_program.cpp:118
Vec< NumT > GetTimeDeriv() const
creates the Vec<NumT> to be used in the overloaded function
Definition straight_line_program.hpp:450
void SetVariableValues(Eigen::MatrixBase< Derived > const &variable_values) const
Copy the values of the variables from the passed in vector to memory.
Definition straight_line_program.hpp:505
unsigned precision() const
Get the current precision of the SLP.
Definition straight_line_program.hpp:468
void GetFuncValsInPlace(Vec< NumT > &result) const
assignts the computed values of functions into the given vector
Definition straight_line_program.hpp:357
bool HavePathVariable() const
Does this SLP have a path variable?
Definition straight_line_program.hpp:487
void SetPathVariable(ComplexT const &time) const
Copy the current time value to memory.
Definition straight_line_program.hpp:535
Mat< NumT > GetJacobian() const
creates the Vec<NumT> to be used in the overloaded function
Definition straight_line_program.hpp:438
void Eval(Eigen::MatrixBase< Derived > const &variable_values, ComplexT const &time) const
copies the variable values into the Matrix base and the path variables into the complex type time
Definition straight_line_program.hpp:298
void GetJacobianInPlace(Mat< NumT > &result) const
retrieves the computed values of jacobians
Definition straight_line_program.hpp:381
Vec< NumT > GetFuncVals() const
creates the Vec<NumT> to be used in the overloaded function
Definition straight_line_program.hpp:426
void GetTimeDerivInPlace(Vec< NumT > &result) const
copies the values of the time derivatives into your given vector
Definition straight_line_program.hpp:407
The fundamental polynomial system class for Bertini2.
Definition system/system.hpp:99
A strawman class for implementing the visitor pattern.
Definition visitor.hpp:46
The first non-trivial visitor class.
Definition visitor.hpp:62
Bertini extensions of the Eigen linear algebra library.
unsigned Precision(Eigen::MatrixBase< Derived > const &v)
Get the precision of an Eigen object. If the object is empty, it's the precision of a default-constru...
Definition eigen_extensions.hpp:292
Forward declarations for types in the node:: namespace.
The main multiprecision complex number type. This is essentially boost::multiprecision' complex.
Extensions to the Boost.Multiprecision library.
Definition straight_line_program.hpp:231
Definition straight_line_program.hpp:249
Definition straight_line_program.hpp:210
Contains the visitor base types.