Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
elliptic_theta.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "math.hpp"
4#include <cstddef>
5#include <limits>
6#include <stdexcept>
7
8namespace uni20
9{
14template <class R> struct theta_constant_result
15{
18};
19
20namespace detail
21{
22template <class R>
23concept ThetaReal = native_math::NativeReal<R>
24#if UNI20_HAS_MPREAL
25 || std::same_as<R, mpreal>
26#endif
27 ;
28
29template <ThetaReal R> theta_constant_result<R> theta_constant_series(unsigned kind, R const& t, std::size_t max_terms)
30{
31 auto const p = precision_of(t);
32 auto number = [p](auto n) { return scalar_at_precision<R>(n, p); };
33 R const zero = number(0), one = number(1), two = number(2);
34 R const pi_value = uni20::pi<R> * one;
35 bool const dual = t < one;
36 // S-transform: theta2 <-> theta4, theta3 -> theta3, with factor 1/sqrt(t).
37 unsigned const series_kind = dual && kind != 3 ? 6 - kind : kind;
38 R scale = dual ? -math::log(t) / two : zero;
39 if (series_kind == 2)
40 {
41 // Divide pi first: pi*t can overflow while pi*t/4 is representable.
42 R const leading = dual ? (pi_value / number(4)) / t : (pi_value / number(4)) * t;
43 scale -= leading;
44 }
45 if (!uni20::isfinite(scale)) throw std::overflow_error("theta constant logarithmic scale exceeds scalar range");
46
47 // An infinite decay is harmless: every nonconstant series term then rounds
48 // to zero. Never form exp(+decay) or subtract nearly equal theta constants.
49 R const decay = dual ? pi_value / t : pi_value * t;
50 R const target = numeric_limits<R>::epsilon(t) / number(16);
51 if (!(target > zero)) throw std::underflow_error("theta constant series tolerance exceeds scalar range");
52 R sum = series_kind == 2 ? two : one, correction = zero, n = one;
53 for (std::size_t index = 0; index < max_terms; ++index)
54 {
55 R const power = series_kind == 2 ? n * (n + one) : n * n;
56 R const term = two * math::exp(-decay * power);
57 // In the reduced domain t>=1 successive absolute terms have ratio at
58 // most exp(-3*pi)<1/2. The entire omitted tail is less than twice term.
59 // This bounds truncation in exact arithmetic, not floating-point rounding.
60 if (term <= target) return {sum + correction, scale};
61 R const signed_term = series_kind == 4 && index % 2 == 0 ? -term : term;
62 R const next = sum + signed_term;
63 correction += (sum - next) + signed_term; // |sum| > |signed_term|.
64 sum = next;
65 n += one;
66 }
67 throw std::runtime_error("theta constant series exhausted its term budget");
68}
69} // namespace detail
70
81template <detail::ThetaReal R>
82theta_constant_result<R> elliptic_theta_constant(unsigned kind, R const& t, std::size_t max_terms = 4096)
83{
84 if (kind < 2 || kind > 4 || max_terms == 0)
85 throw std::invalid_argument("theta constant requires kind 2..4 and a positive term budget");
86 auto const p = precision_of(t);
87 if (p.is_exact()) throw std::invalid_argument("theta constant requires explicit finite precision");
88 if (!uni20::isfinite(t) || !(t > 0)) throw std::invalid_argument("theta constant requires finite t>0");
89#if UNI20_HAS_MPREAL
90 if constexpr (std::same_as<R, mpreal>)
91 {
92 if (p.bit_count() > std::numeric_limits<Precision::bit_count_type>::max() - 32)
93 throw std::overflow_error("theta constant guard precision exceeds supported range");
94 auto const work = Precision::bits(p.bit_count() + 32);
95 auto result = detail::theta_constant_series(kind, t.at(work), max_terms);
96 theta_constant_result<R> rounded{result.scaled_value.at(p), result.log_scale.at(p)};
97 if (!uni20::isfinite(rounded.log_scale))
98 throw std::overflow_error("theta constant logarithmic scale exceeds result range");
99 return rounded;
100 }
101 else
102#endif
103 return detail::theta_constant_series(kind, t, max_terms);
104}
105
109template <detail::ThetaReal R>
110theta_constant_result<R> elliptic_theta_constant(unsigned kind, R const& t, Precision p, std::size_t max_terms = 4096)
111{
112 if (p.is_exact()) throw std::invalid_argument("theta constant requires explicit finite precision");
113 return elliptic_theta_constant(kind, scalar_at_precision<R>(t, p), max_terms);
114}
115} // namespace uni20
Exact arithmetic or an explicit binary working precision.
Definition core/precision.hpp:13
static constexpr Precision bits(bit_count_type count)
Select a positive binary significand size.
Definition core/precision.hpp:21
constexpr bool isfinite(I const &x) noexcept
Returns whether an integer scalar is finite.
Definition math.hpp:121
Scalar math helper utilities.
constexpr detail::scalar_math::exp_fn exp
Natural exponential with scalar-specific precision handling.
Definition math.hpp:465
constexpr detail::scalar_math::log_fn log
Natural logarithm with scalar-specific precision handling.
Definition math.hpp:467
Definition data_table.cpp:8
Definition async/async.hpp:26
void sum(BackendSelector selector, async::Async< OutputTensor > &output, async::Async< InputTensor > const &input)
Schedule a full sum into an explicit async scalar tensor.
Definition linalg/async/reductions.hpp:309
constexpr Precision precision_of(T const &value)
Read scalar precision without changing any scalar or provider state.
Definition runtime_precision.hpp:53
auto dual(SpaceType const &space) -> Dual< std::remove_cvref_t< SpaceType > >
Construct an explicitly dual basis without changing occurrence order.
Definition dual_space.hpp:244
void scale(Tensor &tensor, Scalar factor)
Multiply every stored numerical block by a scalar in place.
Definition block_tensor_linear.hpp:400
theta_constant_result< R > elliptic_theta_constant(unsigned kind, R const &t, std::size_t max_terms=4096)
Evaluate theta_kind(0 | i*t), kind=2,3,4 and finite t>0, in scaled form.
Definition elliptic_theta.hpp:82
Project-level numeric limits customization point.
Definition numeric_limits.hpp:16
A positive theta constant represented as scaled_value * exp(log_scale).
Definition elliptic_theta.hpp:15
R log_scale
Definition elliptic_theta.hpp:17
R scaled_value
Definition elliptic_theta.hpp:16