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{
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 #[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 #[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