// Copyright (C) 2026 Kiyotsugu Arai // SPDX-License-Identifier: LGPL-3.0-or-later // expr_templates.hpp // Expression Templates — ベクトル・行列の遅延評価 // // v1 + v2 + v3 のような連鎖演算で一時オブジェクト生成を排除し、 // 一回のループで全要素を計算する。 // // 使い方: // Vector v = v1 + 2.0 * v2 - v3 / 3.0; // 一時 Vector 生成なし // Matrix m = m1 + m2 - 3.0 * m3; // 一時 Matrix 生成なし // // 注意: auto で式を保持するとダングリング参照の危険がある // auto expr = v1 + v2; // expr は v1, v2 への参照を保持 — 寿命に注意 // Vector v = v1 + v2; // こちらは安全 (即座に評価) #ifndef CALX_EXPR_TEMPLATES_HPP #define CALX_EXPR_TEMPLATES_HPP #include #include #include #include #include #include #include namespace calx { // 式テンプレート型の基底マーカー (スカラー演算子の制約に使用) struct ExprBase {}; // ================================================================ // ベクトル式テンプレート // ================================================================ template struct VecExpr : ExprBase { const Derived& derived() const noexcept { return static_cast(*this); } }; // 単項マイナス template struct VecNeg : VecExpr> { using value_type = typename E::value_type; const E& expr_; explicit VecNeg(const E& e) : expr_(e) {} value_type operator[](std::size_t i) const { return -expr_[i]; } std::size_t size() const { return expr_.size(); } auto packet(std::size_t i) const { return PacketTraits::negate(expr_.packet(i)); } }; // スカラー乗算 template struct VecScale : VecExpr> { using value_type = typename E::value_type; const E& expr_; S scalar_; VecScale(const E& e, S s) : expr_(e), scalar_(s) {} value_type operator[](std::size_t i) const { return static_cast(expr_[i] * scalar_); } std::size_t size() const { return expr_.size(); } auto packet(std::size_t i) const { using PT = PacketTraits; return PT::mul(expr_.packet(i), PT::set1(static_cast(scalar_))); } }; // スカラー除算 (逆数乗算に変換: div は ~20 サイクル、mul は ~4 サイクル) template struct VecScaleDiv : VecExpr> { using value_type = typename E::value_type; const E& expr_; value_type inv_; // 1/scalar を事前計算 VecScaleDiv(const E& e, S s) : expr_(e), inv_(value_type(1) / static_cast(s)) {} value_type operator[](std::size_t i) const { return static_cast(expr_[i] * inv_); } std::size_t size() const { return expr_.size(); } auto packet(std::size_t i) const { using PT = PacketTraits; return PT::mul(expr_.packet(i), PT::set1(inv_)); } }; // VecScale 型判定ヘルパー (FMA 融合用) template struct is_vec_scale : std::false_type {}; template struct is_vec_scale> : std::true_type {}; // 二項演算ノード (加算・減算) // Scale+Add/Sub を検出して FMA に融合: mul+add (8c) → fmadd (4c) template struct VecBinOp : VecExpr> { using value_type = typename L::value_type; const L& lhs_; const R& rhs_; VecBinOp(const L& l, const R& r) : lhs_(l), rhs_(r) { if (l.size() != r.size()) throw DimensionError("Vector expression: size mismatch"); } value_type operator[](std::size_t i) const { return Op{}(lhs_[i], rhs_[i]); } std::size_t size() const { return lhs_.size(); } // SIMD パケット評価 — Scale+Add/Sub を FMA に融合 auto packet(std::size_t i) const { using PT = PacketTraits; if constexpr (std::is_same_v> && is_vec_scale::value) { // s*a + b → fmadd(a, s, b) return PT::fmadd(lhs_.expr_.packet(i), PT::set1(static_cast(lhs_.scalar_)), rhs_.packet(i)); } else if constexpr (std::is_same_v> && is_vec_scale::value) { // a + s*b → fmadd(b, s, a) return PT::fmadd(rhs_.expr_.packet(i), PT::set1(static_cast(rhs_.scalar_)), lhs_.packet(i)); } else if constexpr (std::is_same_v> && is_vec_scale::value) { // a - s*b → fmadd(b, -s, a) return PT::fmadd(rhs_.expr_.packet(i), PT::negate(PT::set1(static_cast(rhs_.scalar_))), lhs_.packet(i)); } else { return PacketOp::apply(lhs_.packet(i), rhs_.packet(i)); } } }; // ---- ベクトル演算子 ---- template VecBinOp> operator+(const VecExpr& a, const VecExpr& b) { return {a.derived(), b.derived()}; } template VecBinOp> operator-(const VecExpr& a, const VecExpr& b) { return {a.derived(), b.derived()}; } template VecNeg operator-(const VecExpr& e) { return VecNeg{e.derived()}; } template requires (!std::derived_from, ExprBase>) VecScale operator*(const VecExpr& e, const S& s) { return {e.derived(), s}; } template requires (!std::derived_from, ExprBase>) VecScale operator*(const S& s, const VecExpr& e) { return {e.derived(), s}; } template requires (!std::derived_from, ExprBase>) VecScaleDiv operator/(const VecExpr& e, const S& s) { if (s == S{0}) throw std::invalid_argument("Division by zero"); return {e.derived(), s}; } // ================================================================ // 行列式テンプレート (要素単位演算のみ) // 行列乗算 (matrix * matrix, matrix * vector) は遅延評価しない // ================================================================ template struct MatExpr : ExprBase { const Derived& derived() const noexcept { return static_cast(*this); } }; // 二項演算ノード (加算・減算) template struct MatBinOp : MatExpr> { using value_type = typename L::value_type; const L& lhs_; const R& rhs_; MatBinOp(const L& l, const R& r) : lhs_(l), rhs_(r) { if (l.rows() != r.rows() || l.cols() != r.cols()) throw DimensionError("Matrix expression: dimension mismatch"); } value_type operator()(std::size_t i, std::size_t j) const { return Op{}(lhs_(i, j), rhs_(i, j)); } std::size_t rows() const { return lhs_.rows(); } std::size_t cols() const { return lhs_.cols(); } // SIMD パケット評価 (線形インデックス) auto packet(std::size_t idx) const { return PacketOp::apply(lhs_.packet(idx), rhs_.packet(idx)); } }; // 単項マイナス template struct MatNeg : MatExpr> { using value_type = typename E::value_type; const E& expr_; explicit MatNeg(const E& e) : expr_(e) {} value_type operator()(std::size_t i, std::size_t j) const { return -expr_(i, j); } std::size_t rows() const { return expr_.rows(); } std::size_t cols() const { return expr_.cols(); } auto packet(std::size_t idx) const { return PacketTraits::negate(expr_.packet(idx)); } }; // スカラー乗算 template struct MatScale : MatExpr> { using value_type = typename E::value_type; const E& expr_; S scalar_; MatScale(const E& e, S s) : expr_(e), scalar_(s) {} value_type operator()(std::size_t i, std::size_t j) const { return static_cast(expr_(i, j) * scalar_); } std::size_t rows() const { return expr_.rows(); } std::size_t cols() const { return expr_.cols(); } auto packet(std::size_t idx) const { using PT = PacketTraits; return PT::mul(expr_.packet(idx), PT::set1(static_cast(scalar_))); } }; // スカラー除算 (逆数乗算に変換) template struct MatScaleDiv : MatExpr> { using value_type = typename E::value_type; const E& expr_; value_type inv_; MatScaleDiv(const E& e, S s) : expr_(e), inv_(value_type(1) / static_cast(s)) {} value_type operator()(std::size_t i, std::size_t j) const { return static_cast(expr_(i, j) * inv_); } std::size_t rows() const { return expr_.rows(); } std::size_t cols() const { return expr_.cols(); } auto packet(std::size_t idx) const { using PT = PacketTraits; return PT::mul(expr_.packet(idx), PT::set1(inv_)); } }; // ---- 行列演算子 (要素単位) ---- template MatBinOp> operator+(const MatExpr& a, const MatExpr& b) { return {a.derived(), b.derived()}; } template MatBinOp> operator-(const MatExpr& a, const MatExpr& b) { return {a.derived(), b.derived()}; } template MatNeg operator-(const MatExpr& e) { return MatNeg{e.derived()}; } template requires (!std::derived_from, ExprBase>) MatScale operator*(const MatExpr& e, const S& s) { return {e.derived(), s}; } template requires (!std::derived_from, ExprBase>) MatScale operator*(const S& s, const MatExpr& e) { return {e.derived(), s}; } template requires (!std::derived_from, ExprBase>) MatScaleDiv operator/(const MatExpr& e, const S& s) { if (s == S{0}) throw std::invalid_argument("Division by zero"); return {e.derived(), s}; } } // namespace calx #endif // CALX_EXPR_TEMPLATES_HPP