quad_rs/
integrate.rs

1use crate::{
2    AccumulateError, AdaptiveIntegrator, AdaptiveRectangularContourIntegrator, Contour, Integrable,
3    IntegrableFloat, IntegrationError, IntegrationOutput, IntegrationResult, IntegrationState,
4    RescaleError,
5};
6use nalgebra::{ComplexField, RealField};
7use num_traits::FromPrimitive;
8use std::ops::Range;
9use trellis_runner::{GenerateBuilder, Output, TrellisError};
10
11pub struct Integrator<F> {
12    max_iter: usize,
13    relative_tolerance: F,
14    minimum_segment_width: F,
15}
16
17struct Wrapped<P: Integrable, F>
18// where
19//     <P as Integrable>::Input: ComplexField<RealField = P::Input> + Copy + FromPrimitive,
20{
21    integrable: P,
22    float: std::marker::PhantomData<F>,
23}
24
25impl<P: Integrable<Input = F>, F> Integrable for Wrapped<P, F>
26where
27    <P as Integrable>::Output: ComplexField<RealField = F> + Copy,
28    <P as Integrable>::Input: num_traits::Zero,
29{
30    type Input = num_complex::Complex<P::Input>;
31    type Output = P::Output;
32    fn integrand(
33        &self,
34        input: &num_complex::Complex<P::Input>,
35    ) -> Result<Self::Output, crate::EvaluationError<Self::Input>> {
36        let output: P::Output = self
37            .integrable
38            .integrand(&input.re)
39            .map_err(|e| e.into_complex())?;
40        Ok(output)
41    }
42}
43
44impl<F: FromPrimitive> Default for Integrator<F> {
45    fn default() -> Self {
46        Self {
47            max_iter: 1000,
48            relative_tolerance: F::from_f64(1e-10).unwrap(),
49            minimum_segment_width: F::from_f64(1e-10).unwrap(),
50        }
51    }
52}
53
54impl<F: FromPrimitive> Integrator<F> {
55    /// Set the maximum number of function evaluations for the integrator
56    #[must_use]
57    pub fn with_maximum_iter(mut self, maximum_iter: usize) -> Self {
58        self.max_iter = maximum_iter;
59        self
60    }
61
62    // Set the relative tolerance for the integrator
63    #[must_use]
64    pub fn relative_tolerance(mut self, relative_tolerance: F) -> Self {
65        self.relative_tolerance = relative_tolerance;
66        self
67    }
68
69    #[must_use]
70    pub fn minimum_segment_width(mut self, minimum_segment_width: F) -> Self {
71        self.minimum_segment_width = minimum_segment_width;
72        self
73    }
74
75    pub fn integrate<P>(
76        &self,
77        integrable: P,
78        range: Range<P::Input>,
79    ) -> Result<
80        Output<IntegrationResult<P::Input, P::Output>, IntegrationState<P::Input, P::Output, F>>,
81        TrellisError<IntegrationResult<P::Input, P::Output>, IntegrationError<P::Input>>,
82    >
83    where
84        F: IntegrableFloat + RealField + FromPrimitive + RescaleError + AccumulateError<F>,
85        P: Integrable + Send + Sync,
86        <P as Integrable>::Output: IntegrationOutput<Float = F, Scalar = P::Input>,
87        <P as Integrable>::Input: ComplexField<RealField = F> + FromPrimitive + Copy,
88    {
89        let solver = AdaptiveIntegrator::new(
90            range,
91            1000,
92            self.minimum_segment_width,
93            vec![],
94            self.relative_tolerance,
95            self.relative_tolerance,
96        );
97
98        let runner = solver
99            .build_for(integrable)
100            .configure(|state| {
101                state
102                    .max_iters(self.max_iter)
103                    .relative_tolerance(self.relative_tolerance)
104            })
105            .finalise()
106            .unwrap();
107
108        runner.run()
109    }
110
111    pub fn integrate_real_complex<P>(
112        &self,
113        integrable: P,
114        range: Range<P::Input>,
115    ) -> Result<
116        Output<
117            IntegrationResult<num_complex::Complex<P::Input>, P::Output>,
118            IntegrationState<num_complex::Complex<P::Input>, P::Output, F>,
119        >,
120        TrellisError<
121            IntegrationResult<num_complex::Complex<P::Input>, P::Output>,
122            IntegrationError<num_complex::Complex<P::Input>>,
123        >,
124    >
125    where
126        F: IntegrableFloat + RealField + FromPrimitive + RescaleError + AccumulateError<F> + Copy,
127        P: Integrable<Input = F> + Send + Sync,
128        <P as Integrable>::Output: IntegrationOutput<Float = F, Scalar = num_complex::Complex<F>>
129            + ComplexField<RealField = F>
130            + Copy
131            + FromPrimitive,
132    {
133        let wrapped: Wrapped<P, F> = Wrapped {
134            integrable,
135            float: std::marker::PhantomData,
136        };
137
138        let range: Range<num_complex::Complex<P::Input>> =
139            num_complex::Complex::new(range.start, F::zero())
140                ..num_complex::Complex::new(range.end, F::zero());
141
142        self.integrate::<Wrapped<P, F>>(wrapped, range)
143    }
144
145    pub fn contour_integrate<P>(
146        &self,
147        integrable: P,
148        contour: Contour<P::Input>,
149    ) -> Result<
150        Output<IntegrationResult<P::Input, P::Output>, IntegrationState<P::Input, P::Output, F>>,
151        TrellisError<IntegrationResult<P::Input, P::Output>, IntegrationError<P::Input>>,
152    >
153    where
154        P: Integrable + Send + Sync,
155        F: IntegrableFloat + RealField + FromPrimitive + RescaleError + AccumulateError<F>,
156        <P as Integrable>::Output: IntegrationOutput<Float = F, Scalar = P::Input>,
157        <P as Integrable>::Input: ComplexField<RealField = F> + FromPrimitive + Copy,
158    {
159        let solver = AdaptiveRectangularContourIntegrator::new(
160            contour,
161            1000,
162            self.minimum_segment_width,
163            self.relative_tolerance,
164            self.relative_tolerance,
165        );
166
167        let runner = solver
168            .build_for(integrable)
169            .configure(|state| {
170                state
171                    .max_iters(self.max_iter)
172                    .relative_tolerance(self.relative_tolerance)
173            })
174            .finalise()
175            .unwrap();
176
177        runner.run()
178    }
179}
180
181// pub trait Integrate<T, F>
182// where
183//     T: ComplexField + FromPrimitive + Copy,
184//     F: FnMut(T) -> T,
185// {
186//     /// Integrates the target function over the requested range
187//     fn integrate(
188//         &self,
189//         function: F,
190//         range: std::ops::Range<T>,
191//         possible_singularities: Option<Vec<T>>,
192//     ) -> Result<IntegrationResult<T>, IntegrationError<T>>;
193//
194//     /// Integrates the target function over the closed path
195//     /// If possible singularities are provided they must be ordered from
196//     /// start -> end along the contour
197//     fn path_integrate(
198//         &self,
199//         function: F,
200//         path: Contour<T>,
201//     ) -> Result<IntegrationResult<T>, IntegrationError<T>>;
202// }
203//
204// pub trait IntegrationSettings<N>
205// where
206//     N: RealField + FromPrimitive + PartialOrd + Copy,
207// {
208//     /// Set the relative tolerance for the integrator
209//     fn with_relative_tolerance(self, relative_tolerance: N) -> Self;
210//
211//     /// Set the absolute tolerance for the integrator
212//     fn with_absolute_tolerance(self, absolute_tolerance: N) -> Self;
213//
214//     /// Set the maximum number of function evaluations for the integrator
215//     fn with_maximum_function_evaluations(self, maximum_evaluations: usize) -> Self;
216//
217//     /// Set the minimum segment length for the integrator
218//     fn with_minimum_segment_width(self, minimum_segment_width: N) -> Self;
219// }