2018-10-09 23:36:50 +02:00
|
|
|
// This file is part of Eigen, a lightweight C++ template library
|
|
|
|
|
// for linear algebra.
|
|
|
|
|
//
|
|
|
|
|
// Copyright (C) 2011-2018 Gael Guennebaud <gael.guennebaud@inria.fr>
|
|
|
|
|
//
|
|
|
|
|
// This Source Code Form is subject to the terms of the Mozilla
|
|
|
|
|
// Public License v. 2.0. If a copy of the MPL was not distributed
|
|
|
|
|
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
|
|
|
|
|
|
#ifndef EIGEN_PARTIALREDUX_H
|
|
|
|
|
#define EIGEN_PARTIALREDUX_H
|
|
|
|
|
|
2023-08-21 16:25:22 +00:00
|
|
|
// IWYU pragma: private
|
2021-09-10 19:12:26 +00:00
|
|
|
#include "./InternalHeaderCheck.h"
|
|
|
|
|
|
2018-10-09 23:36:50 +02:00
|
|
|
namespace Eigen {
|
|
|
|
|
|
|
|
|
|
namespace internal {
|
|
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
|
*
|
|
|
|
|
* This file provides evaluators for partial reductions.
|
|
|
|
|
* There are two modes:
|
|
|
|
|
*
|
|
|
|
|
* - scalar path: simply calls the respective function on the column or row.
|
|
|
|
|
* -> nothing special here, all the tricky part is handled by the return
|
|
|
|
|
* types of VectorwiseOp's members. They embed the functor calling the
|
|
|
|
|
* respective DenseBase's member function.
|
|
|
|
|
*
|
|
|
|
|
* - vectorized path: implements a packet-wise reductions followed by
|
|
|
|
|
* some (optional) processing of the outcome, e.g., division by n for mean.
|
|
|
|
|
*
|
|
|
|
|
* For the vectorized path let's observe that the packet-size and outer-unrolling
|
2021-09-23 15:22:00 +00:00
|
|
|
* are both decided by the assignment logic. So all we have to do is to decide
|
2018-10-09 23:36:50 +02:00
|
|
|
* on the inner unrolling.
|
|
|
|
|
*
|
|
|
|
|
* For the unrolling, we can reuse "internal::redux_vec_unroller" from Redux.h,
|
|
|
|
|
* but be need to be careful to specify correct increment.
|
|
|
|
|
*
|
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
|
|
/* logic deciding a strategy for unrolling of vectorized paths */
|
|
|
|
|
template <typename Func, typename Evaluator>
|
|
|
|
|
struct packetwise_redux_traits {
|
2026-03-29 17:40:39 -07:00
|
|
|
static constexpr int OuterSize =
|
|
|
|
|
int(Evaluator::IsRowMajor) ? Evaluator::RowsAtCompileTime : Evaluator::ColsAtCompileTime;
|
|
|
|
|
static constexpr int Cost = OuterSize == Dynamic
|
|
|
|
|
? HugeCost
|
|
|
|
|
: OuterSize * Evaluator::CoeffReadCost + (OuterSize - 1) * functor_traits<Func>::Cost;
|
|
|
|
|
static constexpr int Unrolling = Cost <= EIGEN_UNROLLING_LIMIT ? CompleteUnrolling : NoUnrolling;
|
2018-10-09 23:36:50 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Value to be returned when size==0 , by default let's return 0 */
|
|
|
|
|
template <typename PacketType, typename Func>
|
2021-10-20 16:03:12 -07:00
|
|
|
EIGEN_DEVICE_FUNC PacketType packetwise_redux_empty_value(const Func&) {
|
|
|
|
|
const typename unpacket_traits<PacketType>::type zero(0);
|
|
|
|
|
return pset1<PacketType>(zero);
|
|
|
|
|
}
|
2018-10-09 23:36:50 +02:00
|
|
|
|
|
|
|
|
/* For products the default is 1 */
|
|
|
|
|
template <typename PacketType, typename Scalar>
|
2021-10-20 16:03:12 -07:00
|
|
|
EIGEN_DEVICE_FUNC PacketType packetwise_redux_empty_value(const scalar_product_op<Scalar, Scalar>&) {
|
|
|
|
|
return pset1<PacketType>(Scalar(1));
|
|
|
|
|
}
|
2018-10-09 23:36:50 +02:00
|
|
|
|
|
|
|
|
/* Perform the actual reduction */
|
|
|
|
|
template <typename Func, typename Evaluator, int Unrolling = packetwise_redux_traits<Func, Evaluator>::Unrolling>
|
|
|
|
|
struct packetwise_redux_impl;
|
|
|
|
|
|
|
|
|
|
/* Perform the actual reduction with unrolling */
|
|
|
|
|
template <typename Func, typename Evaluator>
|
|
|
|
|
struct packetwise_redux_impl<Func, Evaluator, CompleteUnrolling> {
|
2026-03-29 17:40:39 -07:00
|
|
|
using Base = redux_novec_unroller<Func, Evaluator, 0, Evaluator::SizeAtCompileTime>;
|
|
|
|
|
using Scalar = typename Evaluator::Scalar;
|
2018-10-09 23:36:50 +02:00
|
|
|
|
|
|
|
|
template <typename PacketType>
|
|
|
|
|
EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE PacketType run(const Evaluator& eval, const Func& func, Index /*size*/) {
|
|
|
|
|
return redux_vec_unroller<Func, Evaluator, 0,
|
|
|
|
|
packetwise_redux_traits<Func, Evaluator>::OuterSize>::template run<PacketType>(eval,
|
|
|
|
|
func);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Add a specialization of redux_vec_unroller for size==0 at compiletime.
|
|
|
|
|
* This specialization is not required for general reductions, which is
|
|
|
|
|
* why it is defined here.
|
|
|
|
|
*/
|
2023-05-24 20:26:52 +00:00
|
|
|
template <typename Func, typename Evaluator, Index Start>
|
2018-10-09 23:36:50 +02:00
|
|
|
struct redux_vec_unroller<Func, Evaluator, Start, 0> {
|
|
|
|
|
template <typename PacketType>
|
|
|
|
|
EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE PacketType run(const Evaluator&, const Func& f) {
|
|
|
|
|
return packetwise_redux_empty_value<PacketType>(f);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Perform the actual reduction for dynamic sizes */
|
|
|
|
|
template <typename Func, typename Evaluator>
|
|
|
|
|
struct packetwise_redux_impl<Func, Evaluator, NoUnrolling> {
|
2026-03-29 17:40:39 -07:00
|
|
|
using Scalar = typename Evaluator::Scalar;
|
|
|
|
|
using PacketScalar = typename redux_traits<Func, Evaluator>::PacketType;
|
2018-10-09 23:36:50 +02:00
|
|
|
|
|
|
|
|
template <typename PacketType>
|
|
|
|
|
EIGEN_DEVICE_FUNC static PacketType run(const Evaluator& eval, const Func& func, Index size) {
|
|
|
|
|
if (size == 0) return packetwise_redux_empty_value<PacketType>(func);
|
2023-11-29 11:12:48 +00:00
|
|
|
|
2025-04-14 17:44:53 +00:00
|
|
|
const Index size4 = 1 + numext::round_down(size - 1, 4);
|
2018-10-09 23:36:50 +02:00
|
|
|
PacketType p = eval.template packetByOuterInner<Unaligned, PacketType>(0, 0);
|
|
|
|
|
// This loop is optimized for instruction pipelining:
|
|
|
|
|
// - each iteration generates two independent instructions
|
|
|
|
|
// - thanks to branch prediction and out-of-order execution we have independent instructions across loops
|
2025-04-14 17:44:53 +00:00
|
|
|
for (Index i = 1; i < size4; i += 4)
|
2018-10-09 23:36:50 +02:00
|
|
|
p = func.packetOp(
|
|
|
|
|
p, func.packetOp(func.packetOp(eval.template packetByOuterInner<Unaligned, PacketType>(i + 0, 0),
|
|
|
|
|
eval.template packetByOuterInner<Unaligned, PacketType>(i + 1, 0)),
|
|
|
|
|
func.packetOp(eval.template packetByOuterInner<Unaligned, PacketType>(i + 2, 0),
|
|
|
|
|
eval.template packetByOuterInner<Unaligned, PacketType>(i + 3, 0))));
|
2025-04-14 17:44:53 +00:00
|
|
|
for (Index i = size4; i < size; ++i)
|
|
|
|
|
p = func.packetOp(p, eval.template packetByOuterInner<Unaligned, PacketType>(i, 0));
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename Func, typename Evaluator>
|
|
|
|
|
struct packetwise_segment_redux_impl {
|
2026-03-29 17:40:39 -07:00
|
|
|
using Scalar = typename Evaluator::Scalar;
|
|
|
|
|
using PacketScalar = typename redux_traits<Func, Evaluator>::PacketType;
|
2025-04-14 17:44:53 +00:00
|
|
|
|
|
|
|
|
template <typename PacketType>
|
|
|
|
|
EIGEN_DEVICE_FUNC static PacketType run(const Evaluator& eval, const Func& func, Index size, Index begin,
|
|
|
|
|
Index count) {
|
|
|
|
|
if (size == 0) return packetwise_redux_empty_value<PacketType>(func);
|
|
|
|
|
|
|
|
|
|
PacketType p = eval.template packetSegmentByOuterInner<Unaligned, PacketType>(0, 0, begin, count);
|
|
|
|
|
for (Index i = 1; i < size; ++i)
|
|
|
|
|
p = func.packetOp(p, eval.template packetSegmentByOuterInner<Unaligned, PacketType>(i, 0, begin, count));
|
2018-10-09 23:36:50 +02:00
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename ArgType, typename MemberOp, int Direction>
|
|
|
|
|
struct evaluator<PartialReduxExpr<ArgType, MemberOp, Direction> >
|
|
|
|
|
: evaluator_base<PartialReduxExpr<ArgType, MemberOp, Direction> > {
|
2026-03-29 17:40:39 -07:00
|
|
|
using XprType = PartialReduxExpr<ArgType, MemberOp, Direction>;
|
|
|
|
|
using ArgTypeNested = typename internal::nested_eval<ArgType, 1>::type;
|
|
|
|
|
using ConstArgTypeNested = add_const_on_value_type_t<ArgTypeNested>;
|
|
|
|
|
using ArgTypeNestedCleaned = internal::remove_all_t<ArgTypeNested>;
|
|
|
|
|
using InputScalar = typename ArgType::Scalar;
|
|
|
|
|
using Scalar = typename XprType::Scalar;
|
2018-10-09 23:36:50 +02:00
|
|
|
enum {
|
|
|
|
|
TraversalSize = Direction == int(Vertical) ? int(ArgType::RowsAtCompileTime) : int(ArgType::ColsAtCompileTime)
|
|
|
|
|
};
|
2026-03-29 17:40:39 -07:00
|
|
|
using CostOpType = typename MemberOp::template Cost<int(TraversalSize)>;
|
2018-10-09 23:36:50 +02:00
|
|
|
enum {
|
|
|
|
|
CoeffReadCost = TraversalSize == Dynamic ? HugeCost
|
|
|
|
|
: TraversalSize == 0
|
|
|
|
|
? 1
|
2021-06-10 17:17:39 -07:00
|
|
|
: int(TraversalSize) * int(evaluator<ArgType>::CoeffReadCost) + int(CostOpType::value),
|
2023-11-29 11:12:48 +00:00
|
|
|
|
2022-01-10 20:53:29 +00:00
|
|
|
ArgFlags_ = evaluator<ArgType>::Flags,
|
2018-10-09 23:36:50 +02:00
|
|
|
|
2022-01-10 20:53:29 +00:00
|
|
|
Vectorizable_ = bool(int(ArgFlags_) & PacketAccessBit) && bool(MemberOp::Vectorizable) &&
|
|
|
|
|
(Direction == int(Vertical) ? bool(ArgFlags_ & RowMajorBit) : (ArgFlags_ & RowMajorBit) == 0) &&
|
2018-10-09 23:36:50 +02:00
|
|
|
(TraversalSize != 0),
|
2023-11-29 11:12:48 +00:00
|
|
|
|
2018-10-09 23:36:50 +02:00
|
|
|
Flags = (traits<XprType>::Flags & RowMajorBit) | (evaluator<ArgType>::Flags & (HereditaryBits & (~RowMajorBit))) |
|
2022-01-10 20:53:29 +00:00
|
|
|
(Vectorizable_ ? PacketAccessBit : 0) | LinearAccessBit,
|
2023-11-29 11:12:48 +00:00
|
|
|
|
2018-10-09 23:36:50 +02:00
|
|
|
Alignment = 0 // FIXME this will need to be improved once PartialReduxExpr is vectorized
|
|
|
|
|
};
|
|
|
|
|
|
2026-03-27 18:25:51 -07:00
|
|
|
EIGEN_DEVICE_FUNC explicit evaluator(const XprType& xpr) : m_arg(xpr.nestedExpression()), m_functor(xpr.functor()) {
|
2018-10-09 23:36:50 +02:00
|
|
|
EIGEN_INTERNAL_CHECK_COST_VALUE(TraversalSize == Dynamic ? HugeCost
|
|
|
|
|
: (TraversalSize == 0 ? 1 : int(CostOpType::value)));
|
|
|
|
|
EIGEN_INTERNAL_CHECK_COST_VALUE(CoeffReadCost);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-29 17:40:39 -07:00
|
|
|
using CoeffReturnType = typename XprType::CoeffReturnType;
|
2018-10-09 23:36:50 +02:00
|
|
|
|
|
|
|
|
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Scalar coeff(Index i, Index j) const {
|
|
|
|
|
return coeff(Direction == Vertical ? j : i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Scalar coeff(Index index) const {
|
|
|
|
|
return m_functor(m_arg.template subVector<DirectionType(Direction)>(index));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <int LoadMode, typename PacketType>
|
|
|
|
|
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketType packet(Index i, Index j) const {
|
|
|
|
|
return packet<LoadMode, PacketType>(Direction == Vertical ? j : i);
|
|
|
|
|
}
|
2023-11-29 11:12:48 +00:00
|
|
|
|
2018-10-09 23:36:50 +02:00
|
|
|
template <int LoadMode, typename PacketType>
|
|
|
|
|
EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC PacketType packet(Index idx) const {
|
2025-04-14 17:44:53 +00:00
|
|
|
static constexpr int PacketSize = internal::unpacket_traits<PacketType>::size;
|
|
|
|
|
static constexpr int PanelRows = Direction == Vertical ? ArgType::RowsAtCompileTime : PacketSize;
|
|
|
|
|
static constexpr int PanelCols = Direction == Vertical ? PacketSize : ArgType::ColsAtCompileTime;
|
|
|
|
|
using PanelType = Block<const ArgTypeNestedCleaned, PanelRows, PanelCols, true /* InnerPanel */>;
|
|
|
|
|
using PanelEvaluator = typename internal::redux_evaluator<PanelType>;
|
|
|
|
|
using BinaryOp = typename MemberOp::BinaryOp;
|
|
|
|
|
using Impl = internal::packetwise_redux_impl<BinaryOp, PanelEvaluator>;
|
2018-10-16 01:04:25 +02:00
|
|
|
|
|
|
|
|
// FIXME
|
|
|
|
|
// See bug 1612, currently if PacketSize==1 (i.e. complex<double> with 128bits registers) then the storage-order of
|
|
|
|
|
// panel get reversed and methods like packetByOuterInner do not make sense anymore in this context. So let's just
|
|
|
|
|
// by pass "vectorization" in this case:
|
2026-03-29 17:40:39 -07:00
|
|
|
EIGEN_IF_CONSTEXPR(PacketSize == 1) return internal::pset1<PacketType>(coeff(idx));
|
2023-11-29 11:12:48 +00:00
|
|
|
|
2025-04-14 17:44:53 +00:00
|
|
|
Index startRow = Direction == Vertical ? 0 : idx;
|
|
|
|
|
Index startCol = Direction == Vertical ? idx : 0;
|
|
|
|
|
Index numRows = Direction == Vertical ? m_arg.rows() : PacketSize;
|
|
|
|
|
Index numCols = Direction == Vertical ? PacketSize : m_arg.cols();
|
|
|
|
|
|
|
|
|
|
PanelType panel(m_arg, startRow, startCol, numRows, numCols);
|
|
|
|
|
PanelEvaluator panel_eval(panel);
|
|
|
|
|
PacketType p = Impl::template run<PacketType>(panel_eval, m_functor.binaryFunc(), m_arg.outerSize());
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <int LoadMode, typename PacketType>
|
|
|
|
|
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE PacketType packetSegment(Index i, Index j, Index begin, Index count) const {
|
|
|
|
|
return packetSegment<LoadMode, PacketType>(Direction == Vertical ? j : i, begin, count);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <int LoadMode, typename PacketType>
|
|
|
|
|
EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC PacketType packetSegment(Index idx, Index begin, Index count) const {
|
|
|
|
|
static constexpr int PanelRows = Direction == Vertical ? ArgType::RowsAtCompileTime : Dynamic;
|
|
|
|
|
static constexpr int PanelCols = Direction == Vertical ? Dynamic : ArgType::ColsAtCompileTime;
|
|
|
|
|
using PanelType = Block<const ArgTypeNestedCleaned, PanelRows, PanelCols, true /* InnerPanel */>;
|
|
|
|
|
using PanelEvaluator = typename internal::redux_evaluator<PanelType>;
|
|
|
|
|
using BinaryOp = typename MemberOp::BinaryOp;
|
|
|
|
|
using Impl = internal::packetwise_segment_redux_impl<BinaryOp, PanelEvaluator>;
|
|
|
|
|
|
|
|
|
|
Index startRow = Direction == Vertical ? 0 : idx;
|
|
|
|
|
Index startCol = Direction == Vertical ? idx : 0;
|
|
|
|
|
Index numRows = Direction == Vertical ? m_arg.rows() : begin + count;
|
|
|
|
|
Index numCols = Direction == Vertical ? begin + count : m_arg.cols();
|
|
|
|
|
|
|
|
|
|
PanelType panel(m_arg, startRow, startCol, numRows, numCols);
|
2018-10-09 23:36:50 +02:00
|
|
|
PanelEvaluator panel_eval(panel);
|
2025-04-14 17:44:53 +00:00
|
|
|
PacketType p = Impl::template run<PacketType>(panel_eval, m_functor.binaryFunc(), m_arg.outerSize(), begin, count);
|
2018-10-09 23:36:50 +02:00
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected:
|
2018-10-10 23:47:30 +02:00
|
|
|
ConstArgTypeNested m_arg;
|
2018-10-09 23:36:50 +02:00
|
|
|
const MemberOp m_functor;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // end namespace internal
|
|
|
|
|
|
|
|
|
|
} // end namespace Eigen
|
|
|
|
|
|
|
|
|
|
#endif // EIGEN_PARTIALREDUX_H
|