Skip to main content

quad_rs/
integrable.rs

1//! Integrand abstractions.
2//!
3//! This module defines the traits required for a function to be integrated by
4//! the numerical integration routines provided by this crate.
5//!
6//! An [`Integrable`] object maps an input value from the integration domain to
7//! an output value. The input domain may be real or complex, allowing both
8//! ordinary quadrature and contour integration to be expressed using the same
9//! interface.
10//!
11//! The output may be a scalar, vector, matrix, or other structure implementing
12//! [`IntegrationOutput`].
13//!
14//! # Real integration
15//!
16//! ```text
17//! f : ℝ → ℝ
18//! f : ℝ → ℂ
19//! f : ℝ → Vector
20//! ```
21//!
22//! # Contour integration
23//!
24//! ```text
25//! f : ℂ → ℂ
26//! f : ℂ → Vector
27//! ```
28//!
29//! The integrator operates entirely in terms of these abstractions and does not
30//! require any knowledge of the concrete output type beyond the operations
31//! provided by [`IntegrationOutput`].
32
33use nalgebra::ComplexField;
34use num_complex::Complex;
35use num_traits::{Float, FromPrimitive};
36use std::ops::{AddAssign, SubAssign};
37use trellis_runner::TrellisFloat;
38
39/// Function-like object that can be numerically integrated.
40///
41/// An `Integrable` defines:
42///
43/// - the scalar type used internally by the integrator (`Float`),
44/// - the input domain (`Input`),
45/// - the output type (`Output`),
46///
47/// together with a method for evaluating the integrand.
48///
49/// # Associated types
50///
51/// - [`Float`](Self::Float): underlying floating-point type.
52/// - [`Input`](Self::Input): integration domain. This may be real or complex.
53/// - [`Output`](Self::Output): value returned by the integrand.
54///
55/// # Examples
56///
57/// A real-valued function:
58///
59/// ```text
60/// f : ℝ → ℝ
61/// ```
62///
63/// A contour integrand:
64///
65/// ```text
66/// f : ℂ → ℂ
67/// ```
68///
69/// A vector-valued integrand:
70///
71/// ```text
72/// f : ℝ → ℝⁿ
73/// ```
74pub trait Integrable {
75    /// Underlying floating-point type used by the integrator.
76    type Float: IntegrableFloat;
77
78    /// Input domain of the integrand.
79    ///
80    /// This may be a real scalar such as `f64` or a complex scalar used for
81    /// contour integration.
82    type Input: ComplexField<RealField = Self::Float> + Copy;
83
84    /// Output of the integrand.
85    ///
86    /// This may be a scalar, complex scalar, vector, matrix, or other type
87    /// implementing [`IntegrationOutput`].
88    type Output: Clone; //: IntegrationOutput<Self::Input, Float = Self::Float>;
89
90    /// Evaluates the integrand at `input`.
91    ///
92    /// This method performs no validation and may return non-finite values.
93    /// Integrators should generally call
94    /// [`checked_integrand`](Self::checked_integrand) instead unless they
95    /// explicitly wish to handle invalid values themselves.
96    fn integrand(&self, input: &Self::Input) -> Self::Output;
97}
98
99/// Floating-point type supported by the integration routines.
100///
101/// This trait bundles together the numerical functionality required by the
102/// integration algorithms. It is primarily an implementation detail used to
103/// restrict the supported scalar types.
104///
105/// Currently the crate supports:
106///
107/// - `f32`
108/// - `f64`
109pub trait IntegrableFloat:
110    ComplexScalar + Float + FromPrimitive + AddAssign + SubAssign + TrellisFloat + Send + Sync + 'static
111{
112}
113
114impl IntegrableFloat for f32 {}
115impl IntegrableFloat for f64 {}
116
117/// Floating-point type with an associated complex scalar type.
118///
119/// This trait is used by complex contour pieces. It connects a real scalar
120/// type, such as `f64`, to the corresponding complex type,
121/// such as `Complex<f64>`.
122///
123/// It also provides a constructor for complex values from real and imaginary
124/// parts, avoiding repeated low-level bounds throughout the contour
125/// implementation.
126pub trait ComplexScalar: Float {
127    type Complex: ComplexField<RealField = Self> + Copy;
128
129    fn complex(re: Self, im: Self) -> Self::Complex;
130}
131
132impl ComplexScalar for f32 {
133    type Complex = Complex<f32>;
134
135    fn complex(re: Self, im: Self) -> Self::Complex {
136        num_complex::Complex::new(re, im)
137    }
138}
139
140impl ComplexScalar for f64 {
141    type Complex = Complex<f64>;
142
143    fn complex(re: Self, im: Self) -> Self::Complex {
144        Complex::new(re, im)
145    }
146}