Bertini 2 d882924 (2026-10-06 19:56 UTC)
Numerical algebraic geometry library (C++ core)
Loading...
Searching...
No Matches
linear_forms_block.hpp
Go to the documentation of this file.
1//This file is part of Bertini 2.
2//
3//linear_forms_block.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//linear_forms_block.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 linear_forms_block.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
47
48#pragma once
49
50#include <stdexcept>
51#include <string>
52#include <tuple>
53
57
58namespace bertini {
59namespace blocks {
60
68{
69public:
70 LinearFormsBlock() : num_vars_(0), precision_(DefaultPrecision()) {}
71
78 LinearFormsBlock(size_t num_vars, Mat<complex_mp> coefficients)
79 : num_vars_(num_vars), coefficients_highest_precision_(std::move(coefficients)),
80 precision_(DefaultPrecision())
81 {
82 assert(static_cast<size_t>(coefficients_highest_precision_.cols()) == num_vars_ + 1 &&
83 "a linear-forms coefficient matrix must have num_vars+1 columns");
84 BuildWorking();
85 }
86
98 LinearFormsBlock(size_t num_vars, Mat<complex_mp> coefficients, bool homogenized)
99 : num_vars_(num_vars), coefficients_highest_precision_(std::move(coefficients)),
100 precision_(DefaultPrecision())
101 {
102 homogeneous_ = homogenized;
103 if (static_cast<size_t>(coefficients_highest_precision_.cols()) != num_vars_ + (homogenized ? 0 : 1))
104 throw std::invalid_argument("LinearFormsBlock: the coefficient matrix has "
105 + std::to_string(coefficients_highest_precision_.cols()) + " columns, but a block on "
106 + std::to_string(num_vars_) + " variables that " + (homogenized ? "is" : "is not")
107 + " homogenized needs " + std::to_string(num_vars_ + (homogenized ? 0 : 1)));
108 BuildWorking();
109 }
110
112 size_t NumFunctions() const { return static_cast<size_t>(coefficients_highest_precision_.rows()); }
113
115 std::vector<int> Degrees() const { return std::vector<int>(NumFunctions(), 1); }
117 std::vector<int> Degrees(VariableGroup const&) const { return Degrees(); }
118
120 bool IsPolynomial(VariableGroup const&) const { return true; }
123 bool IsHomogeneous(VariableGroup const&) const { return homogeneous_; }
124
131 void Homogenize(VariableGroup const& /*group*/, std::shared_ptr<node::Variable> const& /*hom_var*/)
132 {
133 if (homogeneous_)
134 throw std::runtime_error("LinearFormsBlock::Homogenize: block is already homogenized "
135 "(multiple affine variable groups are not yet supported for linear-forms blocks)");
136 const auto& M = coefficients_highest_precision_;
137 Mat<complex_mp> Mh(M.rows(), M.cols()); // same #cols: n+1
138 Mh.col(0) = M.col(static_cast<Eigen::Index>(num_vars_)); // constant -> h column (front)
139 Mh.rightCols(static_cast<Eigen::Index>(num_vars_)) =
140 M.leftCols(static_cast<Eigen::Index>(num_vars_)); // original variable columns
141 coefficients_highest_precision_ = Mh;
142 num_vars_ += 1; // h is now a real variable; all n+1 columns are variable columns
143 homogeneous_ = true;
144 BuildWorking();
145 }
146
148 size_t NumVariables() const { return num_vars_; }
149
153 Mat<complex_mp> const& Coefficients() const { return coefficients_highest_precision_; }
155 bool IsHomogenized() const { return homogeneous_; }
156
161 void Describe(std::ostream& out, size_t& row, VariableGroup const& vars, bool verbose) const
162 {
163 auto const& M = coefficients_highest_precision_;
164 const Eigen::Index n = M.rows();
165 if (n == 0)
166 return;
167 const Eigen::Index cap = static_cast<Eigen::Index>(describe_detail::kTerseRowCap);
168 const Eigen::Index shown = (verbose || n <= cap) ? n : cap;
169
170 // placeholder line per form: f_k = c.[x, y, 1]
171 for (Eigen::Index r = 0; r < shown; ++r)
172 {
173 out << " f_" << row++ << " = c.";
174 describe_detail::PrintAugmentedVars(out, vars, num_vars_, homogeneous_);
175 out << "\n";
176 }
177
178 // the actual coefficients below, as a named-expression-style legend (row r <-> f_k above)
179 const int sig = describe_detail::CoeffSig(verbose);
180 out << " c =\n";
181 for (Eigen::Index r = 0; r < shown; ++r)
182 {
183 out << " [ ";
184 for (Eigen::Index c = 0; c < M.cols(); ++c)
185 {
186 if (c) out << ", ";
187 describe_detail::PrintCoeff(out, M(r, c), sig);
188 }
189 out << " ]\n";
190 }
191
192 if (shown < n)
193 {
194 row += static_cast<size_t>(n - shown); // keep the global row index correct
195 out << " ... (" << (n - shown) << " more form" << (n - shown == 1 ? "" : "s")
196 << "; describe(verbose=True) for all)\n";
197 }
198 }
199
201 bool DependsOnPathVariable() const { return false; }
202
204 bool HasConstantJacobian() const { return true; }
205
207 void Differentiate() const {}
208
210 unsigned Precision() const { return precision_; }
211
213 void Precision(unsigned new_precision) const
214 {
215 // short-circuit when already materialized here. Each holder of mp values keeps its
216 // own "materialized at" tag; System deliberately keeps none and simply fans out on
217 // every evaluation, which is cheap precisely because of this early return (ADR-0057).
218 if (precision_==new_precision)
219 return;
220 if (new_precision > DoublePrecision())
221 {
222 auto& wm = std::get<Mat<complex_mp>>(coefficients_working_);
223 for (Eigen::Index r = 0; r < wm.rows(); ++r)
224 for (Eigen::Index c = 0; c < wm.cols(); ++c)
225 {
226 wm(r, c).precision(new_precision);
227 if (new_precision > precision_)
228 wm(r, c) = coefficients_highest_precision_(r, c);
229 }
230 }
231 precision_ = new_precision;
232 }
233
242 template <typename T>
243 void EvalInPlace(Eigen::Ref<Vec<T>> result, Vec<T> const& vars, T const& /*path_value*/) const
244 {
245 SyncPrecision(vars);
246 // affine: f(x) = W * [x ; 1] (the trailing 1 carries each row's constant in the last
247 // column). homogeneous (post-Homogenize): every column is a variable column, so it is
248 // just W * vars (the old constant is now the homogenizing variable's coefficient).
249 if (homogeneous_)
250 result.noalias() = Working<T>() * vars;
251 else
252 result.noalias() = Working<T>() * Augment<T>(vars);
253 }
254
266 template <typename T>
267 void JacobianInPlace(Eigen::Ref<Mat<T>> J, Vec<T> const& vars, T const& /*path_value*/) const
268 {
269 SyncPrecision(vars);
270 // homogeneous: every column is d f / d x. affine: drop the trailing constant column.
271 if (homogeneous_)
272 J = Working<T>();
273 else
274 J = Working<T>().leftCols(static_cast<Eigen::Index>(num_vars_));
275 }
276
281 template <typename T>
282 void TimeDerivInPlace(Eigen::Ref<Vec<T>> result, Vec<T> const& /*vars*/, T const& /*path_value*/) const
283 {
284 result.setZero();
285 }
286
287private:
293 template <typename T>
294 void SyncPrecision(Vec<T> const& vars) const
295 {
296 if constexpr (!std::is_same<T, complex_dbl>::value)
297 {
298 if (vars.size() > 0)
299 {
300 const unsigned p = bertini::Precision(vars(0));
301 if (p != precision_)
302 Precision(p);
303 }
304 }
305 }
306
307 template <typename T>
308 const Mat<T>& Working() const
309 {
310 return std::get<Mat<T>>(coefficients_working_);
311 }
312
313 template <typename T>
314 Vec<T> Augment(Vec<T> const& vars) const
315 {
316 Vec<T> aug(static_cast<Eigen::Index>(num_vars_ + 1));
317 aug.head(static_cast<Eigen::Index>(num_vars_)) = vars;
318 T one(1);
319 if constexpr (!std::is_same<T, complex_dbl>::value)
320 one.precision(precision_);
321 aug(static_cast<Eigen::Index>(num_vars_)) = one;
322 return aug;
323 }
324
325 void BuildWorking() const
326 {
327 const auto& M = coefficients_highest_precision_;
328 auto& wd = std::get<Mat<complex_dbl>>(coefficients_working_);
329 auto& wm = std::get<Mat<complex_mp>>(coefficients_working_);
330 wd.resize(M.rows(), M.cols());
331 wm.resize(M.rows(), M.cols());
332 for (Eigen::Index r = 0; r < M.rows(); ++r)
333 for (Eigen::Index c = 0; c < M.cols(); ++c)
334 {
335 wd(r, c) = complex_dbl(M(r, c));
336 wm(r, c) = M(r, c);
337 }
338 }
339
340 size_t num_vars_;
341 bool homogeneous_ = false;
342 Mat<complex_mp> coefficients_highest_precision_;
343 mutable std::tuple<Mat<complex_dbl>, Mat<complex_mp>> coefficients_working_;
344 mutable unsigned precision_;
345
347
348 template <typename Archive>
349 void serialize(Archive& ar, const unsigned /*version*/)
350 {
351 ar & num_vars_;
352 ar & homogeneous_;
353 ar & precision_;
354 ar & coefficients_highest_precision_;
355 ar & std::get<0>(coefficients_working_);
356 ar & std::get<1>(coefficients_working_);
357 }
358};
359
360} // namespace blocks
361} // namespace bertini
void Precision(unsigned new_precision) const
Set the working precision; recasts the mpfr working coefficients from the master.
Definition linear_forms_block.hpp:213
size_t NumFunctions() const
Number of functions (rows the block contributes to the system).
Definition linear_forms_block.hpp:112
std::vector< int > Degrees() const
Linear forms are degree 1.
Definition linear_forms_block.hpp:115
bool DependsOnPathVariable() const
Linear forms do not depend on the path variable.
Definition linear_forms_block.hpp:201
void EvalInPlace(Eigen::Ref< Vec< T > > result, Vec< T > const &vars, T const &) const
Evaluate the block's function values into a caller-provided segment.
Definition linear_forms_block.hpp:243
unsigned Precision() const
Get the block's current working precision.
Definition linear_forms_block.hpp:210
void Differentiate() const
Analytic block: nothing symbolic to differentiate.
Definition linear_forms_block.hpp:207
void TimeDerivInPlace(Eigen::Ref< Vec< T > > result, Vec< T > const &, T const &) const
Time-derivative into a caller-provided segment.
Definition linear_forms_block.hpp:282
void JacobianInPlace(Eigen::Ref< Mat< T > > J, Vec< T > const &vars, T const &) const
Evaluate the block's Jacobian (d f_i / d x_j) into a caller-provided block.
Definition linear_forms_block.hpp:267
std::vector< int > Degrees(VariableGroup const &) const
Per-function degrees with respect to a given variable group (all 1).
Definition linear_forms_block.hpp:117
size_t NumVariables() const
Number of variables the block expects in the input vector.
Definition linear_forms_block.hpp:148
bool IsHomogeneous(VariableGroup const &) const
An affine form a.x + b is inhomogeneous (it carries a constant); once homogenized the constant has be...
Definition linear_forms_block.hpp:123
LinearFormsBlock(size_t num_vars, Mat< complex_mp > coefficients, bool homogenized)
Construct a block in a stated homogenization state – for a loader restoring an archived block as it w...
Definition linear_forms_block.hpp:98
bool IsPolynomial(VariableGroup const &) const
Linear forms are polynomial.
Definition linear_forms_block.hpp:120
bool HasConstantJacobian() const
The Jacobian is the (constant) coefficient matrix, independent of x and t.
Definition linear_forms_block.hpp:204
void Describe(std::ostream &out, size_t &row, VariableGroup const &vars, bool verbose) const
Human-facing description: each form prints as the placeholder 'f_k = c.
Definition linear_forms_block.hpp:161
bool IsHomogenized() const
Whether Homogenize has folded the constant column onto a homogenizing variable.
Definition linear_forms_block.hpp:155
friend class boost::serialization::access
Definition linear_forms_block.hpp:346
LinearFormsBlock()
Definition linear_forms_block.hpp:70
void Homogenize(VariableGroup const &, std::shared_ptr< node::Variable > const &)
Homogenize: fold the constant column onto the homogenizing variable.
Definition linear_forms_block.hpp:131
LinearFormsBlock(size_t num_vars, Mat< complex_mp > coefficients)
Definition linear_forms_block.hpp:78
Mat< complex_mp > const & Coefficients() const
The master coefficient matrix (one row per form).
Definition linear_forms_block.hpp:153
Small shared helpers for the per-block Describe methods – the human-facing printing of a block-compos...
Bertini extensions of the Eigen linear algebra library.
void PrintCoeff(std::ostream &out, complex_mp const &c, int sig=0)
Print one (complex_mp) coefficient compactly: a real prints as its real part, a pure imaginary as b*i...
Definition describe.hpp:56
void PrintAugmentedVars(std::ostream &out, VariableGroup const &vars, size_t num_vars, bool homogeneous)
The augmenting variable list [x, y, 1] (affine) or [h, x, y] (homogeneous: no trailing 1).
Definition describe.hpp:79
int CoeffSig(bool verbose)
The significant-digit count for coefficient printing: 4 for terse (short and legible),...
Definition describe.hpp:68
constexpr size_t kTerseRowCap
How many function rows a block lists in terse (non-verbose) describe before truncating with a ....
Definition describe.hpp:43
Definition blend_block.hpp:75
Definition algorithm_builder.hpp:36
constexpr unsigned DoublePrecision()
The number of digits considered "double precision" (16).
Definition num_traits.hpp:152
std::complex< double > complex_dbl
The double-precision complex number type.
Definition double_extensions.hpp:47
Eigen::Matrix< NumT, Eigen::Dynamic, 1 > Vec
A dynamically-sized column vector of NumT.
Definition eigen_extensions.hpp:296
std::vector< std::shared_ptr< node::Variable > > VariableGroup
An ordered group of variables (the unit of homogenization / variable grouping).
Definition node.hpp:69
Eigen::Matrix< NumT, Eigen::Dynamic, Eigen::Dynamic > Mat
A dynamically-sized matrix of NumT.
Definition eigen_extensions.hpp:298
auto DefaultPrecision()
Get the global default precision (digits) for multiprecision numbers.
Definition mpfr_complex.hpp:135
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.