$darkmode
Eigen  5.0.1-dev
FindCoeff.h
1 // This file is part of Eigen, a lightweight C++ template library
2 // for linear algebra.
3 //
4 // Copyright (C) 2025 Charlie Schlosser <cs.schlosser@gmail.com>
5 //
6 // This Source Code Form is subject to the terms of the Mozilla
7 // Public License v. 2.0. If a copy of the MPL was not distributed
8 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 
10 #ifndef EIGEN_FIND_COEFF_H
11 #define EIGEN_FIND_COEFF_H
12 
13 // IWYU pragma: private
14 #include "./InternalHeaderCheck.h"
15 
16 namespace Eigen {
17 
18 namespace internal {
19 
20 template <typename Scalar, int NaNPropagation, bool IsInteger = NumTraits<Scalar>::IsInteger>
21 struct max_coeff_functor {
22  EIGEN_DEVICE_FUNC inline bool compareCoeff(const Scalar& incumbent, const Scalar& candidate) const {
23  return candidate > incumbent;
24  }
25  template <typename Packet>
26  EIGEN_DEVICE_FUNC inline Packet comparePacket(const Packet& incumbent, const Packet& candidate) const {
27  return pcmp_lt(incumbent, candidate);
28  }
29  template <typename Packet>
30  EIGEN_DEVICE_FUNC inline Scalar predux(const Packet& a) const {
31  return predux_max(a);
32  }
33 };
34 
35 template <typename Scalar>
36 struct max_coeff_functor<Scalar, PropagateNaN, false> {
37  EIGEN_DEVICE_FUNC inline Scalar compareCoeff(const Scalar& incumbent, const Scalar& candidate) {
38  return (candidate > incumbent) || ((candidate != candidate) && (incumbent == incumbent));
39  }
40  template <typename Packet>
41  EIGEN_DEVICE_FUNC inline Packet comparePacket(const Packet& incumbent, const Packet& candidate) {
42  return pandnot(pcmp_lt_or_nan(incumbent, candidate), pisnan(incumbent));
43  }
44  template <typename Packet>
45  EIGEN_DEVICE_FUNC inline Scalar predux(const Packet& a) const {
46  return predux_max<PropagateNaN>(a);
47  }
48 };
49 
50 template <typename Scalar>
51 struct max_coeff_functor<Scalar, PropagateNumbers, false> {
52  EIGEN_DEVICE_FUNC inline bool compareCoeff(const Scalar& incumbent, const Scalar& candidate) const {
53  return (candidate > incumbent) || ((candidate == candidate) && (incumbent != incumbent));
54  }
55  template <typename Packet>
56  EIGEN_DEVICE_FUNC inline Packet comparePacket(const Packet& incumbent, const Packet& candidate) const {
57  return pandnot(pcmp_lt_or_nan(incumbent, candidate), pisnan(candidate));
58  }
59  template <typename Packet>
60  EIGEN_DEVICE_FUNC inline Scalar predux(const Packet& a) const {
61  return predux_max<PropagateNumbers>(a);
62  }
63 };
64 
65 template <typename Scalar, int NaNPropagation, bool IsInteger = NumTraits<Scalar>::IsInteger>
66 struct min_coeff_functor {
67  EIGEN_DEVICE_FUNC inline bool compareCoeff(const Scalar& incumbent, const Scalar& candidate) const {
68  return candidate < incumbent;
69  }
70  template <typename Packet>
71  EIGEN_DEVICE_FUNC inline Packet comparePacket(const Packet& incumbent, const Packet& candidate) const {
72  return pcmp_lt(candidate, incumbent);
73  }
74  template <typename Packet>
75  EIGEN_DEVICE_FUNC inline Scalar predux(const Packet& a) const {
76  return predux_min(a);
77  }
78 };
79 
80 template <typename Scalar>
81 struct min_coeff_functor<Scalar, PropagateNaN, false> {
82  EIGEN_DEVICE_FUNC inline Scalar compareCoeff(const Scalar& incumbent, const Scalar& candidate) {
83  return (candidate < incumbent) || ((candidate != candidate) && (incumbent == incumbent));
84  }
85  template <typename Packet>
86  EIGEN_DEVICE_FUNC inline Packet comparePacket(const Packet& incumbent, const Packet& candidate) {
87  return pandnot(pcmp_lt_or_nan(candidate, incumbent), pisnan(incumbent));
88  }
89  template <typename Packet>
90  EIGEN_DEVICE_FUNC inline Scalar predux(const Packet& a) const {
91  return predux_min<PropagateNaN>(a);
92  }
93 };
94 
95 template <typename Scalar>
96 struct min_coeff_functor<Scalar, PropagateNumbers, false> {
97  EIGEN_DEVICE_FUNC inline bool compareCoeff(const Scalar& incumbent, const Scalar& candidate) const {
98  return (candidate < incumbent) || ((candidate == candidate) && (incumbent != incumbent));
99  }
100  template <typename Packet>
101  EIGEN_DEVICE_FUNC inline Packet comparePacket(const Packet& incumbent, const Packet& candidate) const {
102  return pandnot(pcmp_lt_or_nan(candidate, incumbent), pisnan(candidate));
103  }
104  template <typename Packet>
105  EIGEN_DEVICE_FUNC inline Scalar predux(const Packet& a) const {
106  return predux_min<PropagateNumbers>(a);
107  }
108 };
109 
110 template <typename Scalar>
111 struct min_max_traits {
112  static constexpr bool PacketAccess = packet_traits<Scalar>::Vectorizable;
113 };
114 template <typename Scalar, int NaNPropagation>
115 struct functor_traits<max_coeff_functor<Scalar, NaNPropagation>> : min_max_traits<Scalar> {};
116 template <typename Scalar, int NaNPropagation>
117 struct functor_traits<min_coeff_functor<Scalar, NaNPropagation>> : min_max_traits<Scalar> {};
118 
119 template <typename Evaluator, typename Func, bool Linear, bool Vectorize>
120 struct find_coeff_loop;
121 template <typename Evaluator, typename Func>
122 struct find_coeff_loop<Evaluator, Func, /*Linear*/ false, /*Vectorize*/ false> {
123  using Scalar = typename Evaluator::Scalar;
124  static EIGEN_DEVICE_FUNC inline void run(const Evaluator& eval, Func& func, Scalar& res, Index& outer, Index& inner) {
125  Index outerSize = eval.outerSize();
126  Index innerSize = eval.innerSize();
127 
128  /* initialization performed in calling function */
129  /* result = eval.coeff(0, 0); */
130  /* outer = 0; */
131  /* inner = 0; */
132 
133  for (Index j = 0; j < outerSize; j++) {
134  for (Index i = 0; i < innerSize; i++) {
135  Scalar xprCoeff = eval.coeffByOuterInner(j, i);
136  bool newRes = func.compareCoeff(res, xprCoeff);
137  if (newRes) {
138  outer = j;
139  inner = i;
140  res = xprCoeff;
141  }
142  }
143  }
144  }
145 };
146 template <typename Evaluator, typename Func>
147 struct find_coeff_loop<Evaluator, Func, /*Linear*/ true, /*Vectorize*/ false> {
148  using Scalar = typename Evaluator::Scalar;
149  static EIGEN_DEVICE_FUNC inline void run(const Evaluator& eval, Func& func, Scalar& res, Index& index) {
150  Index size = eval.size();
151 
152  /* initialization performed in calling function */
153  /* result = eval.coeff(0); */
154  /* index = 0; */
155 
156  for (Index k = 0; k < size; k++) {
157  Scalar xprCoeff = eval.coeff(k);
158  bool newRes = func.compareCoeff(res, xprCoeff);
159  if (newRes) {
160  index = k;
161  res = xprCoeff;
162  }
163  }
164  }
165 };
166 template <typename Evaluator, typename Func>
167 struct find_coeff_loop<Evaluator, Func, /*Linear*/ false, /*Vectorize*/ true> {
168  using ScalarImpl = find_coeff_loop<Evaluator, Func, false, false>;
169  using Scalar = typename Evaluator::Scalar;
170  using Packet = typename Evaluator::Packet;
171  static constexpr int PacketSize = unpacket_traits<Packet>::size;
172  static EIGEN_DEVICE_FUNC inline void run(const Evaluator& eval, Func& func, Scalar& result, Index& outer,
173  Index& inner) {
174  Index outerSize = eval.outerSize();
175  Index innerSize = eval.innerSize();
176  Index packetEnd = numext::round_down(innerSize, PacketSize);
177 
178  /* initialization performed in calling function */
179  /* result = eval.coeff(0, 0); */
180  /* outer = 0; */
181  /* inner = 0; */
182 
183  bool checkPacket = false;
184 
185  for (Index j = 0; j < outerSize; j++) {
186  Packet resultPacket = pset1<Packet>(result);
187  for (Index i = 0; i < packetEnd; i += PacketSize) {
188  Packet xprPacket = eval.template packetByOuterInner<Unaligned, Packet>(j, i);
189  if (predux_any(func.comparePacket(resultPacket, xprPacket))) {
190  outer = j;
191  inner = i;
192  result = func.predux(xprPacket);
193  resultPacket = pset1<Packet>(result);
194  checkPacket = true;
195  }
196  }
197 
198  for (Index i = packetEnd; i < innerSize; i++) {
199  Scalar xprCoeff = eval.coeffByOuterInner(j, i);
200  if (func.compareCoeff(result, xprCoeff)) {
201  outer = j;
202  inner = i;
203  result = xprCoeff;
204  checkPacket = false;
205  }
206  }
207  }
208 
209  if (checkPacket) {
210  result = eval.coeffByOuterInner(outer, inner);
211  Index i_end = inner + PacketSize;
212  for (Index i = inner; i < i_end; i++) {
213  Scalar xprCoeff = eval.coeffByOuterInner(outer, i);
214  if (func.compareCoeff(result, xprCoeff)) {
215  inner = i;
216  result = xprCoeff;
217  }
218  }
219  }
220  }
221 };
222 template <typename Evaluator, typename Func>
223 struct find_coeff_loop<Evaluator, Func, /*Linear*/ true, /*Vectorize*/ true> {
224  using ScalarImpl = find_coeff_loop<Evaluator, Func, true, false>;
225  using Scalar = typename Evaluator::Scalar;
226  using Packet = typename Evaluator::Packet;
227  static constexpr int PacketSize = unpacket_traits<Packet>::size;
228  static constexpr int Alignment = Evaluator::Alignment;
229 
230  static EIGEN_DEVICE_FUNC inline void run(const Evaluator& eval, Func& func, Scalar& result, Index& index) {
231  Index size = eval.size();
232  Index packetEnd = numext::round_down(size, PacketSize);
233 
234  /* initialization performed in calling function */
235  /* result = eval.coeff(0); */
236  /* index = 0; */
237 
238  Packet resultPacket = pset1<Packet>(result);
239  bool checkPacket = false;
240 
241  for (Index k = 0; k < packetEnd; k += PacketSize) {
242  Packet xprPacket = eval.template packet<Alignment, Packet>(k);
243  if (predux_any(func.comparePacket(resultPacket, xprPacket))) {
244  index = k;
245  result = func.predux(xprPacket);
246  resultPacket = pset1<Packet>(result);
247  checkPacket = true;
248  }
249  }
250 
251  for (Index k = packetEnd; k < size; k++) {
252  Scalar xprCoeff = eval.coeff(k);
253  if (func.compareCoeff(result, xprCoeff)) {
254  index = k;
255  result = xprCoeff;
256  checkPacket = false;
257  }
258  }
259 
260  if (checkPacket) {
261  result = eval.coeff(index);
262  Index k_end = index + PacketSize;
263  for (Index k = index; k < k_end; k++) {
264  Scalar xprCoeff = eval.coeff(k);
265  if (func.compareCoeff(result, xprCoeff)) {
266  index = k;
267  result = xprCoeff;
268  }
269  }
270  }
271  }
272 };
273 
274 template <typename Derived>
275 struct find_coeff_evaluator : public evaluator<Derived> {
276  using Base = evaluator<Derived>;
277  using Scalar = typename Derived::Scalar;
278  using Packet = typename packet_traits<Scalar>::type;
279  static constexpr int Flags = Base::Flags;
280  static constexpr bool IsRowMajor = bool(Flags & RowMajorBit);
281  EIGEN_DEVICE_FUNC inline find_coeff_evaluator(const Derived& xpr) : Base(xpr), m_xpr(xpr) {}
282 
283  EIGEN_DEVICE_FUNC inline Scalar coeffByOuterInner(Index outer, Index inner) const {
284  Index row = IsRowMajor ? outer : inner;
285  Index col = IsRowMajor ? inner : outer;
286  return Base::coeff(row, col);
287  }
288  template <int LoadMode, typename PacketType>
289  EIGEN_DEVICE_FUNC inline PacketType packetByOuterInner(Index outer, Index inner) const {
290  Index row = IsRowMajor ? outer : inner;
291  Index col = IsRowMajor ? inner : outer;
292  return Base::template packet<LoadMode, PacketType>(row, col);
293  }
294 
295  EIGEN_DEVICE_FUNC inline Index innerSize() const { return m_xpr.innerSize(); }
296  EIGEN_DEVICE_FUNC inline Index outerSize() const { return m_xpr.outerSize(); }
297  EIGEN_DEVICE_FUNC inline Index size() const { return m_xpr.size(); }
298 
299  const Derived& m_xpr;
300 };
301 
302 template <typename Derived, typename Func>
303 struct find_coeff_impl {
304  using Evaluator = find_coeff_evaluator<Derived>;
305  static constexpr int Flags = Evaluator::Flags;
306  static constexpr int Alignment = Evaluator::Alignment;
307  static constexpr bool IsRowMajor = Derived::IsRowMajor;
308  static constexpr int MaxInnerSizeAtCompileTime =
309  IsRowMajor ? Derived::MaxColsAtCompileTime : Derived::MaxRowsAtCompileTime;
310  static constexpr int MaxSizeAtCompileTime = Derived::MaxSizeAtCompileTime;
311 
312  using Scalar = typename Derived::Scalar;
313  using Packet = typename Evaluator::Packet;
314 
315  static constexpr int PacketSize = unpacket_traits<Packet>::size;
316  static constexpr bool Linearize = bool(Flags & LinearAccessBit);
317  static constexpr bool DontVectorize =
318  enum_lt_not_dynamic(Linearize ? MaxSizeAtCompileTime : MaxInnerSizeAtCompileTime, PacketSize);
319  static constexpr bool Vectorize =
320  !DontVectorize && bool(Flags & PacketAccessBit) && functor_traits<Func>::PacketAccess;
321 
322  using Loop = find_coeff_loop<Evaluator, Func, Linearize, Vectorize>;
323 
324  template <bool ForwardLinearAccess = Linearize, std::enable_if_t<!ForwardLinearAccess, bool> = true>
325  static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run(const Derived& xpr, Func& func, Scalar& res, Index& outer,
326  Index& inner) {
327  Evaluator eval(xpr);
328  Loop::run(eval, func, res, outer, inner);
329  }
330  template <bool ForwardLinearAccess = Linearize, std::enable_if_t<ForwardLinearAccess, bool> = true>
331  static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run(const Derived& xpr, Func& func, Scalar& res, Index& outer,
332  Index& inner) {
333  // where possible, use the linear loop and back-calculate the outer and inner indices
334  Index index = 0;
335  run(xpr, func, res, index);
336  outer = index / xpr.innerSize();
337  inner = index % xpr.innerSize();
338  }
339  static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run(const Derived& xpr, Func& func, Scalar& res, Index& index) {
340  Evaluator eval(xpr);
341  Loop::run(eval, func, res, index);
342  }
343 };
344 
345 template <typename Derived, typename IndexType, typename Func>
346 EIGEN_DEVICE_FUNC typename internal::traits<Derived>::Scalar findCoeff(const DenseBase<Derived>& mat, Func& func,
347  IndexType* rowPtr, IndexType* colPtr) {
348  eigen_assert(mat.rows() > 0 && mat.cols() > 0 && "you are using an empty matrix");
349  using Scalar = typename DenseBase<Derived>::Scalar;
350  using FindCoeffImpl = internal::find_coeff_impl<Derived, Func>;
351  Index outer = 0;
352  Index inner = 0;
353  Scalar res = mat.coeff(0, 0);
354  FindCoeffImpl::run(mat.derived(), func, res, outer, inner);
355  *rowPtr = internal::convert_index<IndexType>(Derived::IsRowMajor ? outer : inner);
356  if (colPtr) *colPtr = internal::convert_index<IndexType>(Derived::IsRowMajor ? inner : outer);
357  return res;
358 }
359 
360 template <typename Derived, typename IndexType, typename Func>
361 EIGEN_DEVICE_FUNC typename internal::traits<Derived>::Scalar findCoeff(const DenseBase<Derived>& mat, Func& func,
362  IndexType* indexPtr) {
363  eigen_assert(mat.size() > 0 && "you are using an empty matrix");
364  EIGEN_STATIC_ASSERT_VECTOR_ONLY(Derived)
365  using Scalar = typename DenseBase<Derived>::Scalar;
366  using FindCoeffImpl = internal::find_coeff_impl<Derived, Func>;
367  Index index = 0;
368  Scalar res = mat.coeff(0);
369  FindCoeffImpl::run(mat.derived(), func, res, index);
370  *indexPtr = internal::convert_index<IndexType>(index);
371  return res;
372 }
373 
374 } // namespace internal
375 
389 template <typename Derived>
390 template <int NaNPropagation, typename IndexType>
391 EIGEN_DEVICE_FUNC typename internal::traits<Derived>::Scalar DenseBase<Derived>::minCoeff(IndexType* rowPtr,
392  IndexType* colPtr) const {
393  using Func = internal::min_coeff_functor<Scalar, NaNPropagation>;
394  Func func;
395  return internal::findCoeff(derived(), func, rowPtr, colPtr);
396 }
397 
411 template <typename Derived>
412 template <int NaNPropagation, typename IndexType>
413 EIGEN_DEVICE_FUNC typename internal::traits<Derived>::Scalar DenseBase<Derived>::minCoeff(IndexType* indexPtr) const {
414  using Func = internal::min_coeff_functor<Scalar, NaNPropagation>;
415  Func func;
416  return internal::findCoeff(derived(), func, indexPtr);
417 }
418 
432 template <typename Derived>
433 template <int NaNPropagation, typename IndexType>
434 EIGEN_DEVICE_FUNC typename internal::traits<Derived>::Scalar DenseBase<Derived>::maxCoeff(IndexType* rowPtr,
435  IndexType* colPtr) const {
436  using Func = internal::max_coeff_functor<Scalar, NaNPropagation>;
437  Func func;
438  return internal::findCoeff(derived(), func, rowPtr, colPtr);
439 }
440 
454 template <typename Derived>
455 template <int NaNPropagation, typename IndexType>
456 EIGEN_DEVICE_FUNC typename internal::traits<Derived>::Scalar DenseBase<Derived>::maxCoeff(IndexType* indexPtr) const {
457  using Func = internal::max_coeff_functor<Scalar, NaNPropagation>;
458  Func func;
459  return internal::findCoeff(derived(), func, indexPtr);
460 }
461 
462 } // namespace Eigen
463 
464 #endif // EIGEN_FIND_COEFF_H
internal::traits< Derived >::Scalar Scalar
Definition: DenseBase.h:62
Definition: Constants.h:344
Namespace containing all symbols from the Eigen library.
Definition: B01_Experimental.dox:1
const unsigned int RowMajorBit
Definition: Constants.h:70
const unsigned int PacketAccessBit
Definition: Constants.h:97
EIGEN_DEFAULT_DENSE_INDEX_TYPE Index
The Index type as used for the API.
Definition: Meta.h:82
internal::traits< Derived >::Scalar minCoeff() const
Definition: Redux.h:464
Definition: Constants.h:342
internal::traits< Derived >::Scalar maxCoeff() const
Definition: Redux.h:477
const unsigned int LinearAccessBit
Definition: Constants.h:133