From 482f3cbaa21b2b91987455b147f9a5478427d889 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Sat, 5 Sep 2026 08:05:03 -0400 Subject: [PATCH] Real part comparisons --- Grid/lattice/Lattice_comparison.h | 63 +++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/Grid/lattice/Lattice_comparison.h b/Grid/lattice/Lattice_comparison.h index 91d683da8..d1067a968 100644 --- a/Grid/lattice/Lattice_comparison.h +++ b/Grid/lattice/Lattice_comparison.h @@ -175,5 +175,68 @@ template inline Lattice > operator != (const lobj & lhs, const Lattice & rhs) { return SLComparison(vne(),lhs,rhs); } + +////////////////////////////////////////////////////////////////////////// +// Real-part relational comparison for COMPLEX lattices. +// +// Complex has no ordering, so operator<,>,<=,>= are (deliberately) undefined for +// complex operands -- Comparison() in Lattice_comparison_utils.h is IfNotComplex -- +// and where() cannot be driven by a complex lattice directly. It is however often +// useful to threshold on the REAL PART of a complex (scalar/singlet) field, e.g. a +// momentum-magnitude mask phat^2 > pc^2. These free functions compare the real part +// to a real threshold and return the matching IntegerPredicate, so the result feeds +// where() exactly like the built-in relationals. +// +// Written with the per-lane getlane/putlane accessors (as in FFT.h, PaddedCell.h), +// not extract/merge buffers. One wrinkle: the predicate type IntegerPredicate +// is vInteger, whose Nsimd (>= the widest real type's) exceeds the complex operand's +// Nsimd by s = Npred/Nsimd. where() only reads the ii=0 representative of each group, +// at physical lane lane*s (the "s-fold skip" -- cf. extract()'s getlane(i*s)), so +// filling the rest of the group is not functionally required; we replicate the value +// across all s lanes anyway because some Grid code asserts the s replicas are equal +// (and it mirrors what merge() does). When vInteger is reworked to carry the operand +// Nsimd (scope later) s becomes 1 and the inner loop drops out. +////////////////////////////////////////////////////////////////////////// +template class sRealLt { public: + accelerator_inline Integer operator()(const scalar &a, RealD b) const { return a.real() < b ? 1 : 0; } }; +template class sRealLe { public: + accelerator_inline Integer operator()(const scalar &a, RealD b) const { return a.real() <= b ? 1 : 0; } }; +template class sRealGt { public: + accelerator_inline Integer operator()(const scalar &a, RealD b) const { return a.real() > b ? 1 : 0; } }; +template class sRealGe { public: + accelerator_inline Integer operator()(const scalar &a, RealD b) const { return a.real() >= b ? 1 : 0; } }; + +template +inline Lattice > RealPartComparison(sfunctor op,const Lattice &lhs, RealD thr) +{ + Lattice > ret(lhs.Grid()); + autoView( lv, lhs, AcceleratorRead); + autoView( rv, ret, AcceleratorWrite); + typedef typename CComplex::vector_type vsimd; + const int Nsimd = vsimd::Nsimd(); + const int s = IntegerPredicate::Nsimd() / Nsimd; // lane-replication factor (see note) + accelerator_for(ss, lhs.Grid()->oSites(), Nsimd, { + vsimd v = TensorRemove(lv[ss]); // strip iScalar nest to the bare complex SIMD word +#ifdef GRID_SIMT + { int lane = acceleratorSIMTlane(Nsimd); // GPU: this thread == this operand lane +#else + for(int lane=0;lane inline Lattice > RealPartLessThan (const Lattice &a, RealD b){ return RealPartComparison(sRealLt(),a,b); } +template inline Lattice > RealPartLessEqual (const Lattice &a, RealD b){ return RealPartComparison(sRealLe(),a,b); } +template inline Lattice > RealPartGreaterThan (const Lattice &a, RealD b){ return RealPartComparison(sRealGt(),a,b); } +template inline Lattice > RealPartGreaterEqual (const Lattice &a, RealD b){ return RealPartComparison(sRealGe(),a,b); } + NAMESPACE_END(Grid); #endif