Eigen  5.0.1
 
Loading...
Searching...
No Matches
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// SPDX-License-Identifier: MPL-2.0
10
11#ifndef EIGEN_FIND_COEFF_H
12#define EIGEN_FIND_COEFF_H
13
14// IWYU pragma: private
15#include "./InternalHeaderCheck.h"
16
17namespace Eigen {
18
19namespace internal {
20
21template <typename Scalar, int NaNPropagation, bool IsInteger = NumTraits<Scalar>::IsInteger>
22struct max_coeff_functor {
23 EIGEN_DEVICE_FUNC inline bool compareCoeff(const Scalar& incumbent, const Scalar& candidate) const {
24 return candidate > incumbent;
25 }
26 template <typename Packet>
27 EIGEN_DEVICE_FUNC inline Packet comparePacket(const Packet& incumbent, const Packet& candidate) const {
28 return pcmp_lt(incumbent, candidate);
29 }
30 template <typename Packet>
31 EIGEN_DEVICE_FUNC inline Scalar predux(const Packet& a) const {
32 return predux_max(a);
33 }
34};
35
36template <typename Scalar>
37struct max_coeff_functor<Scalar, PropagateNaN, false> {
38 EIGEN_DEVICE_FUNC inline bool compareCoeff(const Scalar& incumbent, const Scalar& candidate) const {
39 return (candidate > incumbent) || ((candidate != candidate) && (incumbent == incumbent));
40 }
41 template <typename Packet>
42 EIGEN_DEVICE_FUNC inline Packet comparePacket(const Packet& incumbent, const Packet& candidate) const {
43 return pandnot(pcmp_lt_or_nan(incumbent, candidate), pisnan(incumbent));
44 }
45 template <typename Packet>
46 EIGEN_DEVICE_FUNC inline Scalar predux(const Packet& a) const {
47 return predux_max<PropagateNaN>(a);
48 }
49};
50
51template <typename Scalar>
52struct max_coeff_functor<Scalar, PropagateNumbers, false> {
53 EIGEN_DEVICE_FUNC inline bool compareCoeff(const Scalar& incumbent, const Scalar& candidate) const {
54 return (candidate > incumbent) || ((candidate == candidate) && (incumbent != incumbent));
55 }
56 template <typename Packet>
57 EIGEN_DEVICE_FUNC inline Packet comparePacket(const Packet& incumbent, const Packet& candidate) const {
58 return pandnot(pcmp_lt_or_nan(incumbent, candidate), pisnan(candidate));
59 }
60 template <typename Packet>
61 EIGEN_DEVICE_FUNC inline Scalar predux(const Packet& a) const {
62 return predux_max<PropagateNumbers>(a);
63 }
64};
65
66template <typename Scalar, int NaNPropagation, bool IsInteger = NumTraits<Scalar>::IsInteger>
67struct min_coeff_functor {
68 EIGEN_DEVICE_FUNC inline bool compareCoeff(const Scalar& incumbent, const Scalar& candidate) const {
69 return candidate < incumbent;
70 }
71 template <typename Packet>
72 EIGEN_DEVICE_FUNC inline Packet comparePacket(const Packet& incumbent, const Packet& candidate) const {
73 return pcmp_lt(candidate, incumbent);
74 }
75 template <typename Packet>
76 EIGEN_DEVICE_FUNC inline Scalar predux(const Packet& a) const {
77 return predux_min(a);
78 }
79};
80
81template <typename Scalar>
82struct min_coeff_functor<Scalar, PropagateNaN, false> {
83 EIGEN_DEVICE_FUNC inline bool compareCoeff(const Scalar& incumbent, const Scalar& candidate) const {
84 return (candidate < incumbent) || ((candidate != candidate) && (incumbent == incumbent));
85 }
86 template <typename Packet>
87 EIGEN_DEVICE_FUNC inline Packet comparePacket(const Packet& incumbent, const Packet& candidate) const {
88 return pandnot(pcmp_lt_or_nan(candidate, incumbent), pisnan(incumbent));
89 }
90 template <typename Packet>
91 EIGEN_DEVICE_FUNC inline Scalar predux(const Packet& a) const {
92 return predux_min<PropagateNaN>(a);
93 }
94};
95
96template <typename Scalar>
97struct min_coeff_functor<Scalar, PropagateNumbers, false> {
98 EIGEN_DEVICE_FUNC inline bool compareCoeff(const Scalar& incumbent, const Scalar& candidate) const {
99 return (candidate < incumbent) || ((candidate == candidate) && (incumbent != incumbent));
100 }
101 template <typename Packet>
102 EIGEN_DEVICE_FUNC inline Packet comparePacket(const Packet& incumbent, const Packet& candidate) const {
103 return pandnot(pcmp_lt_or_nan(candidate, incumbent), pisnan(candidate));
104 }
105 template <typename Packet>
106 EIGEN_DEVICE_FUNC inline Scalar predux(const Packet& a) const {
107 return predux_min<PropagateNumbers>(a);
108 }
109};
110
111// The packet loop needs pcmp_lt (and pcmp_lt_or_nan/pisnan for the NaN-aware functors); predux_max/predux_min
112// fall back to a per-lane reduction, so HasMax/HasMin are not required.
113template <typename Scalar>
114struct min_max_traits {
115 static constexpr bool PacketAccess = packet_traits<Scalar>::Vectorizable && packet_traits<Scalar>::HasCmp;
116};
117template <typename Scalar, int NaNPropagation>
118struct functor_traits<max_coeff_functor<Scalar, NaNPropagation>> : min_max_traits<Scalar> {};
119template <typename Scalar, int NaNPropagation>
120struct functor_traits<min_coeff_functor<Scalar, NaNPropagation>> : min_max_traits<Scalar> {};
121
122template <typename Evaluator, typename Func, bool Linear, bool Vectorize>
123struct find_coeff_loop;
124template <typename Evaluator, typename Func>
125struct find_coeff_loop<Evaluator, Func, /*Linear*/ false, /*Vectorize*/ false> {
126 using Scalar = typename Evaluator::Scalar;
127 static EIGEN_DEVICE_FUNC inline void run(const Evaluator& eval, Func& func, Scalar& res, Index& outer, Index& inner) {
128 Index outerSize = eval.outerSize();
129 Index innerSize = eval.innerSize();
130
131 /* initialization performed in calling function */
132 /* result = eval.coeff(0, 0); */
133 /* outer = 0; */
134 /* inner = 0; */
135
136 for (Index j = 0; j < outerSize; j++) {
137 for (Index i = 0; i < innerSize; i++) {
138 Scalar xprCoeff = eval.coeffByOuterInner(j, i);
139 if (func.compareCoeff(res, xprCoeff)) {
140 outer = j;
141 inner = i;
142 res = xprCoeff;
143 }
144 }
145 }
146 }
147};
148template <typename Evaluator, typename Func>
149struct find_coeff_loop<Evaluator, Func, /*Linear*/ true, /*Vectorize*/ false> {
150 using Scalar = typename Evaluator::Scalar;
151 static EIGEN_DEVICE_FUNC inline void run(const Evaluator& eval, Func& func, Scalar& res, Index& index) {
152 Index size = eval.size();
153
154 /* initialization performed in calling function */
155 /* result = eval.coeff(0); */
156 /* index = 0; */
157
158 for (Index k = 0; k < size; k++) {
159 Scalar xprCoeff = eval.coeff(k);
160 if (func.compareCoeff(res, xprCoeff)) {
161 index = k;
162 res = xprCoeff;
163 }
164 }
165 }
166};
167template <typename Evaluator, typename Func>
168struct find_coeff_loop<Evaluator, Func, /*Linear*/ false, /*Vectorize*/ true> {
169 using ScalarImpl = find_coeff_loop<Evaluator, Func, false, false>;
170 using Scalar = typename Evaluator::Scalar;
171 using Packet = typename Evaluator::Packet;
172 static constexpr int PacketSize = unpacket_traits<Packet>::size;
173 static EIGEN_DEVICE_FUNC inline void run(const Evaluator& eval, Func& func, Scalar& result, Index& outer,
174 Index& inner) {
175 Index outerSize = eval.outerSize();
176 Index innerSize = eval.innerSize();
177 if (innerSize < PacketSize) {
178 ScalarImpl::run(eval, func, result, outer, inner);
179 return;
180 }
181 Index packetEnd = numext::round_down(innerSize, PacketSize);
182
183 /* initialization performed in calling function */
184 /* result = eval.coeff(0, 0); */
185 /* outer = 0; */
186 /* inner = 0; */
187
188 bool checkPacket = false;
189
190 for (Index j = 0; j < outerSize; j++) {
191 Packet resultPacket = pset1<Packet>(result);
192 for (Index i = 0; i < packetEnd; i += PacketSize) {
193 Packet xprPacket = eval.template packetByOuterInner<Unaligned, Packet>(j, i);
194 if (predux_any(func.comparePacket(resultPacket, xprPacket))) {
195 outer = j;
196 inner = i;
197 result = func.predux(xprPacket);
198 resultPacket = pset1<Packet>(result);
199 checkPacket = true;
200 }
201 }
202
203 for (Index i = packetEnd; i < innerSize; i++) {
204 Scalar xprCoeff = eval.coeffByOuterInner(j, i);
205 if (func.compareCoeff(result, xprCoeff)) {
206 outer = j;
207 inner = i;
208 result = xprCoeff;
209 checkPacket = false;
210 }
211 }
212 }
213
214 if (checkPacket) {
215 result = eval.coeffByOuterInner(outer, inner);
216 Index i_end = inner + PacketSize;
217 for (Index i = inner; i < i_end; i++) {
218 Scalar xprCoeff = eval.coeffByOuterInner(outer, i);
219 if (func.compareCoeff(result, xprCoeff)) {
220 inner = i;
221 result = xprCoeff;
222 }
223 }
224 }
225 }
226};
227template <typename Evaluator, typename Func>
228struct find_coeff_loop<Evaluator, Func, /*Linear*/ true, /*Vectorize*/ true> {
229 using ScalarImpl = find_coeff_loop<Evaluator, Func, true, false>;
230 using Scalar = typename Evaluator::Scalar;
231 using Packet = typename Evaluator::Packet;
232 static constexpr int PacketSize = unpacket_traits<Packet>::size;
233 static constexpr int Alignment = Evaluator::Alignment;
234
235 static EIGEN_DEVICE_FUNC inline void run(const Evaluator& eval, Func& func, Scalar& result, Index& index) {
236 Index size = eval.size();
237 if (size < PacketSize) {
238 ScalarImpl::run(eval, func, result, index);
239 return;
240 }
241 Index packetEnd = numext::round_down(size, PacketSize);
242
243 /* initialization performed in calling function */
244 /* result = eval.coeff(0); */
245 /* index = 0; */
246
247 Packet resultPacket = pset1<Packet>(result);
248 bool checkPacket = false;
249
250 for (Index k = 0; k < packetEnd; k += PacketSize) {
251 Packet xprPacket = eval.template packet<Alignment, Packet>(k);
252 if (predux_any(func.comparePacket(resultPacket, xprPacket))) {
253 index = k;
254 result = func.predux(xprPacket);
255 resultPacket = pset1<Packet>(result);
256 checkPacket = true;
257 }
258 }
259
260 for (Index k = packetEnd; k < size; k++) {
261 Scalar xprCoeff = eval.coeff(k);
262 if (func.compareCoeff(result, xprCoeff)) {
263 index = k;
264 result = xprCoeff;
265 checkPacket = false;
266 }
267 }
268
269 if (checkPacket) {
270 result = eval.coeff(index);
271 Index k_end = index + PacketSize;
272 for (Index k = index; k < k_end; k++) {
273 Scalar xprCoeff = eval.coeff(k);
274 if (func.compareCoeff(result, xprCoeff)) {
275 index = k;
276 result = xprCoeff;
277 }
278 }
279 }
280 }
281};
282
283template <typename Derived>
284struct find_coeff_evaluator : public evaluator<Derived> {
285 using Base = evaluator<Derived>;
286 using Scalar = typename Derived::Scalar;
287 using Packet = typename packet_traits<Scalar>::type;
288 static constexpr int Flags = Base::Flags;
289 static constexpr bool IsRowMajor = bool(Flags & RowMajorBit);
290 EIGEN_DEVICE_FUNC inline find_coeff_evaluator(const Derived& xpr) : Base(xpr), m_xpr(xpr) {}
291
292 EIGEN_DEVICE_FUNC inline Scalar coeffByOuterInner(Index outer, Index inner) const {
293 Index row = IsRowMajor ? outer : inner;
294 Index col = IsRowMajor ? inner : outer;
295 return Base::coeff(row, col);
296 }
297 template <int LoadMode, typename PacketType>
298 EIGEN_DEVICE_FUNC inline PacketType packetByOuterInner(Index outer, Index inner) const {
299 Index row = IsRowMajor ? outer : inner;
300 Index col = IsRowMajor ? inner : outer;
301 return Base::template packet<LoadMode, PacketType>(row, col);
302 }
303
304 EIGEN_DEVICE_FUNC inline Index innerSize() const { return m_xpr.innerSize(); }
305 EIGEN_DEVICE_FUNC inline Index outerSize() const { return m_xpr.outerSize(); }
306 EIGEN_DEVICE_FUNC inline Index size() const { return m_xpr.size(); }
307
308 const Derived& m_xpr;
309};
310
311template <typename Derived, typename Func>
312struct find_coeff_impl {
313 using Evaluator = find_coeff_evaluator<Derived>;
314 static constexpr int Flags = Evaluator::Flags;
315 static constexpr bool IsRowMajor = Derived::IsRowMajor;
316 static constexpr int MaxInnerSizeAtCompileTime =
317 IsRowMajor ? Derived::MaxColsAtCompileTime : Derived::MaxRowsAtCompileTime;
318 static constexpr int MaxSizeAtCompileTime = Derived::MaxSizeAtCompileTime;
319
320 using Scalar = typename Derived::Scalar;
321 using Packet = typename Evaluator::Packet;
322
323 static constexpr int PacketSize = unpacket_traits<Packet>::size;
324 static constexpr bool Linearize = bool(Flags & LinearAccessBit);
325 static constexpr bool DontVectorize =
326 enum_lt_not_dynamic(Linearize ? MaxSizeAtCompileTime : MaxInnerSizeAtCompileTime, PacketSize);
327 static constexpr bool Vectorize =
328 !DontVectorize && bool(Flags & PacketAccessBit) && functor_traits<Func>::PacketAccess;
329
330 using Loop = find_coeff_loop<Evaluator, Func, Linearize, Vectorize>;
331
332 template <bool ForwardLinearAccess = Linearize, std::enable_if_t<!ForwardLinearAccess, bool> = true>
333 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run(const Derived& xpr, Func& func, Scalar& res, Index& outer,
334 Index& inner) {
335 Evaluator eval(xpr);
336 Loop::run(eval, func, res, outer, inner);
337 }
338 template <bool ForwardLinearAccess = Linearize, std::enable_if_t<ForwardLinearAccess, bool> = true>
339 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run(const Derived& xpr, Func& func, Scalar& res, Index& outer,
340 Index& inner) {
341 // where possible, use the linear loop and back-calculate the outer and inner indices
342 Index index = 0;
343 run(xpr, func, res, index);
344 outer = index / xpr.innerSize();
345 inner = index % xpr.innerSize();
346 }
347 static EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void run(const Derived& xpr, Func& func, Scalar& res, Index& index) {
348 Evaluator eval(xpr);
349 Loop::run(eval, func, res, index);
350 }
351};
352
353template <typename Derived, typename IndexType, typename Func>
354EIGEN_DEVICE_FUNC typename internal::traits<Derived>::Scalar findCoeff(const DenseBase<Derived>& mat, Func& func,
355 IndexType* rowPtr, IndexType* colPtr) {
356 eigen_assert(mat.rows() > 0 && mat.cols() > 0 && "you are using an empty matrix");
357 using Scalar = typename DenseBase<Derived>::Scalar;
358 using FindCoeffImpl = internal::find_coeff_impl<Derived, Func>;
359 Index outer = 0;
360 Index inner = 0;
361 Scalar res = mat.coeff(0, 0);
362 FindCoeffImpl::run(mat.derived(), func, res, outer, inner);
363 *rowPtr = internal::convert_index<IndexType>(Derived::IsRowMajor ? outer : inner);
364 if (colPtr) *colPtr = internal::convert_index<IndexType>(Derived::IsRowMajor ? inner : outer);
365 return res;
366}
367
368template <typename Derived, typename IndexType, typename Func>
369EIGEN_DEVICE_FUNC typename internal::traits<Derived>::Scalar findCoeff(const DenseBase<Derived>& mat, Func& func,
370 IndexType* indexPtr) {
371 eigen_assert(mat.size() > 0 && "you are using an empty matrix");
372 EIGEN_STATIC_ASSERT_VECTOR_ONLY(Derived)
373 using Scalar = typename DenseBase<Derived>::Scalar;
374 using FindCoeffImpl = internal::find_coeff_impl<Derived, Func>;
375 Index index = 0;
376 Scalar res = mat.coeff(0);
377 FindCoeffImpl::run(mat.derived(), func, res, index);
378 *indexPtr = internal::convert_index<IndexType>(index);
379 return res;
380}
381
382} // namespace internal
383
397template <typename Derived>
398template <int NaNPropagation, typename IndexType>
399EIGEN_DEVICE_FUNC typename internal::traits<Derived>::Scalar DenseBase<Derived>::minCoeff(IndexType* rowPtr,
400 IndexType* colPtr) const {
401 using Func = internal::min_coeff_functor<Scalar, NaNPropagation>;
402 Func func;
403 return internal::findCoeff(derived(), func, rowPtr, colPtr);
404}
405
419template <typename Derived>
420template <int NaNPropagation, typename IndexType>
421EIGEN_DEVICE_FUNC typename internal::traits<Derived>::Scalar DenseBase<Derived>::minCoeff(IndexType* indexPtr) const {
422 using Func = internal::min_coeff_functor<Scalar, NaNPropagation>;
423 Func func;
424 return internal::findCoeff(derived(), func, indexPtr);
425}
428 * \returns the maximum of all coefficients of *this and puts in *row and *col its location.
429 *
430 * If there are multiple coefficients with the same extreme value, the location of the first instance is returned.
431 *
432 * In case \c *this contains NaN, NaNPropagation determines the behavior:
433 * NaNPropagation == PropagateFast : undefined
434 * NaNPropagation == PropagateNaN : result is NaN
435 * NaNPropagation == PropagateNumbers : result is maximum of elements that are not NaN
436 * \warning the matrix must be not empty, otherwise an assertion is triggered.
437 *
438 * \sa DenseBase::minCoeff(IndexType*,IndexType*), DenseBase::visit(), DenseBase::maxCoeff()
439 */
440template <typename Derived>
441template <int NaNPropagation, typename IndexType>
442EIGEN_DEVICE_FUNC typename internal::traits<Derived>::Scalar DenseBase<Derived>::maxCoeff(IndexType* rowPtr,
443 IndexType* colPtr) const {
444 using Func = internal::max_coeff_functor<Scalar, NaNPropagation>;
445 Func func;
446 return internal::findCoeff(derived(), func, rowPtr, colPtr);
447}
448
462template <typename Derived>
463template <int NaNPropagation, typename IndexType>
464EIGEN_DEVICE_FUNC typename internal::traits<Derived>::Scalar DenseBase<Derived>::maxCoeff(IndexType* indexPtr) const {
465 using Func = internal::max_coeff_functor<Scalar, NaNPropagation>;
466 Func func;
467 return internal::findCoeff(derived(), func, indexPtr);
468}
469
470} // namespace Eigen
471
472#endif // EIGEN_FIND_COEFF_H
internal::traits< Derived >::Scalar minCoeff() const
Definition Redux.h:786
internal::traits< Derived >::Scalar maxCoeff() const
Definition Redux.h:799
typename internal::traits< Derived >::Scalar Scalar
Definition DenseBase.h:63
@ PropagateNaN
Definition Constants.h:343
@ PropagateNumbers
Definition Constants.h:345
constexpr unsigned int PacketAccessBit
Definition Constants.h:98
constexpr unsigned int LinearAccessBit
Definition Constants.h:134
constexpr unsigned int RowMajorBit
Definition Constants.h:71