Bertini 2 d882924 (2026-10-06 19:56 UTC)
Numerical algebraic geometry library (C++ core)
Loading...
Searching...
No Matches
products_of_linears_block.hpp
Go to the documentation of this file.
1//This file is part of Bertini 2.
2//
3//products_of_linears_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//products_of_linears_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 products_of_linears_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
42
43#pragma once
44
45#include <vector>
46#include <tuple>
47
48#include <boost/serialization/vector.hpp>
49
53
54namespace bertini {
55namespace blocks {
56
64{
65public:
66 ProductsOfLinearsBlock() : num_vars_(0), precision_(DefaultPrecision()) {}
67
74 ProductsOfLinearsBlock(size_t num_vars, std::vector<Mat<complex_mp>> factors)
75 : num_vars_(num_vars), factors_highest_precision_(std::move(factors)), precision_(DefaultPrecision())
76 {
77#ifndef NDEBUG
78 for (auto const& M : factors_highest_precision_)
79 assert(static_cast<size_t>(M.cols()) == num_vars_ + 1 &&
80 "every products-of-linears coefficient matrix must have num_vars+1 columns");
81#endif
82 BuildWorking();
83 }
84
86 size_t NumFunctions() const { return factors_highest_precision_.size(); }
87
89 std::vector<int> Degrees() const
90 {
91 std::vector<int> d; d.reserve(factors_highest_precision_.size());
92 for (auto const& M : factors_highest_precision_) d.push_back(static_cast<int>(M.rows()));
93 return d;
94 }
95
96 std::vector<int> Degrees(VariableGroup const&) const { return Degrees(); }
97
102 bool IsHomogenized() const
103 {
104 return !factors_highest_precision_.empty()
105 && static_cast<size_t>(factors_highest_precision_.front().cols()) == num_vars_;
106 }
107
117 void Homogenize(VariableGroup const& /*group*/, std::shared_ptr<node::Variable> const& /*hom_var*/)
118 {
119 if (IsHomogenized())
120 throw std::runtime_error("ProductsOfLinearsBlock::Homogenize: block is already homogenized "
121 "(multiple affine variable groups are not yet supported for products-of-linears blocks)");
122 const Eigen::Index n = static_cast<Eigen::Index>(num_vars_);
123 for (auto& M : factors_highest_precision_)
124 {
125 Mat<complex_mp> Mh(M.rows(), M.cols()); // same column count, n+1: now all variable columns
126 Mh.col(0) = M.col(n); // constant -> the homogenizing variable, in front
127 Mh.rightCols(n) = M.leftCols(n); // the original variable columns
128 M = Mh;
129 }
130 num_vars_ += 1;
131 BuildWorking();
132 }
133
135 bool IsHomogeneous(VariableGroup const&) const
136 {
137 if (IsHomogenized())
138 return true;
139 const Eigen::Index n = static_cast<Eigen::Index>(num_vars_);
140 for (auto const& M : factors_highest_precision_)
141 for (Eigen::Index r = 0; r < M.rows(); ++r)
142 if (M(r, n).real() != 0 || M(r, n).imag() != 0)
143 return false;
144 return true;
145 }
146
147 bool IsPolynomial(VariableGroup const&) const { return true; }
148
150 size_t NumVariables() const { return num_vars_; }
151
155 std::vector<Mat<complex_mp>> const& Factors() const { return factors_highest_precision_; }
156
159 void Describe(std::ostream& out, size_t& row, VariableGroup const& vars, bool verbose) const
160 {
161 for (auto const& M : factors_highest_precision_)
162 {
163 out << " f_" << row++ << " = ";
164 const Eigen::Index k = M.rows();
165 if (!verbose)
166 {
167 out << "prod of " << k << " linear form" << (k == 1 ? "" : "s");
168 }
169 else if (k == 0)
170 {
171 out << "1";
172 }
173 else
174 {
175 for (Eigen::Index r = 0; r < k; ++r)
176 {
177 out << (r ? " * " : "") << "(";
178 describe_detail::PrintLinearFormVerbose(out, M, r, vars, num_vars_, IsHomogenized());
179 out << ")";
180 }
181 }
182 out << "\n";
183 }
184 }
185
187 bool DependsOnPathVariable() const { return false; }
188
190 bool HasConstantJacobian() const { return false; }
191
193 void Differentiate() const {}
194
196 unsigned Precision() const { return precision_; }
197
199 void Precision(unsigned new_precision) const
200 {
201 // short-circuit when already materialized here. Each holder of mp values keeps its
202 // own "materialized at" tag; System deliberately keeps none and simply fans out on
203 // every evaluation, which is cheap precisely because of this early return (ADR-0057).
204 if (precision_==new_precision)
205 return;
206 if (new_precision > DoublePrecision())
207 {
208 auto& wm = std::get<std::vector<Mat<complex_mp>>>(factors_working_);
209 for (size_t i = 0; i < wm.size(); ++i)
210 for (Eigen::Index r = 0; r < wm[i].rows(); ++r)
211 for (Eigen::Index c = 0; c < wm[i].cols(); ++c)
212 {
213 wm[i](r, c).precision(new_precision);
214 if (new_precision > precision_)
215 wm[i](r, c) = factors_highest_precision_[i](r, c);
216 }
217 }
218 precision_ = new_precision;
219 }
220
229 template <typename T>
230 void EvalInPlace(Eigen::Ref<Vec<T>> result, Vec<T> const& vars, T const& /*path_value*/) const
231 {
232 SyncPrecision(vars);
233 const auto& W = Working<T>();
234 // aug = [vars ; 1]. The trailing 1 lets each coefficient row carry its constant
235 // term in its last column, so a linear factor is just the dot product (row . aug).
236 const Vec<T> aug = Augment<T>(vars);
237
238 // Function i is a product of linear factors. W[i] is its k x (n+1) coefficient
239 // matrix, one row per factor, so W[i] * aug evaluates all k factors at once and the
240 // function value is their product.
241 for (size_t i = 0; i < W.size(); ++i)
242 {
243 const Vec<T> fvals = W[i] * aug; // the k factor values L_0 .. L_{k-1}
244 T val(1);
245 for (Eigen::Index r = 0; r < fvals.size(); ++r)
246 val *= fvals(r); // f_i = prod_r L_r
247 result(static_cast<Eigen::Index>(i)) = val;
248 }
249 }
250
259 template <typename T>
260 void JacobianInPlace(Eigen::Ref<Mat<T>> J, Vec<T> const& vars, T const& /*path_value*/) const
261 {
262 SyncPrecision(vars);
263 const auto& W = Working<T>();
264 const Vec<T> aug = Augment<T>(vars); // [vars ; 1]; see EvalInPlace
265
266 // Function i is f_i = prod_r L_r, where L_r = (row r of M) . aug is the r-th linear
267 // factor's value. By the product rule, the partial derivative w.r.t. variable c is
268 //
269 // d f_i / d x_c = sum_r (d L_r / d x_c) * prod_{s != r} L_s
270 // = sum_r M(r,c) * weight_r,
271 //
272 // since d L_r / d x_c is just the coefficient M(r,c) of x_c in factor r, and
273 // weight_r := prod_{s != r} L_s is the product of every factor value except r's.
274 for (size_t i = 0; i < W.size(); ++i)
275 {
276 const Mat<T>& M = W[i]; // k x (n+1): rows are factors, last col is constant
277 const Eigen::Index k = M.rows();
278
279 if (k == 0) // a 0-factor product is the constant 1; derivative 0
280 {
281 J.row(static_cast<Eigen::Index>(i)).setZero();
282 continue;
283 }
284
285 const Vec<T> fvals = M * aug; // the factor values L_0 .. L_{k-1}
286
287 // weight_r = prod_{s != r} L_s, computed in O(k) and WITHOUT division -- so it
288 // stays correct even when some factor value L_s is zero (dividing the total
289 // product by L_r would not). We split the "all but r" product into the factors
290 // before r and the factors after r, each built by a running accumulator:
291 // forward pass: weight_r <- prod_{s < r} L_s (the prefix product)
292 // backward pass: weight_r <- weight_r * prod_{s > r} L_s (times the suffix product)
293 Vec<T> weight(k);
294 {
295 T acc(1);
296 for (Eigen::Index r = 0; r < k; ++r) { weight(r) = acc; acc *= fvals(r); } // prefix
297 acc = T(1);
298 for (Eigen::Index r = k - 1; r >= 0; --r) { weight(r) *= acc; acc *= fvals(r); } // suffix
299 }
300
301 // Row i of the Jacobian is (sum_r M(r,c) * weight_r) over the variable columns
302 // c only; M.leftCols(num_vars_) drops the trailing constant column (not a variable).
303 J.row(static_cast<Eigen::Index>(i)) = weight.transpose() * M.leftCols(static_cast<Eigen::Index>(num_vars_));
304 }
305 }
306
311 template <typename T>
312 void TimeDerivInPlace(Eigen::Ref<Vec<T>> result, Vec<T> const& /*vars*/, T const& /*path_value*/) const
313 {
314 result.setZero();
315 }
316
317private:
323 template <typename T>
324 void SyncPrecision(Vec<T> const& vars) const
325 {
326 if constexpr (!std::is_same<T, complex_dbl>::value)
327 {
328 if (vars.size() > 0)
329 {
330 const unsigned p = bertini::Precision(vars(0));
331 if (p != precision_)
332 Precision(p);
333 }
334 }
335 }
336
337 template <typename T>
338 const std::vector<Mat<T>>& Working() const
339 {
340 return std::get<std::vector<Mat<T>>>(factors_working_);
341 }
342
343 template <typename T>
344 Vec<T> Augment(Vec<T> const& vars) const
345 {
346 if (IsHomogenized())
347 return vars; // every column is a variable column; nothing to append
348 Vec<T> aug(static_cast<Eigen::Index>(num_vars_ + 1));
349 aug.head(static_cast<Eigen::Index>(num_vars_)) = vars;
350 T one(1);
351 if constexpr (!std::is_same<T, complex_dbl>::value)
352 one.precision(precision_);
353 aug(static_cast<Eigen::Index>(num_vars_)) = one;
354 return aug;
355 }
356
357 void BuildWorking() const
358 {
359 auto& wd = std::get<std::vector<Mat<complex_dbl>>>(factors_working_);
360 auto& wm = std::get<std::vector<Mat<complex_mp>>>(factors_working_);
361 const size_t n = factors_highest_precision_.size();
362 wd.resize(n);
363 wm.resize(n);
364 for (size_t i = 0; i < n; ++i)
365 {
366 const auto& M = factors_highest_precision_[i];
367 wd[i].resize(M.rows(), M.cols());
368 wm[i].resize(M.rows(), M.cols());
369 for (Eigen::Index r = 0; r < M.rows(); ++r)
370 for (Eigen::Index c = 0; c < M.cols(); ++c)
371 {
372 wd[i](r, c) = complex_dbl(M(r, c));
373 wm[i](r, c) = M(r, c);
374 }
375 }
376 }
377
378 size_t num_vars_;
379 std::vector<Mat<complex_mp>> factors_highest_precision_;
380 mutable std::tuple<std::vector<Mat<complex_dbl>>, std::vector<Mat<complex_mp>>> factors_working_;
381 mutable unsigned precision_;
382
384
385 template <typename Archive>
386 void serialize(Archive& ar, const unsigned /*version*/)
387 {
388 ar & num_vars_;
389 ar & precision_;
390 ar & factors_highest_precision_;
391 ar & std::get<0>(factors_working_);
392 ar & std::get<1>(factors_working_);
393 }
394};
395
396} // namespace blocks
397} // namespace bertini
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 products_of_linears_block.hpp:230
bool DependsOnPathVariable() const
Products of linears do not depend on the path variable.
Definition products_of_linears_block.hpp:187
std::vector< Mat< complex_mp > > const & Factors() const
The master (highest-precision) coefficient matrices, one per function; matrix i is (number of factors...
Definition products_of_linears_block.hpp:155
ProductsOfLinearsBlock(size_t num_vars, std::vector< Mat< complex_mp > > factors)
Definition products_of_linears_block.hpp:74
std::vector< int > Degrees(VariableGroup const &) const
Per-function degrees with respect to a given variable group (same as the total degrees).
Definition products_of_linears_block.hpp:96
bool IsHomogeneous(VariableGroup const &) const
Homogeneous once Homogenize has run, or when every factor's constant column is zero (the form the m-h...
Definition products_of_linears_block.hpp:135
std::vector< int > Degrees() const
Each function is a product of its linear factors, so its degree is the factor count.
Definition products_of_linears_block.hpp:89
bool HasConstantJacobian() const
The Jacobian is not constant (it depends on x), unlike a slice.
Definition products_of_linears_block.hpp:190
bool IsHomogenized() const
Whether Homogenize has folded the constant column onto a homogenizing variable.
Definition products_of_linears_block.hpp:102
size_t NumFunctions() const
Number of functions (rows the block contributes to the system).
Definition products_of_linears_block.hpp:86
void Describe(std::ostream &out, size_t &row, VariableGroup const &vars, bool verbose) const
Human-facing description: terse shows prod of k linear forms; verbose shows the actual product of aff...
Definition products_of_linears_block.hpp:159
bool IsPolynomial(VariableGroup const &) const
Always true: a product of linear forms is a polynomial.
Definition products_of_linears_block.hpp:147
void Precision(unsigned new_precision) const
Set the working precision; recasts the mpfr working coefficients from the master.
Definition products_of_linears_block.hpp:199
friend class boost::serialization::access
Definition products_of_linears_block.hpp:383
void TimeDerivInPlace(Eigen::Ref< Vec< T > > result, Vec< T > const &, T const &) const
Time-derivative into a caller-provided segment.
Definition products_of_linears_block.hpp:312
size_t NumVariables() const
Number of variables the block expects in the input vector.
Definition products_of_linears_block.hpp:150
void Differentiate() const
Analytic block: nothing symbolic to differentiate.
Definition products_of_linears_block.hpp:193
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 products_of_linears_block.hpp:260
ProductsOfLinearsBlock()
Definition products_of_linears_block.hpp:66
unsigned Precision() const
Get the block's current working precision.
Definition products_of_linears_block.hpp:196
void Homogenize(VariableGroup const &, std::shared_ptr< node::Variable > const &)
Homogenize with respect to an affine variable group: fold each factor's constant column onto the homo...
Definition products_of_linears_block.hpp:117
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 PrintLinearFormVerbose(std::ostream &out, Mat< complex_mp > const &M, Eigen::Index r, VariableGroup const &vars, size_t num_vars, bool homogeneous)
One affine linear form, row r of an augmented coefficient matrix M (num_vars+1 cols affine,...
Definition describe.hpp:92
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.