16#include <unsupported/Eigen/AutoDiff>
62[[nodiscard]]
constexpr Shared
79inline constexpr bool isBatchedMark =
false;
83inline constexpr bool isBatchedMark<NoJacobians>{
true};
86inline constexpr bool isBatchedMark<jacobians::PerColumn>{
true};
89inline constexpr bool isBatchedMark<jacobians::Shared>{
true};
101 internal::isBatchedMark<std::remove_cvref_t<T>> && !std::is_same_v<std::remove_cvref_t<T>, internal::NoJacobians>;
114template <
typename F,
typename Mark =
internal::NoJacobians>
117 static_assert(internal::isBatchedMark<Mark>);
125 static constexpr bool GivesJacobians = !std::is_same_v<Mark, internal::NoJacobians>;
128 static constexpr bool PerColumn = std::is_same_v<Mark, jacobians::PerColumn>;
141 operator()(In
const & in, Out
const & out, X
const & x, Rest
const &... rest)
const;
149inline constexpr bool isBatched =
false;
152template <
typename F,
typename Mark>
153inline constexpr bool isBatched<Batched<F, Mark>>{
true};
163inline constexpr bool takesBatches = isBatched<F> ||
requires {
requires F::TakesBatches; };
171 using type =
typename F::MarkType;
175template <
typename F,
typename Mark>
176struct BatchMarkOf<Batched<F, Mark>>
185using BatchMark =
typename BatchMarkOf<F>::type;
190template <
typename F,
bool = takesBatches<F>>
191struct BatchMarkOrNoneOf
193 using type = NoJacobians;
198struct BatchMarkOrNoneOf<F, true>
200 using type = BatchMark<F>;
207using BatchMarkOrNone =
typename BatchMarkOrNoneOf<F>::type;
213[[nodiscard]]
auto const &
214batchBody(F
const & f)
216 if constexpr (isBatched<F>)
228using BatchBody = std::remove_cvref_t<decltype(batchBody(std::declval<F const &>()))>;
247[[nodiscard]]
Batched<std::remove_cvref_t<F>>
250 return {std::forward<F>(function)};
257template <
typename F, JacobianMark Mark>
258 requires(!internal::isBatched<std::remove_cvref_t<F>> && !MatrixExpression<F>)
259[[nodiscard]] Batched<std::remove_cvref_t<F>, std::remove_cvref_t<Mark>>
262 return {std::forward<F>(function)};
269template <
typename Name,
typename Blocks>
270inline constexpr std::size_t blockPosition = 0;
273template <
typename Name,
typename... Blocks>
274inline constexpr std::size_t blockPosition<Name, std::tuple<Blocks...>> = [] {
275 constexpr std::array<bool,
sizeof...(Blocks) + 1> same{std::is_same_v<Name, Blocks>...,
true};
289template <
typename In>
290using InputBlocks =
typename decltype([] {
291 if constexpr (IsComposite<In>)
293 return std::type_identity<CompositeBlocks<In>>{};
297 return std::type_identity<std::tuple<In>>{};
304template <
typename Name,
typename In>
305[[nodiscard]] BlockRows
306inputBlockRows(In
const & in)
308 if constexpr (IsComposite<In>)
310 return differenceBlockRows<Name>(in);
340template <
typename In,
typename Out,
typename Scalar_,
typename Mark>
344 using Blocks = internal::InputBlocks<In>;
346 static constexpr std::size_t BlockCount = std::tuple_size_v<Blocks>;
378 std::array<bool, BlockCount>
const & wanted
384 Eigen::Index offset = 0;
385 [&]<std::size_t... K>(std::index_sequence<K...>) {
386 ((m_offsets[K] = wanted[K] ? offset : -1,
387 offset += wanted[K] ? internal::inputBlockRows<std::tuple_element_t<K, Blocks>>(in).size : 0),
389 }(std::make_index_sequence<BlockCount>{});
390 m_wantedColumns = offset;
392 m_jacobians.cols() == (std::is_same_v<Mark, jacobians::PerColumn> ?
values.cols() : 1) * m_wantedColumns
393 &&
"the Jacobians' storage holds the wanted blocks' columns for each column, or once"
414 template <
typename Name>
419 return m_offsets[internal::blockPosition<Name, Blocks>] >= 0;
428 return !std::is_same_v<Mark, internal::NoJacobians> && m_wantedColumns ==
differenceDimension(m_in);
439 !std::is_same_v<Mark, jacobians::Shared>,
440 "under jacobians::shared(), a batched function writes its whole Jacobian once for every column, jacobian()"
442 EMRYS_FILTER_ASSERT(j >= 0 && j < m_values.cols() &&
"the column must be one of the batch");
443 return wholeColumns(j * m_wantedColumns);
454 !std::is_same_v<Mark, jacobians::PerColumn>,
455 "under jacobians::perColumn(), a batched function writes its whole Jacobian once per column, jacobian(j)"
457 return wholeColumns(0);
464 template <
typename Name>
468 checkJacobian<Name>();
470 !std::is_same_v<Mark, jacobians::Shared>,
471 "a batched function marked jacobians::shared() writes one Jacobian for every column, jacobian<Name>()"
473 EMRYS_FILTER_ASSERT(j >= 0 && j < m_values.cols() &&
"the column must be one of the batch");
474 return columnsOf<Name>(j * m_wantedColumns);
480 template <
typename Name>
484 checkJacobian<Name>();
486 !std::is_same_v<Mark, jacobians::PerColumn>,
487 "a batched function marked jacobians::perColumn() writes one Jacobian per column, jacobian<Name>(j)"
489 return columnsOf<Name>(0);
497 template <
typename Out2,
typename In2>
502 static_assert(std::tuple_size_v<internal::InputBlocks<In2>> == BlockCount);
503 auto wanted = std::array<bool, BlockCount>{};
504 for (std::size_t k = 0; k < BlockCount; ++k)
506 wanted[k] = m_offsets[k] >= 0;
509 typename Renamed::ValuesType{m_values.data(), m_values.rows(), m_values.cols()},
510 typename Renamed::JacobiansType{m_jacobians.data(), m_jacobians.rows(), m_jacobians.cols()},
519 template <
typename Name>
520 static consteval void
524 internal::blockPosition<Name, Blocks> < BlockCount,
525 "a batched function writes a Jacobian only for a block of its input, by the name reads() gives it"
532 template <
typename Name>
533 static consteval void
537 !std::is_same_v<Mark, internal::NoJacobians>,
538 "a batched function marked without Jacobians writes none: mark it batched(f, jacobians::perColumn()) "
539 "or batched(f, jacobians::shared())"
547 template <
typename Name>
549 columnsOf(Eigen::Index start)
551 Eigen::Index
const offset = m_offsets[internal::blockPosition<Name, Blocks>];
552 EMRYS_FILTER_ASSERT(offset >= 0 &&
"write only the Jacobians wantsJacobian() says are wanted");
554 return internal::middleColsOf<Cols>(m_jacobians, start + offset, internal::inputBlockRows<Name>(m_in).size);
558 static consteval void
562 !std::is_same_v<Mark, internal::NoJacobians>,
563 "a batched function marked without Jacobians writes none: mark it batched(f, jacobians::perColumn()) "
564 "or batched(f, jacobians::shared())"
570 wholeColumns(Eigen::Index start)
574 &&
"write the whole Jacobian only when wantsJacobians() says every block is wanted"
577 return internal::middleColsOf<Cols>(m_jacobians, start, m_wantedColumns);
583 std::array<Eigen::Index, BlockCount> m_offsets{};
584 Eigen::Index m_wantedColumns = 0;
587template <
typename F,
typename Mark>
594 "a batched function returns nothing to size its output by, so its model names the output with writes() or "
597 using T =
typename X::Scalar;
600 using Jacobian = internal::MatrixOf<
606 EMRYS_FILTER_ASSERT(x.rows() ==
valueDimension(in) && x.cols() == 1 &&
"the point is a vector in the input space");
608 auto jacobian = Jacobian{};
609 auto wanted = std::array<bool, std::tuple_size_v<internal::InputBlocks<In>>>{};
616 typename Output::ValuesType{value.data(), value.rows(), 1},
621 internal::callForm(
function, in, out, x, rest..., y);
624 return withJacobian(std::move(value), std::move(jacobian));
640template <
typename F,
typename In,
typename Out,
typename X,
typename Y,
typename... Rest>
642callBatched(F
const &
batched, In
const & in, Out
const & out, X
const & x, Y & y, Rest
const &... rest)
644 static_assert(takesBatches<F>);
645 using T =
typename X::Scalar;
649 &&
"the batch writes a column in the output space per column in the input space"
651 auto output = Output{
652 typename Output::ValuesType{y.data(), y.rows(), y.cols()},
657 callForm(batchBody(
batched), in, out, x, rest..., output);
663template <
typename Name,
typename In>
664[[nodiscard]] BlockRows
665inputValueBlockRows(In
const & in)
667 if constexpr (IsComposite<In>)
669 return blockRows<Name>(in);
681template <
typename F,
typename In,
typename Out,
typename Dual,
typename... Rest>
682inline constexpr bool batchTakesDuals = functionForm<
693 BatchedOutput<In, Out, Dual, NoJacobians> &>
710template <
typename F,
typename In,
typename Out,
typename X, std::size_t K,
typename Y,
typename J,
typename... Rest>
712callBatchedWithJacobians(
717 std::array<bool, K>
const & wanted,
723 static_assert(takesBatches<F>);
724 using Mark = BatchMark<F>;
725 using T =
typename X::Scalar;
726 using Blocks = InputBlocks<In>;
727 static_assert(K == std::tuple_size_v<Blocks>);
728 Eigen::Index
const count = x.cols();
731 [&]<std::size_t... B>(std::index_sequence<B...>) {
732 ((w += wanted[B] ? inputBlockRows<std::tuple_element_t<B, Blocks>>(in).size : 0), ...);
733 }(std::make_index_sequence<K>{});
736 && jacobians.cols() == w * count
737 &&
"the batch writes a value and a Jacobian over the wanted blocks per column of its input"
739 if constexpr (!std::is_same_v<Mark, NoJacobians>)
741 using Output = BatchedOutput<In, Out, T, Mark>;
742 constexpr bool PerColumn = std::is_same_v<Mark, jacobians::PerColumn>;
744 using Shared = MatrixOf<
750 auto shared = Shared{};
751 if constexpr (!PerColumn)
755 auto output = Output{
756 typename Output::ValuesType{y.data(), y.rows(), count},
757 typename Output::JacobiansType{PerColumn ? jacobians.data() : shared.data(), m, PerColumn ? w * count : w},
761 callForm(batchBody(
batched), in, out, x, rest..., output);
762 if constexpr (!PerColumn)
764 for (Eigen::Index j = 0; j < count; ++j)
766 jacobians.middleCols(j * w, w) = shared;
772 using Derivatives = Eigen::
773 Matrix<T, Eigen::Dynamic, 1, Eigen::ColMajor, maxValueDimensionAtCompileTime<DifferenceSpace<In>>, 1>;
774 using Dual = Eigen::AutoDiffScalar<Derivatives>;
776 batchTakesDuals<BatchBody<F>, In, Out, Dual, Rest...>,
777 "a batched function whose Jacobian a method uses is differentiated in dual numbers, so it takes X as "
778 "auto const & and writes Y in X's scalar; or it writes its Jacobians, batched(f, jacobians::perColumn()) "
779 "or batched(f, jacobians::shared())"
781 using DualX = MatrixOf<
784 X::ColsAtCompileTime,
786 X::MaxColsAtCompileTime>;
787 using DualY = MatrixOf<
790 Y::ColsAtCompileTime,
792 Y::MaxColsAtCompileTime>;
794 xd.resize(x.rows(), count);
795 for (Eigen::Index j = 0; j < count; ++j)
797 for (Eigen::Index r = 0; r < x.rows(); ++r)
799 xd(r, j) = Dual{x(r, j), Derivatives::Zero(w)};
802 Eigen::Index offset = 0;
803 [&]<std::size_t... B>(std::index_sequence<B...>) {
806 using Name = std::tuple_element_t<B, Blocks>;
809 BlockRows
const values = inputValueBlockRows<Name>(in);
810 BlockRows
const differences = inputBlockRows<Name>(in);
813 &&
"a block differentiated in a batch is linear, its values its differences"
815 for (Eigen::Index r = 0; r < values.size; ++r)
817 for (Eigen::Index j = 0; j < count; ++j)
819 xd(values.start + r, j).derivatives()(offset + r) = T(1);
822 offset += differences.size;
827 }(std::make_index_sequence<K>{});
829 yd.resize(y.rows(), count);
830 callBatched(
batched, in, out, xd, yd, rest...);
831 using Deviation = MatrixOf<
837 for (Eigen::Index j = 0; j < count; ++j)
839 for (Eigen::Index r = 0; r < y.rows(); ++r)
841 y(r, j) = yd(r, j).value();
843 Deviation
const deviation = [&] {
844 if constexpr (HasMinus<Out>)
846 return Deviation{
Emrys::minus(out, yd.col(j), y.col(j).template cast<Dual>())};
850 return Deviation{yd.col(j)};
853 for (Eigen::Index r = 0; r < m; ++r)
856 if (deviation(r).derivatives().size() > 0)
858 jacobians.block(r, j * w, 1, w) = deviation(r).derivatives().transpose();
862 jacobians.block(r, j * w, 1, w).setZero();
Spaces built from other spaces (decisions.md [sizes-and-dimensions], [several-resizable-blocks]): Com...
How a function that stores a matrix argument takes it (decisions.md [forwarded-matrices]): the concep...
The macros every header includes first.
The transform methods, as policies made by their factories (decisions.md [policy-factories],...
The Space concept and what every continuous space shares (decisions.md [sizes-and-dimensions]): the t...
Tag paths and the index utilities (decisions.md [tag-addressing], [index-names]): TagPath and TagPath...
The output of a batched function (decisions.md [batched-functions]): its values, a column per point,...
Definition Batched.h:342
ValuesType & values()
Definition Batched.h:399
auto jacobian()
Definition Batched.h:482
BatchedOutput(ValuesType values, JacobiansType jacobians, In const &in, std::array< bool, BlockCount > const &wanted)
The output over the values values and the Jacobians' storage jacobians, of the function's input in,...
Definition Batched.h:374
bool wantsJacobians() const
Definition Batched.h:426
Eigen::Map< internal::MatrixOf< Scalar_, valueDimensionAtCompileTime< DifferenceSpace< Out > >, Eigen::Dynamic, maxValueDimensionAtCompileTime< DifferenceSpace< Out > >, Eigen::Dynamic > > JacobiansType
The Jacobians' storage: the blocks wanted side by side, for each column or once.
Definition Batched.h:362
ValuesType const & values() const
Definition Batched.h:406
BatchedOutput< In2, Out2, Scalar_, Mark > renamed(In2 const &in)
Definition Batched.h:499
Eigen::Map< internal::MatrixOf< Scalar_, valueDimensionAtCompileTime< Out >, Eigen::Dynamic, maxValueDimensionAtCompileTime< Out >, Eigen::Dynamic > > ValuesType
The values, a column per point.
Definition Batched.h:354
auto jacobian()
Definition Batched.h:450
bool wantsJacobian() const
Definition Batched.h:416
Scalar_ Scalar
the scalar type
Definition Batched.h:351
auto jacobian(Eigen::Index j)
Definition Batched.h:466
auto jacobian(Eigen::Index j)
Definition Batched.h:435
Whether T is a mark of the Jacobians a batched function writes, made by a factory in Emrys::jacobians...
Definition Batched.h:100
A dense matrix or matrix expression, possibly cv- or reference-qualified: what a const Eigen::MatrixB...
Definition Forwarding.h:39
A type usable as the space of a distribution.
Definition Space.h:186
constexpr Shared shared()
Definition Batched.h:63
constexpr PerColumn perColumn()
Definition Batched.h:53
Batched< std::remove_cvref_t< F > > batched(F &&function)
Definition Batched.h:248
Eigen::Index valueDimension(S const &space)
Definition Space.h:272
auto minus(S const &space, Eigen::MatrixBase< ADerived > const &a, Eigen::MatrixBase< BDerived > const &b)
Definition Space.h:715
Eigen::Index differenceDimension(S const &space)
Definition Space.h:284
constexpr bool isLinear
Whether S is linear: its values and its differences are both plain vectors.
Definition Space.h:321
constexpr int valueDimensionAtCompileTime
The value dimension of Space at compile time, or Eigen::Dynamic if it is resizable.
Definition Space.h:251
constexpr int maxValueDimensionAtCompileTime
The largest valid value dimension of Space, or Eigen::Dynamic if it is unbounded.
Definition Space.h:258
The marks of the Jacobians a batched function writes beside its values (decisions....
Definition Batched.h:33
A function that takes every point at once, as batched() marks it (decisions.md [batched-functions]): ...
Definition Batched.h:116
F FunctionType
the function
Definition Batched.h:120
static constexpr bool GivesJacobians
whether the function writes Jacobians
Definition Batched.h:125
static constexpr bool PerColumn
whether it writes one Jacobian per column
Definition Batched.h:128
Mark MarkType
the Jacobians it writes
Definition Batched.h:122
auto operator()(In const &in, Out const &out, X const &x, Rest const &... rest) const
Definition Batched.h:590
F function
the function
Definition Batched.h:130
The mark that a batched function writes one Jacobian per column, by block of its input: Y....
Definition Batched.h:40
The mark that a batched function writes one Jacobian for every column, by block of its input: Y....
Definition Batched.h:47