Bertini 2 d882924 (2026-10-06 19:56 UTC)
Numerical algebraic geometry library (C++ core)
Loading...
Searching...
No Matches
slice.hpp
Go to the documentation of this file.
1//This file is part of Bertini 2.
2//
3//slice.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//slice.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 slice.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
39
40
41#ifndef BERTINI_SLICE_HPP
42#define BERTINI_SLICE_HPP
43
44#include <vector>
45
51
52
53namespace bertini {
54
55 // Slice::AddTo hands the slice's linear-forms block to a System; we only need System's
56 // name here (the definition lives in slice.cpp, which includes system.hpp).
57 class System;
58
67 class Slice
68 {
70 VariableGroup sliced_vars_;
71 bool is_homogeneous_ = false;
72
73 public:
74
76 Slice() = default;
77
86 static Slice FromCoefficients(VariableGroup const& v, Mat<complex_mp> const& augmented_coefficients, bool homogeneous = false)
87 {
88 assert(static_cast<size_t>(augmented_coefficients.cols()) == v.size() + 1 &&
89 "a slice coefficient matrix must have (num_variables + 1) columns");
90 Slice s;
91 s.sliced_vars_ = v;
92 s.is_homogeneous_ = homogeneous;
93 s.block_ = blocks::LinearFormsBlock(v.size(), augmented_coefficients);
94 return s;
95 }
96
110 static Slice ThroughPoint(VariableGroup const& v, Mat<complex_mp> const& coefficients, Vec<complex_mp> const& point)
111 {
112 if (static_cast<size_t>(coefficients.cols()) != v.size())
113 throw std::runtime_error("Slice::ThroughPoint coefficient block must have num_variables columns (it is not augmented)");
114 if (static_cast<size_t>(point.size()) != v.size())
115 throw std::runtime_error("Slice::ThroughPoint point must have num_variables entries");
116
117 return FromCoefficients(v, AugmentAffineThroughPoint(coefficients, point), /*homogeneous=*/false);
118 }
119
129 static Slice RandomReal(VariableGroup const& v, unsigned dim, bool homogeneous = false, bool orthogonal = true, Vec<complex_mp> const* through_point = nullptr)
130 {
131 typedef void (*funtype) (complex_mp&, unsigned); // the type for number generation
132 // bounded-modulus draw (away from 0 and infinity), matching patches and the start systems;
133 // kept REAL so a real slice stays real. The orthogonal path is real too (real=true below):
134 // it QR-factors a matrix of REAL units, yielding a real orthogonal coefficient block (issue
135 // #294 -- previously the orthogonal path hardcoded the complex orthonormal matrix, so a
136 // "real" slice came out complex).
138 return Make(v, dim, homogeneous, orthogonal, /*real=*/true, gen, through_point);
139 }
140
150 static Slice RandomComplex(VariableGroup const& v, unsigned dim, bool homogeneous = false, bool orthogonal = true, Vec<complex_mp> const* through_point = nullptr)
151 {
152 typedef void (*funtype) (complex_mp&, unsigned); // the type for number generation
153 // bounded-modulus draw (away from 0 and infinity), matching patches and the start systems.
155 return Make(v, dim, homogeneous, orthogonal, /*real=*/false, gen, through_point);
156 }
157
173 static Slice Make(VariableGroup const& v, unsigned dim, bool homogeneous, bool orthogonal, bool real, std::function<void(complex_mp&, unsigned)> gen, Vec<complex_mp> const* through_point = nullptr)
174 {
175 const unsigned num_vars = static_cast<unsigned>(v.size());
176
177 // more forms than variables cannot be independent: the orthonormalization below would
178 // hand back dependent rows without a word, and the slice would cut nothing new (b2#380)
179 if (dim > num_vars)
180 throw std::invalid_argument("Slice: " + std::to_string(dim) + " linear forms on "
181 + std::to_string(num_vars) + " variables; a slice cannot have more forms than variables");
182 if (homogeneous && through_point && dim + 1 > num_vars)
183 throw std::invalid_argument("Slice: " + std::to_string(dim) + " homogeneous linear forms through a point on "
184 + std::to_string(num_vars) + " variables; the forms live in the point's orthogonal complement, which has "
185 + std::to_string(num_vars - 1) + " dimensions");
186
187 if (through_point && static_cast<unsigned>(through_point->size()) != num_vars)
188 throw std::runtime_error("Slice: through_point must have num_variables entries");
189
190 Mat<complex_mp> coeffs(dim, num_vars); // the variable coefficients (one row per form)
191
192 if (orthogonal)
193 {
194 // conjugate-orthonormal coefficient matrix (orthonormal linear forms), drawn the b1 way
195 // via RandomConjugateOrthonormalMatrix (ADR-0041) -- it generates square and truncates,
196 // so the old transpose dance is gone. Built at max precision, like the rest of the slice.
197 // A real slice QR-factors a matrix of REAL units (a real orthogonal block); a complex
198 // slice uses complex units. (Issue #294: the real path must stay real.)
199 auto prev_precision = DefaultPrecision();
201 if (real)
203 .unaryExpr([](real_mp const& r){ return complex_mp(r); });
204 else
206 DefaultPrecision(prev_precision);
207 }
208 else
209 {
210 for (unsigned ii(0); ii < dim; ++ii)
211 for (unsigned jj(0); jj < num_vars; ++jj)
212 gen(coeffs(ii, jj), MaxPrecisionAllowed());
213 }
214
215 assert(static_cast<unsigned>(coeffs.rows()) == dim);
216 assert(static_cast<unsigned>(coeffs.cols()) == num_vars);
217
218 // Through a point. Homogeneous: project the rows into p's orthogonal complement (a.p = 0),
219 // zero constant column. Affine: the constant column is -A*p, so every form vanishes at p.
220 if (through_point && homogeneous)
221 return FromCoefficients(v, AugmentHomogeneousThroughPoint(coeffs, *through_point, orthogonal), /*homogeneous=*/true);
222 if (through_point)
223 return FromCoefficients(v, AugmentAffineThroughPoint(coeffs, *through_point), /*homogeneous=*/false);
224
225 // Assemble the augmented matrix: [ coeffs | constants ]. A homogeneous slice's constant
226 // column is zero; otherwise it is freshly generated.
227 Mat<complex_mp> augmented(dim, num_vars + 1);
228 augmented.leftCols(num_vars) = coeffs;
229 if (homogeneous)
230 augmented.col(num_vars).setZero();
231 else
232 for (unsigned ii(0); ii < dim; ++ii)
233 gen(augmented(ii, num_vars), MaxPrecisionAllowed());
234
235 return FromCoefficients(v, augmented, homogeneous);
236 }
237
238
242 template<typename NumT>
243 void Eval(Vec<NumT> & result, Vec<NumT> const& x) const
244 {
245 result.resize(Dimension());
246 NumT path_value(0); // ignored: linear forms are autonomous
247 block_.EvalInPlace<NumT>(result, x, path_value);
248 }
249
253 template<typename NumT>
254 Vec<NumT> Eval(Vec<NumT> const& x) const
255 {
256 Vec<NumT> result(Dimension());
257 Eval(result, x);
258 return result;
259 }
260
264 template<typename NumT>
265 void Jacobian(Mat<NumT> & result, Vec<NumT> const& x) const
266 {
267 result.resize(Dimension(), NumVariables());
268 NumT path_value(0);
269 block_.JacobianInPlace<NumT>(result, x, path_value);
270 }
271
275 template<typename NumT>
277 {
278 Mat<NumT> result(Dimension(), NumVariables());
279 Jacobian(result, x);
280 return result;
281 }
282
283
292 {
293 return block_.Coefficients();
294 }
295
298 {
299 return block_;
300 }
301
303 void AddTo(System & s) const;
304
309
317 Slice Concatenate(Slice const& other) const
318 {
319 if (NumVariables() != other.NumVariables())
320 throw std::runtime_error("Slice::Concatenate requires both slices to be on the same number of variables");
321
322 Mat<complex_mp> const& A = Coefficients();
323 Mat<complex_mp> const& B = other.Coefficients();
324 Mat<complex_mp> stacked(A.rows() + B.rows(), A.cols());
325 stacked.topRows(A.rows()) = A;
326 stacked.bottomRows(B.rows()) = B;
327
328 return FromCoefficients(sliced_vars_, stacked, is_homogeneous_ && other.is_homogeneous_);
329 }
330
331
335 Slice Head(unsigned m) const
336 {
337 if (m > Dimension())
338 throw std::runtime_error("Slice::Head asked for more forms than the slice has");
339 return FromCoefficients(sliced_vars_, Coefficients().topRows(m), is_homogeneous_);
340 }
341
345 Slice Tail(unsigned m) const
346 {
347 if (m > Dimension())
348 throw std::runtime_error("Slice::Tail asked for more forms than the slice has");
349 return FromCoefficients(sliced_vars_, Coefficients().bottomRows(m), is_homogeneous_);
350 }
351
355 Slice Rows(std::vector<unsigned> const& indices) const
356 {
357 Mat<complex_mp> const& C = Coefficients();
358 Mat<complex_mp> sub(static_cast<Eigen::Index>(indices.size()), C.cols());
359 for (size_t ii = 0; ii < indices.size(); ++ii)
360 {
361 if (indices[ii] >= Dimension())
362 throw std::runtime_error("Slice::Rows asked for a form index outside the slice");
363 sub.row(static_cast<Eigen::Index>(ii)) = C.row(indices[ii]);
364 }
365 return FromCoefficients(sliced_vars_, sub, is_homogeneous_);
366 }
367
368
372 unsigned Dimension() const
373 {
374 return static_cast<unsigned>(block_.NumFunctions());
375 }
376
380 unsigned NumVariables() const
381 {
382 return static_cast<unsigned>(sliced_vars_.size());
383 }
384
389 {
390 return sliced_vars_;
391 }
392
396 bool IsHomogeneous() const
397 {
398 return is_homogeneous_;
399 }
400
401
405 unsigned Precision() const
406 {
407 return block_.Precision();
408 }
409
413 void Precision(unsigned new_precision) const
414 {
415 block_.Precision(new_precision);
416 }
417
418 private:
419
430 static Mat<complex_mp> AugmentAffineThroughPoint(Mat<complex_mp> const& coeffs, Vec<complex_mp> const& point)
431 {
432 const auto num_vars = coeffs.cols();
433
434 auto prev_precision = DefaultPrecision();
436
437 Mat<complex_mp> A = coeffs; // copies, re-materialized at max precision below
438 Vec<complex_mp> p = point;
439 bertini::Precision(A, MaxPrecisionAllowed()); // qualify: class has member Precision overloads
441
442 Mat<complex_mp> augmented(coeffs.rows(), num_vars + 1);
443 augmented.leftCols(num_vars) = A;
444 augmented.col(num_vars) = -(A * p); // b = -A*p, so A*p + b = 0
445
446 DefaultPrecision(prev_precision);
447 return augmented;
448 }
449
464 static Mat<complex_mp> AugmentHomogeneousThroughPoint(Mat<complex_mp> const& coeffs, Vec<complex_mp> const& point, bool orthogonal)
465 {
466 const auto dim = coeffs.rows();
467 const auto num_vars = coeffs.cols();
468
469 auto prev_precision = DefaultPrecision();
471
472 Mat<complex_mp> A = coeffs;
473 Vec<complex_mp> p = point;
474 bertini::Precision(A, MaxPrecisionAllowed()); // qualify: class has member Precision overloads
476
477 real_mp p_norm_sq = p.squaredNorm(); // pbar.p = ||p||^2, real-positive for p != 0
478 if (p_norm_sq == real_mp(0))
479 throw std::runtime_error("Slice: a homogeneous slice through a point needs a nonzero point");
480
481 // project every row into p's orthogonal complement (bilinear a.p = 0): subtract the
482 // rank-one correction (A*p) pbar^T / ||p||^2.
483 Vec<complex_mp> Ap = A * p; // bilinear a_i . p, one per row
484 A = A - (Ap / complex_mp(p_norm_sq)) * p.conjugate().transpose();
485
486 if (orthogonal)
487 {
488 // orthonormalize the projected rows via a QR of A^T: the columns of Q span the row
489 // space of A (which lies in p's complement), so Q^T's rows stay orthogonal to p.
490 Eigen::HouseholderQR<Mat<complex_mp>> qr(A.transpose());
491 Mat<complex_mp> Q = qr.householderQ() * Mat<complex_mp>::Identity(num_vars, num_vars);
492 A = Q.leftCols(dim).transpose();
493 }
494
495 Mat<complex_mp> augmented(dim, num_vars + 1);
496 augmented.leftCols(num_vars) = A;
497 augmented.col(num_vars).setZero(); // homogeneous: no constant term
498
499 DefaultPrecision(prev_precision);
500 return augmented;
501 }
502
504
505 template <typename Archive>
506 void serialize(Archive& ar, const unsigned /*version*/) {
507 ar & block_;
508 ar & sliced_vars_;
509 ar & is_homogeneous_;
510
511 // a loaded slice's variables join the live node universe, as a loaded System's do
512 // (ADR-0068): otherwise its system of linear forms could not be combined with the
513 // system it slices
514 if (Archive::is_loading::value)
515 for (auto& v : sliced_vars_)
516 if (v)
517 v = std::static_pointer_cast<node::Variable>(node::Reintern(v));
518 }
519
520 friend std::ostream& operator<<(std::ostream&, Slice const&);
521 };
522
526 std::ostream& operator<<(std::ostream& out, Slice const& s);
527} // re: namespace bertini
528
529#endif
A linear slice of affine or projective space: a stack of linear forms, M [x ; 1].
Definition slice.hpp:68
Slice Concatenate(Slice const &other) const
A new slice stacking this slice's linear forms on top of other's.
Definition slice.hpp:317
void Precision(unsigned new_precision) const
Set the working precision of the slice, in digits.
Definition slice.hpp:413
void Eval(Vec< NumT > &result, Vec< NumT > const &x) const
Evaluate the slice's linear-form values, in-place.
Definition slice.hpp:243
static Slice FromCoefficients(VariableGroup const &v, Mat< complex_mp > const &augmented_coefficients, bool homogeneous=false)
Build a slice directly from an augmented coefficient matrix.
Definition slice.hpp:86
Mat< complex_mp > const & Coefficients() const
The augmented coefficient matrix: one row per linear form, (Dimension) x (NumVariables+1),...
Definition slice.hpp:291
static Slice ThroughPoint(VariableGroup const &v, Mat< complex_mp > const &coefficients, Vec< complex_mp > const &point)
Build an affine slice whose every linear form vanishes at a given point.
Definition slice.hpp:110
bool IsHomogeneous() const
Whether the slice was authored without constant terms (passes through the origin).
Definition slice.hpp:396
VariableGroup const & Variables() const
The variables the slice is a function of.
Definition slice.hpp:388
unsigned Dimension() const
The dimension of the slice – the number of linear forms.
Definition slice.hpp:372
unsigned NumVariables() const
The number of variables sliced.
Definition slice.hpp:380
Slice Tail(unsigned m) const
A new slice over the same variables built from the last m linear forms.
Definition slice.hpp:345
Vec< NumT > Eval(Vec< NumT > const &x) const
Evaluate the slice's linear-form values.
Definition slice.hpp:254
Mat< NumT > Jacobian(Vec< NumT > const &x) const
The slice's Jacobian (its constant variable-coefficient matrix).
Definition slice.hpp:276
friend std::ostream & operator<<(std::ostream &, Slice const &)
Provides output streaming for Slice.
void Jacobian(Mat< NumT > &result, Vec< NumT > const &x) const
The slice's Jacobian (its constant variable-coefficient matrix), in-place.
Definition slice.hpp:265
blocks::LinearFormsBlock const & AsLinearFormsBlock() const
The underlying linear-forms block (eval / Jacobian / precision engine).
Definition slice.hpp:297
Slice Head(unsigned m) const
A new slice over the same variables built from the first m linear forms.
Definition slice.hpp:335
static Slice RandomComplex(VariableGroup const &v, unsigned dim, bool homogeneous=false, bool orthogonal=true, Vec< complex_mp > const *through_point=nullptr)
Generate a random complex slice.
Definition slice.hpp:150
Slice()=default
An empty slice (zero forms, zero variables).
unsigned Precision() const
Get the current working precision of the slice, in digits.
Definition slice.hpp:405
Slice Rows(std::vector< unsigned > const &indices) const
A new slice over the same variables built from the chosen linear forms.
Definition slice.hpp:355
void AddTo(System &s) const
Add this slice's linear forms to a System as a LinearFormsBlock. (Defined in slice....
friend class boost::serialization::access
Definition slice.hpp:503
static Slice Make(VariableGroup const &v, unsigned dim, bool homogeneous, bool orthogonal, bool real, std::function< void(complex_mp &, unsigned)> gen, Vec< complex_mp > const *through_point=nullptr)
Factory for generating slices.
Definition slice.hpp:173
System AsSystem() const
A standalone System whose functions are exactly this slice's linear forms (over the slice's variable ...
static Slice RandomReal(VariableGroup const &v, unsigned dim, bool homogeneous=false, bool orthogonal=true, Vec< complex_mp > const *through_point=nullptr)
Produce a random real slice on a variable group, slicing a given number of dimensions.
Definition slice.hpp:129
The fundamental polynomial system class for Bertini2.
Definition system.hpp:106
A block of system functions, each an affine linear form f_i(x) = c_i .
Definition linear_forms_block.hpp:68
Bertini extensions of the Eigen linear algebra library.
Collects the various header files which define the Bertini2 function tree node types.
A System evaluation block whose functions are linear forms: f(x) = M x + b.
void RandomComplexBoundedModulusAssign(complex &a, unsigned num_digits)
Assign to a a random complex number whose modulus is pulled toward 1, at the given precision.
Definition random.hpp:355
void RandomRealBoundedModulusAssign(complex &a, unsigned num_digits)
Assign to a a random REAL number (imaginary part 0) whose modulus is pulled toward 1,...
Definition random.hpp:415
std::shared_ptr< Node > Reintern(std::shared_ptr< Node > const &root, ReinternMemo &memo)
Rebuild the DAG rooted at root through the interning factories (memoized, post-order),...
Definition algorithm_builder.hpp:36
Eigen::Matrix< NumT, Eigen::Dynamic, 1 > Vec
A dynamically-sized column vector of NumT.
Definition eigen_extensions.hpp:296
std::vector< std::shared_ptr< node::Variable > > VariableGroup
An ordered group of variables (the unit of homogenization / variable grouping).
Definition node.hpp:69
std::ostream & operator<<(typename std::enable_if< std::is_enum< T >::value, std::ostream >::type &stream, const T &e)
Method for printing class enums to streams.
Definition stream_enum.hpp:45
Eigen::Matrix< NumT, Eigen::Dynamic, Eigen::Dynamic > Mat
A dynamically-sized matrix of NumT.
Definition eigen_extensions.hpp:298
constexpr unsigned MaxPrecisionAllowed()
The maximum precision (digits) the library will escalate to.
Definition num_traits.hpp:166
bmp::number< mpc_complex_backend< 0 >, bmp::et_on > complex_mp
The arbitrary-precision complex number type.
Definition mpfr_complex.hpp:58
auto DefaultPrecision()
Get the global default precision (digits) for multiprecision numbers.
Definition mpfr_complex.hpp:135
boost::multiprecision::number< boost::multiprecision::mpfr_float_backend< 0 >, boost::multiprecision::et_on > real_mp
The arbitrary-precision real number type.
Definition mpfr_extensions.hpp:53
Mat< NumberType > RandomConjugateOrthonormalMatrix(unsigned int rows, unsigned int cols)
Make a random conjugate-orthonormal matrix (orthonormal rows when rows<=cols, orthonormal columns whe...
Definition eigen_extensions.hpp:602
unsigned Precision(Eigen::MatrixBase< Derived > const &v)
Get the precision of an Eigen object.
Definition eigen_extensions.hpp:339
Provides an Eigen-like NumTraits struct for querying traits of a number type.
Re-intern deserialized node DAGs into the live hash-consing tables (ADR-0042).