tests/test.sh exit code (0 → resolved); the classification below is post-hoc and cannot change it./workspace/quadrature/src/simpson.rs
1 //! Adaptive Simpson quadrature.
2 //!
3 //! This module implements globally-adaptive integration of a real-valued
4 //! function of one real variable over a finite interval `[a, b]` using
5 //! Simpson's rule with recursive interval bisection and local error control.
6 //!
7 //! # Method
8 //!
9 //! Simpson's rule approximates the integral over `[a, b]` using the endpoints
10 //! and the midpoint `m = (a + b) / 2`:
11 //!
12 //! ```text
13 //! S(a, b) = (b - a) / 6 * (f(a) + 4 f(m) + f(b))
14 //! ```
15 //!
16 //! The adaptive scheme compares the coarse estimate `S(a, b)` against the
17 //! refined estimate `S(a, m) + S(m, b)`. For a smooth integrand the two
18 //! agree to fourth order, and the difference yields a Richardson error
19 //! estimate. When
20 //!
21 //! ```text
22 //! |S(a, m) + S(m, b) - S(a, b)| <= 15 * tol_local
23 //! ```
24 //!
25 //! the refined estimate is accepted (with the standard `/15` Richardson
26 //! correction added back); otherwise the interval is bisected and each half
27 //! is integrated to half the local tolerance. This is the classic
28 //! Lyness adaptive Simpson algorithm.
29 //!
30 //! The public entry point is [`integrate`]. The recursive workhorse,
31 //! [`adaptive_simpson_recurse`], is invoked once per top-level call and
32 //! carries the running diagnostics.
33
34 use crate::config::Config;
35 use crate::error::QuadratureError;
36 use crate::outcome::IntegrationOutcome;
37
38 /// Evaluate the integrand at `x`, converting a non-finite result into a hard
39 /// error. Centralising this keeps the recursive code free of repeated checks.
40 #[inline]
41 fn eval<F>(f: &F, x: f64) -> Result<f64, QuadratureError>
42 where
43 F: Fn(f64) -> f64,
44 {
45 let y = f(x);
46 if y.is_finite() {
47 Ok(y)
48 } else {
49 Err(QuadratureError::NonFiniteIntegrand { x, value: y })
50 }
51 }
52
53 /// Plain (non-adaptive) Simpson estimate over `[a, b]` given precomputed
54 /// endpoint and midpoint ordinates.
55 ///
56 /// `fa = f(a)`, `fm = f((a + b) / 2)`, `fb = f(b)`.
57 #[inline]
58 fn simpson(a: f64, b: f64, fa: f64, fm: f64, fb: f64) -> f64 {
59 (b - a) / 6.0 * (fa + 4.0 * fm + fb)
60 }
61
62 /// Mutable state threaded through the recursion to gather diagnostics without
63 /// returning a tuple from every call.
64 struct Accumulator {
65 evaluations: u64,
66 max_depth_reached: u32,
67 converged: bool,
68 }
69
70 /// Integrate `f` over `[a, b]` to the accuracy requested by `config`.
71 ///
72 /// On success returns an [`IntegrationOutcome`] carrying the estimate and
73 /// diagnostics. Returns a [`QuadratureError`] if the bounds are non-finite,
74 /// the configuration is invalid, or the integrand returns a non-finite value.
75 ///
76 /// Reversed bounds (`a > b`) are handled by the usual sign convention:
77 /// `∫_a^b = -∫_b^a`. Degenerate bounds (`a == b`) integrate to exactly `0`.
78 ///
79 /// # Examples
80 ///
81 /// ```
82 /// use quadrature::{integrate, Config};
83 /// // ∫_0^1 x^2 dx = 1/3
84 /// let out = integrate(|x| x * x, 0.0, 1.0, &Config::new()).unwrap();
85 /// assert!((out.value - 1.0 / 3.0).abs() < 1e-9);
86 /// ```
87 pub fn integrate<F>(
88 f: F,
89 a: f64,
90 b: f64,
91 config: &Config,
92 ) -> Result<IntegrationOutcome, QuadratureError>
93 where
94 F: Fn(f64) -> f64,
95 {
96 // TODO(sci-1421): implement the integration driver. Validate the bounds and
97 // config, handle the degenerate (a == b) and reversed-bound (a > b) cases,
98 // seed the initial panel evaluations, drive `adaptive_simpson_recurse`, and
99 // assemble the `IntegrationOutcome`. See the doc comment and the suite.
100 let _ = (&f, a, b, config);
101 todo!("implement the integrate driver (sci-1421)")
102 }
103
104 /// Recursive core of the adaptive Simpson scheme.
105 ///
106 /// Integrates `f` over `[a, b]`, where `fa`, `fm`, `fb` are the already-known
107 /// ordinates at `a`, the midpoint `m = (a + b) / 2`, and `b`, and `whole` is
108 /// the Simpson estimate `simpson(a, b, fa, fm, fb)` for the current interval
109 /// (passed in so it is not recomputed).
110 ///
111 /// Evaluate the two sub-midpoints (via [`eval`], so non-finite values surface as
112 /// errors, incrementing `acc.evaluations`), form the two half-panel Simpson
113 /// estimates, and use the difference between their sum and `whole` as the local
114 /// error proxy. Either accept the panel , applying the standard Richardson
115 /// correction to the returned value and reporting the matching error estimate ,
116 /// or bisect, recursing on `[a, m]` and `[m, b]` with the tolerance apportioned
117 /// in the usual way and combining the children. The depth limit forces
118 /// acceptance; a panel accepted only on the depth limit sets `acc.converged =
119 /// false`, and `acc.max_depth_reached` tracks the depth. Use the standard
120 /// adaptive-Simpson constants for the acceptance threshold, the correction
121 /// factor, and the tolerance apportionment; the behavioural suite pins them.
122 ///
123 /// The two halves reuse `fa, flm, fm` and `fm, frm, fb` respectively, so each
124 /// recursion level performs exactly two new integrand evaluations.
125 ///
126 /// Returns `(value, error_estimate)` for the subinterval `[a, b]`.
127 #[allow(clippy::too_many_arguments)]
128 fn adaptive_simpson_recurse<F>(
129 f: &F,
130 a: f64,
131 b: f64,
132 fa: f64,
133 fm: f64,
134 fb: f64,
135 whole: f64,
136 tol: f64,
137 max_depth: u32,
138 depth: u32,
139 acc: &mut Accumulator,
140 ) -> Result<(f64, f64), QuadratureError>
141 where
142 F: Fn(f64) -> f64,
143 {
144 // TODO(sci-1421): implement the recursive adaptive Simpson step (see doc).
145 let _ = (f, a, b, fa, fm, fb, whole, tol, max_depth, depth, acc);
146 todo!("implement adaptive_simpson_recurse (sci-1421)")
147 }
148
/workspace/quadrature/src/config.rs
1 //! Configuration for an adaptive integration run.
2
3 use crate::error::QuadratureError;
4
5 /// Default absolute error tolerance used by [`Config::new`].
6 pub const DEFAULT_TOLERANCE: f64 = 1e-10;
7
8 /// Default maximum recursion depth used by [`Config::new`].
9 pub const DEFAULT_MAX_DEPTH: u32 = 50;
10
11 /// Tuning parameters for the adaptive Simpson integrator.
12 ///
13 /// A `Config` bundles the convergence criteria for a single integration.
14 /// Construct one with [`Config::new`] (sensible defaults) and refine it with
15 /// the chained setters, e.g.
16 ///
17 /// ```
18 /// use quadrature::Config;
19 /// let cfg = Config::new()
20 /// .with_tolerance(1e-8)
21 /// .with_max_depth(30);
22 /// assert_eq!(cfg.tolerance(), 1e-8);
23 /// ```
24 #[derive(Debug, Clone, Copy, PartialEq)]
25 pub struct Config {
26 tolerance: f64,
27 max_depth: u32,
28 }
29
30 impl Config {
31 /// Create a configuration with the crate defaults
32 /// ([`DEFAULT_TOLERANCE`], [`DEFAULT_MAX_DEPTH`]).
33 pub fn new() -> Self {
34 Self {
35 tolerance: DEFAULT_TOLERANCE,
36 max_depth: DEFAULT_MAX_DEPTH,
37 }
38 }
39
40 /// Set the absolute error tolerance for the whole integral.
41 ///
42 /// Smaller values demand a more accurate result at the cost of more
43 /// integrand evaluations.
44 #[must_use]
45 pub fn with_tolerance(mut self, tolerance: f64) -> Self {
46 self.tolerance = tolerance;
47 self
48 }
49
50 /// Set the maximum recursion depth. Each level can at most double the
51 /// number of subintervals, so this bounds the work performed.
52 #[must_use]
53 pub fn with_max_depth(mut self, max_depth: u32) -> Self {
54 self.max_depth = max_depth;
55 self
56 }
57
58 /// The configured absolute error tolerance.
59 pub fn tolerance(&self) -> f64 {
60 self.tolerance
61 }
62
63 /// The configured maximum recursion depth.
64 pub fn max_depth(&self) -> u32 {
65 self.max_depth
66 }
67
68 /// Validate the configuration, returning an error if any field is out of
69 /// range. Called internally before an integration begins.
70 pub(crate) fn validate(&self) -> Result<(), QuadratureError> {
71 if !self.tolerance.is_finite() || self.tolerance <= 0.0 {
72 return Err(QuadratureError::InvalidTolerance(self.tolerance));
73 }
74 if self.max_depth == 0 {
75 return Err(QuadratureError::InvalidMaxDepth);
76 }
77 Ok(())
78 }
79 }
80
81 impl Default for Config {
82 fn default() -> Self {
83 Self::new()
84 }
85 }
86
1 //! The result type returned by a successful integration.
2
3 /// The outcome of a converged (or depth-limited) integration.
4 ///
5 /// The crate distinguishes hard failures (reported as
6 /// [`QuadratureError`](crate::QuadratureError)) from a numerically completed
7 /// run that may or may not have met its tolerance. This struct carries the
8 /// estimate together with diagnostics describing how it was obtained.
9 #[derive(Debug, Clone, Copy, PartialEq)]
10 #[non_exhaustive]
11 pub struct IntegrationOutcome {
12 /// The estimated value of the definite integral.
13 pub value: f64,
14
15 /// A conservative estimate of the absolute error in [`value`](Self::value),
16 /// derived from the Richardson-style comparison of the coarse and refined
17 /// Simpson estimates.
18 pub error_estimate: f64,
19
20 /// Total number of integrand evaluations performed.
21 pub evaluations: u64,
22
23 /// The deepest recursion level reached during subdivision.
24 pub max_depth_reached: u32,
25
26 /// Whether every subinterval met the local tolerance before the depth
27 /// limit was hit. When `false`, the result is the best available estimate
28 /// but the global tolerance may not be satisfied.
29 pub converged: bool,
30 }
31
32 impl IntegrationOutcome {
33 /// Convenience accessor returning just the integral estimate.
34 ///
35 /// Useful when the caller does not care about diagnostics:
36 ///
37 /// ```
38 /// use quadrature::{integrate, Config};
39 /// let v = integrate(|x| x, 0.0, 1.0, &Config::new()).unwrap().estimate();
40 /// assert!((v - 0.5).abs() < 1e-9);
41 /// ```
42 pub fn estimate(&self) -> f64 {
43 self.value
44 }
45 }
46
1 //! Error types for the quadrature crate.
2
3 use thiserror::Error;
4
5 /// Errors that can arise while configuring or running an integration.
6 ///
7 /// These cover the cases where the requested integration cannot be carried
8 /// out reliably. They are intentionally distinct from a *successful but
9 /// imprecise* result, which is reported through
10 /// [`IntegrationOutcome`](crate::IntegrationOutcome) instead.
11 #[derive(Debug, Error, Clone, PartialEq)]
12 #[non_exhaustive]
13 pub enum QuadratureError {
14 /// One of the integration bounds was not a finite number (it was `NaN`
15 /// or an infinity). Infinite-domain integration is not supported.
16 #[error("integration bound `{name}` must be finite, got {value}")]
17 NonFiniteBound {
18 /// Which bound was offending (`"a"` or `"b"`).
19 name: &'static str,
20 /// The offending value.
21 value: f64,
22 },
23
24 /// The requested absolute tolerance was not a strictly positive,
25 /// finite number.
26 #[error("tolerance must be finite and strictly positive, got {0}")]
27 InvalidTolerance(f64),
28
29 /// The requested recursion depth limit was zero. At least one level of
30 /// subdivision must be permitted.
31 #[error("max_depth must be at least 1, got 0")]
32 InvalidMaxDepth,
33
34 /// The integrand returned a non-finite value (`NaN` or infinity) at the
35 /// given abscissa, so the quadrature cannot proceed.
36 #[error("integrand returned a non-finite value {value} at x = {x}")]
37 NonFiniteIntegrand {
38 /// The point at which the integrand misbehaved.
39 x: f64,
40 /// The non-finite value returned.
41 value: f64,
42 },
43 }
44
1 //! # quadrature
2 //!
3 //! Adaptive numerical integration of one-dimensional definite integrals.
4 //!
5 //! The crate provides a single high-level entry point, [`integrate`], which
6 //! evaluates `∫_a^b f(x) dx` for a finite interval using a globally-adaptive
7 //! Simpson scheme with recursive bisection and local error control. The
8 //! returned [`IntegrationOutcome`] carries both the estimate and diagnostics
9 //! (error estimate, evaluation count, recursion depth, convergence flag).
10 //!
11 //! ```
12 //! use quadrature::{integrate, Config};
13 //! use std::f64::consts::PI;
14 //!
15 //! // ∫_0^pi sin(x) dx = 2
16 //! let out = integrate(f64::sin, 0.0, PI, &Config::new()).unwrap();
17 //! assert!((out.value - 2.0).abs() < 1e-9);
18 //! assert!(out.converged);
19 //! ```
20 //!
21 //! Convergence behaviour is tuned through [`Config`]:
22 //!
23 //! ```
24 //! use quadrature::{integrate, Config};
25 //! let cfg = Config::new().with_tolerance(1e-6).with_max_depth(20);
26 //! let out = integrate(|x| (-x * x).exp(), -3.0, 3.0, &cfg).unwrap();
27 //! assert!(out.value > 1.7 && out.value < 1.8);
28 //! ```
29 //!
30 //! See the [`simpson`] module for a description of the algorithm.
31
32 #![forbid(unsafe_code)]
33 #![warn(missing_docs)]
34
35 mod config;
36 mod error;
37 mod outcome;
38 mod simpson;
39
40 pub use config::{Config, DEFAULT_MAX_DEPTH, DEFAULT_TOLERANCE};
41 pub use error::QuadratureError;
42 pub use outcome::IntegrationOutcome;
43 pub use simpson::integrate;
44
45 /// Crate-level convenience: integrate with the default [`Config`].
46 ///
47 /// Equivalent to `integrate(f, a, b, &Config::new())`.
48 ///
49 /// ```
50 /// use quadrature::integrate_default;
51 /// let out = integrate_default(|x| x * x * x, 0.0, 2.0).unwrap();
52 /// // ∫_0^2 x^3 dx = 4
53 /// assert!((out.value - 4.0).abs() < 1e-9);
54 /// ```
55 pub fn integrate_default<F>(f: F, a: f64, b: f64) -> Result<IntegrationOutcome, QuadratureError>
56 where
57 F: Fn(f64) -> f64,
58 {
59 integrate(f, a, b, &Config::new())
60 }
61
/workspace/quadrature/src/outcome.rs
1 //! Configuration for an adaptive integration run.
2
3 use crate::error::QuadratureError;
4
5 /// Default absolute error tolerance used by [`Config::new`].
6 pub const DEFAULT_TOLERANCE: f64 = 1e-10;
7
8 /// Default maximum recursion depth used by [`Config::new`].
9 pub const DEFAULT_MAX_DEPTH: u32 = 50;
10
11 /// Tuning parameters for the adaptive Simpson integrator.
12 ///
13 /// A `Config` bundles the convergence criteria for a single integration.
14 /// Construct one with [`Config::new`] (sensible defaults) and refine it with
15 /// the chained setters, e.g.
16 ///
17 /// ```
18 /// use quadrature::Config;
19 /// let cfg = Config::new()
20 /// .with_tolerance(1e-8)
21 /// .with_max_depth(30);
22 /// assert_eq!(cfg.tolerance(), 1e-8);
23 /// ```
24 #[derive(Debug, Clone, Copy, PartialEq)]
25 pub struct Config {
26 tolerance: f64,
27 max_depth: u32,
28 }
29
30 impl Config {
31 /// Create a configuration with the crate defaults
32 /// ([`DEFAULT_TOLERANCE`], [`DEFAULT_MAX_DEPTH`]).
33 pub fn new() -> Self {
34 Self {
35 tolerance: DEFAULT_TOLERANCE,
36 max_depth: DEFAULT_MAX_DEPTH,
37 }
38 }
39
40 /// Set the absolute error tolerance for the whole integral.
41 ///
42 /// Smaller values demand a more accurate result at the cost of more
43 /// integrand evaluations.
44 #[must_use]
45 pub fn with_tolerance(mut self, tolerance: f64) -> Self {
46 self.tolerance = tolerance;
47 self
48 }
49
50 /// Set the maximum recursion depth. Each level can at most double the
51 /// number of subintervals, so this bounds the work performed.
52 #[must_use]
53 pub fn with_max_depth(mut self, max_depth: u32) -> Self {
54 self.max_depth = max_depth;
55 self
56 }
57
58 /// The configured absolute error tolerance.
59 pub fn tolerance(&self) -> f64 {
60 self.tolerance
61 }
62
63 /// The configured maximum recursion depth.
64 pub fn max_depth(&self) -> u32 {
65 self.max_depth
66 }
67
68 /// Validate the configuration, returning an error if any field is out of
69 /// range. Called internally before an integration begins.
70 pub(crate) fn validate(&self) -> Result<(), QuadratureError> {
71 if !self.tolerance.is_finite() || self.tolerance <= 0.0 {
72 return Err(QuadratureError::InvalidTolerance(self.tolerance));
73 }
74 if self.max_depth == 0 {
75 return Err(QuadratureError::InvalidMaxDepth);
76 }
77 Ok(())
78 }
79 }
80
81 impl Default for Config {
82 fn default() -> Self {
83 Self::new()
84 }
85 }
86
1 //! The result type returned by a successful integration.
2
3 /// The outcome of a converged (or depth-limited) integration.
4 ///
5 /// The crate distinguishes hard failures (reported as
6 /// [`QuadratureError`](crate::QuadratureError)) from a numerically completed
7 /// run that may or may not have met its tolerance. This struct carries the
8 /// estimate together with diagnostics describing how it was obtained.
9 #[derive(Debug, Clone, Copy, PartialEq)]
10 #[non_exhaustive]
11 pub struct IntegrationOutcome {
12 /// The estimated value of the definite integral.
13 pub value: f64,
14
15 /// A conservative estimate of the absolute error in [`value`](Self::value),
16 /// derived from the Richardson-style comparison of the coarse and refined
17 /// Simpson estimates.
18 pub error_estimate: f64,
19
20 /// Total number of integrand evaluations performed.
21 pub evaluations: u64,
22
23 /// The deepest recursion level reached during subdivision.
24 pub max_depth_reached: u32,
25
26 /// Whether every subinterval met the local tolerance before the depth
27 /// limit was hit. When `false`, the result is the best available estimate
28 /// but the global tolerance may not be satisfied.
29 pub converged: bool,
30 }
31
32 impl IntegrationOutcome {
33 /// Convenience accessor returning just the integral estimate.
34 ///
35 /// Useful when the caller does not care about diagnostics:
36 ///
37 /// ```
38 /// use quadrature::{integrate, Config};
39 /// let v = integrate(|x| x, 0.0, 1.0, &Config::new()).unwrap().estimate();
40 /// assert!((v - 0.5).abs() < 1e-9);
41 /// ```
42 pub fn estimate(&self) -> f64 {
43 self.value
44 }
45 }
46
1 //! Error types for the quadrature crate.
2
3 use thiserror::Error;
4
5 /// Errors that can arise while configuring or running an integration.
6 ///
7 /// These cover the cases where the requested integration cannot be carried
8 /// out reliably. They are intentionally distinct from a *successful but
9 /// imprecise* result, which is reported through
10 /// [`IntegrationOutcome`](crate::IntegrationOutcome) instead.
11 #[derive(Debug, Error, Clone, PartialEq)]
12 #[non_exhaustive]
13 pub enum QuadratureError {
14 /// One of the integration bounds was not a finite number (it was `NaN`
15 /// or an infinity). Infinite-domain integration is not supported.
16 #[error("integration bound `{name}` must be finite, got {value}")]
17 NonFiniteBound {
18 /// Which bound was offending (`"a"` or `"b"`).
19 name: &'static str,
20 /// The offending value.
21 value: f64,
22 },
23
24 /// The requested absolute tolerance was not a strictly positive,
25 /// finite number.
26 #[error("tolerance must be finite and strictly positive, got {0}")]
27 InvalidTolerance(f64),
28
29 /// The requested recursion depth limit was zero. At least one level of
30 /// subdivision must be permitted.
31 #[error("max_depth must be at least 1, got 0")]
32 InvalidMaxDepth,
33
34 /// The integrand returned a non-finite value (`NaN` or infinity) at the
35 /// given abscissa, so the quadrature cannot proceed.
36 #[error("integrand returned a non-finite value {value} at x = {x}")]
37 NonFiniteIntegrand {
38 /// The point at which the integrand misbehaved.
39 x: f64,
40 /// The non-finite value returned.
41 value: f64,
42 },
43 }
44
1 //! # quadrature
2 //!
3 //! Adaptive numerical integration of one-dimensional definite integrals.
4 //!
5 //! The crate provides a single high-level entry point, [`integrate`], which
6 //! evaluates `∫_a^b f(x) dx` for a finite interval using a globally-adaptive
7 //! Simpson scheme with recursive bisection and local error control. The
8 //! returned [`IntegrationOutcome`] carries both the estimate and diagnostics
9 //! (error estimate, evaluation count, recursion depth, convergence flag).
10 //!
11 //! ```
12 //! use quadrature::{integrate, Config};
13 //! use std::f64::consts::PI;
14 //!
15 //! // ∫_0^pi sin(x) dx = 2
16 //! let out = integrate(f64::sin, 0.0, PI, &Config::new()).unwrap();
17 //! assert!((out.value - 2.0).abs() < 1e-9);
18 //! assert!(out.converged);
19 //! ```
20 //!
21 //! Convergence behaviour is tuned through [`Config`]:
22 //!
23 //! ```
24 //! use quadrature::{integrate, Config};
25 //! let cfg = Config::new().with_tolerance(1e-6).with_max_depth(20);
26 //! let out = integrate(|x| (-x * x).exp(), -3.0, 3.0, &cfg).unwrap();
27 //! assert!(out.value > 1.7 && out.value < 1.8);
28 //! ```
29 //!
30 //! See the [`simpson`] module for a description of the algorithm.
31
32 #![forbid(unsafe_code)]
33 #![warn(missing_docs)]
34
35 mod config;
36 mod error;
37 mod outcome;
38 mod simpson;
39
40 pub use config::{Config, DEFAULT_MAX_DEPTH, DEFAULT_TOLERANCE};
41 pub use error::QuadratureError;
42 pub use outcome::IntegrationOutcome;
43 pub use simpson::integrate;
44
45 /// Crate-level convenience: integrate with the default [`Config`].
46 ///
47 /// Equivalent to `integrate(f, a, b, &Config::new())`.
48 ///
49 /// ```
50 /// use quadrature::integrate_default;
51 /// let out = integrate_default(|x| x * x * x, 0.0, 2.0).unwrap();
52 /// // ∫_0^2 x^3 dx = 4
53 /// assert!((out.value - 4.0).abs() < 1e-9);
54 /// ```
55 pub fn integrate_default<F>(f: F, a: f64, b: f64) -> Result<IntegrationOutcome, QuadratureError>
56 where
57 F: Fn(f64) -> f64,
58 {
59 integrate(f, a, b, &Config::new())
60 }
61
/workspace/quadrature/src/error.rs
1 //! Configuration for an adaptive integration run.
2
3 use crate::error::QuadratureError;
4
5 /// Default absolute error tolerance used by [`Config::new`].
6 pub const DEFAULT_TOLERANCE: f64 = 1e-10;
7
8 /// Default maximum recursion depth used by [`Config::new`].
9 pub const DEFAULT_MAX_DEPTH: u32 = 50;
10
11 /// Tuning parameters for the adaptive Simpson integrator.
12 ///
13 /// A `Config` bundles the convergence criteria for a single integration.
14 /// Construct one with [`Config::new`] (sensible defaults) and refine it with
15 /// the chained setters, e.g.
16 ///
17 /// ```
18 /// use quadrature::Config;
19 /// let cfg = Config::new()
20 /// .with_tolerance(1e-8)
21 /// .with_max_depth(30);
22 /// assert_eq!(cfg.tolerance(), 1e-8);
23 /// ```
24 #[derive(Debug, Clone, Copy, PartialEq)]
25 pub struct Config {
26 tolerance: f64,
27 max_depth: u32,
28 }
29
30 impl Config {
31 /// Create a configuration with the crate defaults
32 /// ([`DEFAULT_TOLERANCE`], [`DEFAULT_MAX_DEPTH`]).
33 pub fn new() -> Self {
34 Self {
35 tolerance: DEFAULT_TOLERANCE,
36 max_depth: DEFAULT_MAX_DEPTH,
37 }
38 }
39
40 /// Set the absolute error tolerance for the whole integral.
41 ///
42 /// Smaller values demand a more accurate result at the cost of more
43 /// integrand evaluations.
44 #[must_use]
45 pub fn with_tolerance(mut self, tolerance: f64) -> Self {
46 self.tolerance = tolerance;
47 self
48 }
49
50 /// Set the maximum recursion depth. Each level can at most double the
51 /// number of subintervals, so this bounds the work performed.
52 #[must_use]
53 pub fn with_max_depth(mut self, max_depth: u32) -> Self {
54 self.max_depth = max_depth;
55 self
56 }
57
58 /// The configured absolute error tolerance.
59 pub fn tolerance(&self) -> f64 {
60 self.tolerance
61 }
62
63 /// The configured maximum recursion depth.
64 pub fn max_depth(&self) -> u32 {
65 self.max_depth
66 }
67
68 /// Validate the configuration, returning an error if any field is out of
69 /// range. Called internally before an integration begins.
70 pub(crate) fn validate(&self) -> Result<(), QuadratureError> {
71 if !self.tolerance.is_finite() || self.tolerance <= 0.0 {
72 return Err(QuadratureError::InvalidTolerance(self.tolerance));
73 }
74 if self.max_depth == 0 {
75 return Err(QuadratureError::InvalidMaxDepth);
76 }
77 Ok(())
78 }
79 }
80
81 impl Default for Config {
82 fn default() -> Self {
83 Self::new()
84 }
85 }
86
1 //! The result type returned by a successful integration.
2
3 /// The outcome of a converged (or depth-limited) integration.
4 ///
5 /// The crate distinguishes hard failures (reported as
6 /// [`QuadratureError`](crate::QuadratureError)) from a numerically completed
7 /// run that may or may not have met its tolerance. This struct carries the
8 /// estimate together with diagnostics describing how it was obtained.
9 #[derive(Debug, Clone, Copy, PartialEq)]
10 #[non_exhaustive]
11 pub struct IntegrationOutcome {
12 /// The estimated value of the definite integral.
13 pub value: f64,
14
15 /// A conservative estimate of the absolute error in [`value`](Self::value),
16 /// derived from the Richardson-style comparison of the coarse and refined
17 /// Simpson estimates.
18 pub error_estimate: f64,
19
20 /// Total number of integrand evaluations performed.
21 pub evaluations: u64,
22
23 /// The deepest recursion level reached during subdivision.
24 pub max_depth_reached: u32,
25
26 /// Whether every subinterval met the local tolerance before the depth
27 /// limit was hit. When `false`, the result is the best available estimate
28 /// but the global tolerance may not be satisfied.
29 pub converged: bool,
30 }
31
32 impl IntegrationOutcome {
33 /// Convenience accessor returning just the integral estimate.
34 ///
35 /// Useful when the caller does not care about diagnostics:
36 ///
37 /// ```
38 /// use quadrature::{integrate, Config};
39 /// let v = integrate(|x| x, 0.0, 1.0, &Config::new()).unwrap().estimate();
40 /// assert!((v - 0.5).abs() < 1e-9);
41 /// ```
42 pub fn estimate(&self) -> f64 {
43 self.value
44 }
45 }
46
1 //! Error types for the quadrature crate.
2
3 use thiserror::Error;
4
5 /// Errors that can arise while configuring or running an integration.
6 ///
7 /// These cover the cases where the requested integration cannot be carried
8 /// out reliably. They are intentionally distinct from a *successful but
9 /// imprecise* result, which is reported through
10 /// [`IntegrationOutcome`](crate::IntegrationOutcome) instead.
11 #[derive(Debug, Error, Clone, PartialEq)]
12 #[non_exhaustive]
13 pub enum QuadratureError {
14 /// One of the integration bounds was not a finite number (it was `NaN`
15 /// or an infinity). Infinite-domain integration is not supported.
16 #[error("integration bound `{name}` must be finite, got {value}")]
17 NonFiniteBound {
18 /// Which bound was offending (`"a"` or `"b"`).
19 name: &'static str,
20 /// The offending value.
21 value: f64,
22 },
23
24 /// The requested absolute tolerance was not a strictly positive,
25 /// finite number.
26 #[error("tolerance must be finite and strictly positive, got {0}")]
27 InvalidTolerance(f64),
28
29 /// The requested recursion depth limit was zero. At least one level of
30 /// subdivision must be permitted.
31 #[error("max_depth must be at least 1, got 0")]
32 InvalidMaxDepth,
33
34 /// The integrand returned a non-finite value (`NaN` or infinity) at the
35 /// given abscissa, so the quadrature cannot proceed.
36 #[error("integrand returned a non-finite value {value} at x = {x}")]
37 NonFiniteIntegrand {
38 /// The point at which the integrand misbehaved.
39 x: f64,
40 /// The non-finite value returned.
41 value: f64,
42 },
43 }
44
1 //! # quadrature
2 //!
3 //! Adaptive numerical integration of one-dimensional definite integrals.
4 //!
5 //! The crate provides a single high-level entry point, [`integrate`], which
6 //! evaluates `∫_a^b f(x) dx` for a finite interval using a globally-adaptive
7 //! Simpson scheme with recursive bisection and local error control. The
8 //! returned [`IntegrationOutcome`] carries both the estimate and diagnostics
9 //! (error estimate, evaluation count, recursion depth, convergence flag).
10 //!
11 //! ```
12 //! use quadrature::{integrate, Config};
13 //! use std::f64::consts::PI;
14 //!
15 //! // ∫_0^pi sin(x) dx = 2
16 //! let out = integrate(f64::sin, 0.0, PI, &Config::new()).unwrap();
17 //! assert!((out.value - 2.0).abs() < 1e-9);
18 //! assert!(out.converged);
19 //! ```
20 //!
21 //! Convergence behaviour is tuned through [`Config`]:
22 //!
23 //! ```
24 //! use quadrature::{integrate, Config};
25 //! let cfg = Config::new().with_tolerance(1e-6).with_max_depth(20);
26 //! let out = integrate(|x| (-x * x).exp(), -3.0, 3.0, &cfg).unwrap();
27 //! assert!(out.value > 1.7 && out.value < 1.8);
28 //! ```
29 //!
30 //! See the [`simpson`] module for a description of the algorithm.
31
32 #![forbid(unsafe_code)]
33 #![warn(missing_docs)]
34
35 mod config;
36 mod error;
37 mod outcome;
38 mod simpson;
39
40 pub use config::{Config, DEFAULT_MAX_DEPTH, DEFAULT_TOLERANCE};
41 pub use error::QuadratureError;
42 pub use outcome::IntegrationOutcome;
43 pub use simpson::integrate;
44
45 /// Crate-level convenience: integrate with the default [`Config`].
46 ///
47 /// Equivalent to `integrate(f, a, b, &Config::new())`.
48 ///
49 /// ```
50 /// use quadrature::integrate_default;
51 /// let out = integrate_default(|x| x * x * x, 0.0, 2.0).unwrap();
52 /// // ∫_0^2 x^3 dx = 4
53 /// assert!((out.value - 4.0).abs() < 1e-9);
54 /// ```
55 pub fn integrate_default<F>(f: F, a: f64, b: f64) -> Result<IntegrationOutcome, QuadratureError>
56 where
57 F: Fn(f64) -> f64,
58 {
59 integrate(f, a, b, &Config::new())
60 }
61
/workspace/quadrature/src/lib.rs
1 //! Configuration for an adaptive integration run.
2
3 use crate::error::QuadratureError;
4
5 /// Default absolute error tolerance used by [`Config::new`].
6 pub const DEFAULT_TOLERANCE: f64 = 1e-10;
7
8 /// Default maximum recursion depth used by [`Config::new`].
9 pub const DEFAULT_MAX_DEPTH: u32 = 50;
10
11 /// Tuning parameters for the adaptive Simpson integrator.
12 ///
13 /// A `Config` bundles the convergence criteria for a single integration.
14 /// Construct one with [`Config::new`] (sensible defaults) and refine it with
15 /// the chained setters, e.g.
16 ///
17 /// ```
18 /// use quadrature::Config;
19 /// let cfg = Config::new()
20 /// .with_tolerance(1e-8)
21 /// .with_max_depth(30);
22 /// assert_eq!(cfg.tolerance(), 1e-8);
23 /// ```
24 #[derive(Debug, Clone, Copy, PartialEq)]
25 pub struct Config {
26 tolerance: f64,
27 max_depth: u32,
28 }
29
30 impl Config {
31 /// Create a configuration with the crate defaults
32 /// ([`DEFAULT_TOLERANCE`], [`DEFAULT_MAX_DEPTH`]).
33 pub fn new() -> Self {
34 Self {
35 tolerance: DEFAULT_TOLERANCE,
36 max_depth: DEFAULT_MAX_DEPTH,
37 }
38 }
39
40 /// Set the absolute error tolerance for the whole integral.
41 ///
42 /// Smaller values demand a more accurate result at the cost of more
43 /// integrand evaluations.
44 #[must_use]
45 pub fn with_tolerance(mut self, tolerance: f64) -> Self {
46 self.tolerance = tolerance;
47 self
48 }
49
50 /// Set the maximum recursion depth. Each level can at most double the
51 /// number of subintervals, so this bounds the work performed.
52 #[must_use]
53 pub fn with_max_depth(mut self, max_depth: u32) -> Self {
54 self.max_depth = max_depth;
55 self
56 }
57
58 /// The configured absolute error tolerance.
59 pub fn tolerance(&self) -> f64 {
60 self.tolerance
61 }
62
63 /// The configured maximum recursion depth.
64 pub fn max_depth(&self) -> u32 {
65 self.max_depth
66 }
67
68 /// Validate the configuration, returning an error if any field is out of
69 /// range. Called internally before an integration begins.
70 pub(crate) fn validate(&self) -> Result<(), QuadratureError> {
71 if !self.tolerance.is_finite() || self.tolerance <= 0.0 {
72 return Err(QuadratureError::InvalidTolerance(self.tolerance));
73 }
74 if self.max_depth == 0 {
75 return Err(QuadratureError::InvalidMaxDepth);
76 }
77 Ok(())
78 }
79 }
80
81 impl Default for Config {
82 fn default() -> Self {
83 Self::new()
84 }
85 }
86
1 //! The result type returned by a successful integration.
2
3 /// The outcome of a converged (or depth-limited) integration.
4 ///
5 /// The crate distinguishes hard failures (reported as
6 /// [`QuadratureError`](crate::QuadratureError)) from a numerically completed
7 /// run that may or may not have met its tolerance. This struct carries the
8 /// estimate together with diagnostics describing how it was obtained.
9 #[derive(Debug, Clone, Copy, PartialEq)]
10 #[non_exhaustive]
11 pub struct IntegrationOutcome {
12 /// The estimated value of the definite integral.
13 pub value: f64,
14
15 /// A conservative estimate of the absolute error in [`value`](Self::value),
16 /// derived from the Richardson-style comparison of the coarse and refined
17 /// Simpson estimates.
18 pub error_estimate: f64,
19
20 /// Total number of integrand evaluations performed.
21 pub evaluations: u64,
22
23 /// The deepest recursion level reached during subdivision.
24 pub max_depth_reached: u32,
25
26 /// Whether every subinterval met the local tolerance before the depth
27 /// limit was hit. When `false`, the result is the best available estimate
28 /// but the global tolerance may not be satisfied.
29 pub converged: bool,
30 }
31
32 impl IntegrationOutcome {
33 /// Convenience accessor returning just the integral estimate.
34 ///
35 /// Useful when the caller does not care about diagnostics:
36 ///
37 /// ```
38 /// use quadrature::{integrate, Config};
39 /// let v = integrate(|x| x, 0.0, 1.0, &Config::new()).unwrap().estimate();
40 /// assert!((v - 0.5).abs() < 1e-9);
41 /// ```
42 pub fn estimate(&self) -> f64 {
43 self.value
44 }
45 }
46
1 //! Error types for the quadrature crate.
2
3 use thiserror::Error;
4
5 /// Errors that can arise while configuring or running an integration.
6 ///
7 /// These cover the cases where the requested integration cannot be carried
8 /// out reliably. They are intentionally distinct from a *successful but
9 /// imprecise* result, which is reported through
10 /// [`IntegrationOutcome`](crate::IntegrationOutcome) instead.
11 #[derive(Debug, Error, Clone, PartialEq)]
12 #[non_exhaustive]
13 pub enum QuadratureError {
14 /// One of the integration bounds was not a finite number (it was `NaN`
15 /// or an infinity). Infinite-domain integration is not supported.
16 #[error("integration bound `{name}` must be finite, got {value}")]
17 NonFiniteBound {
18 /// Which bound was offending (`"a"` or `"b"`).
19 name: &'static str,
20 /// The offending value.
21 value: f64,
22 },
23
24 /// The requested absolute tolerance was not a strictly positive,
25 /// finite number.
26 #[error("tolerance must be finite and strictly positive, got {0}")]
27 InvalidTolerance(f64),
28
29 /// The requested recursion depth limit was zero. At least one level of
30 /// subdivision must be permitted.
31 #[error("max_depth must be at least 1, got 0")]
32 InvalidMaxDepth,
33
34 /// The integrand returned a non-finite value (`NaN` or infinity) at the
35 /// given abscissa, so the quadrature cannot proceed.
36 #[error("integrand returned a non-finite value {value} at x = {x}")]
37 NonFiniteIntegrand {
38 /// The point at which the integrand misbehaved.
39 x: f64,
40 /// The non-finite value returned.
41 value: f64,
42 },
43 }
44
1 //! # quadrature
2 //!
3 //! Adaptive numerical integration of one-dimensional definite integrals.
4 //!
5 //! The crate provides a single high-level entry point, [`integrate`], which
6 //! evaluates `∫_a^b f(x) dx` for a finite interval using a globally-adaptive
7 //! Simpson scheme with recursive bisection and local error control. The
8 //! returned [`IntegrationOutcome`] carries both the estimate and diagnostics
9 //! (error estimate, evaluation count, recursion depth, convergence flag).
10 //!
11 //! ```
12 //! use quadrature::{integrate, Config};
13 //! use std::f64::consts::PI;
14 //!
15 //! // ∫_0^pi sin(x) dx = 2
16 //! let out = integrate(f64::sin, 0.0, PI, &Config::new()).unwrap();
17 //! assert!((out.value - 2.0).abs() < 1e-9);
18 //! assert!(out.converged);
19 //! ```
20 //!
21 //! Convergence behaviour is tuned through [`Config`]:
22 //!
23 //! ```
24 //! use quadrature::{integrate, Config};
25 //! let cfg = Config::new().with_tolerance(1e-6).with_max_depth(20);
26 //! let out = integrate(|x| (-x * x).exp(), -3.0, 3.0, &cfg).unwrap();
27 //! assert!(out.value > 1.7 && out.value < 1.8);
28 //! ```
29 //!
30 //! See the [`simpson`] module for a description of the algorithm.
31
32 #![forbid(unsafe_code)]
33 #![warn(missing_docs)]
34
35 mod config;
36 mod error;
37 mod outcome;
38 mod simpson;
39
40 pub use config::{Config, DEFAULT_MAX_DEPTH, DEFAULT_TOLERANCE};
41 pub use error::QuadratureError;
42 pub use outcome::IntegrationOutcome;
43 pub use simpson::integrate;
44
45 /// Crate-level convenience: integrate with the default [`Config`].
46 ///
47 /// Equivalent to `integrate(f, a, b, &Config::new())`.
48 ///
49 /// ```
50 /// use quadrature::integrate_default;
51 /// let out = integrate_default(|x| x * x * x, 0.0, 2.0).unwrap();
52 /// // ∫_0^2 x^3 dx = 4
53 /// assert!((out.value - 4.0).abs() < 1e-9);
54 /// ```
55 pub fn integrate_default<F>(f: F, a: f64, b: f64) -> Result<IntegrationOutcome, QuadratureError>
56 where
57 F: Fn(f64) -> f64,
58 {
59 integrate(f, a, b, &Config::new())
60 }
61
ls tests/ 2>/dev/null && echo "---" && find . -name "*.rs" -path "*test*"
smoke.rs --- ./tests/smoke.rs [stdout] smoke.rs --- ./tests/smoke.rs
/workspace/quadrature/tests/smoke.rs
1 //! Trivial smoke test: the public API integrates one smooth function.
2 //! The full behavioral conformance suite is applied separately by the grader.
3
4 use quadrature::{integrate, integrate_default, Config};
5
6 #[test]
7 fn integrates_a_smooth_function() {
8 let out = integrate(|x: f64| x * x, 0.0, 1.0, &Config::new()).unwrap();
9 assert!((out.value - 1.0 / 3.0).abs() < 1e-9);
10 }
11
12 #[test]
13 fn integrate_default_runs() {
14 let out = integrate_default(|x: f64| x, 0.0, 2.0).unwrap();
15 assert!((out.value - 2.0).abs() < 1e-9);
16 }
17
/workspace/quadrature/src/simpson.rs
// TODO(sci-1421): implement the integration driver. Validate the bounds and
// config, handle the degenerate (a == b) and reversed-bound (a > b) cases,
// seed the initial panel evaluations, drive `adaptive_simpson_recurse`, and
// assemble the `IntegrationOutcome`. See the doc comment and the suite.
let _ = (&f, a, b, config);
todo!("implement the integrate driver (sci-1421)") if !a.is_finite() {
return Err(QuadratureError::NonFiniteBound {
name: "a",
value: a,
});
}
if !b.is_finite() {
return Err(QuadratureError::NonFiniteBound {
name: "b",
value: b,
});
}
config.validate()?;
// Degenerate interval: the integral is exactly zero, no work to do.
if a == b {
return Ok(IntegrationOutcome {
value: 0.0,
error_estimate: 0.0,
evaluations: 0,
max_depth_reached: 0,
converged: true,
});
}
// The recursion always works left-to-right; reversed bounds flip the sign
// of the result via `∫_a^b = -∫_b^a`.
let reversed = a > b;
let (lo, hi) = if reversed { (b, a) } else { (a, b) };
// Seed the top-level panel: three ordinates and the coarse Simpson estimate.
let fa = eval(&f, lo)?;
let fb = eval(&f, hi)?;
let m = 0.5 * (lo + hi);
let fm = eval(&f, m)?;
let whole = simpson(lo, hi, fa, fm, fb);
let mut acc = Accumulator {
evaluations: 3,
max_depth_reached: 0,
converged: true,
};
let (value, error_estimate) = adaptive_simpson_recurse(
&f,
lo,
hi,
fa,
fm,
fb,
whole,
config.tolerance(),
config.max_depth(),
0,
&mut acc,
)?;
Ok(IntegrationOutcome {
value: if reversed { -value } else { value },
error_estimate,
evaluations: acc.evaluations,
max_depth_reached: acc.max_depth_reached,
converged: acc.converged,
})/workspace/quadrature/src/simpson.rs
// TODO(sci-1421): implement the recursive adaptive Simpson step (see doc).
let _ = (f, a, b, fa, fm, fb, whole, tol, max_depth, depth, acc);
todo!("implement adaptive_simpson_recurse (sci-1421)") // Record the deepest panel processed so far.
if depth > acc.max_depth_reached {
acc.max_depth_reached = depth;
}
let m = 0.5 * (a + b);
let lm = 0.5 * (a + m);
let rm = 0.5 * (m + b);
// The only two new ordinates needed at this level.
let flm = eval(f, lm)?;
acc.evaluations += 1;
let frm = eval(f, rm)?;
acc.evaluations += 1;
let left = simpson(a, m, fa, flm, fm);
let right = simpson(m, b, fm, frm, fb);
let refined = left + right;
let diff = refined - whole;
// Richardson: the refined estimate is accurate to ~|diff| / 15, and adding
// back `diff / 15` cancels the leading error term.
let corrected = refined + diff / 15.0;
let error = diff.abs() / 15.0;
// Accept when the local error proxy meets the apportioned tolerance.
if diff.abs() <= 15.0 * tol {
return Ok((corrected, error));
}
// Out of depth: take the best estimate available but flag non-convergence.
if depth >= max_depth {
acc.converged = false;
return Ok((corrected, error));
}
// Bisect: each half is integrated to half the local tolerance, reusing the
// already-known ordinates so no evaluation is repeated.
let (lv, le) =
adaptive_simpson_recurse(f, a, m, fa, flm, fm, left, tol / 2.0, max_depth, depth + 1, acc)?;
let (rv, re) =
adaptive_simpson_recurse(f, m, b, fm, frm, fb, right, tol / 2.0, max_depth, depth + 1, acc)?;
Ok((lv + rv, le + re))cargo test --release 2>&1 | tail -40
Compiling quadrature v0.3.1 (/workspace/quadrature)
Finished `release` profile [optimized] target(s) in 1.23s
Running unittests src/lib.rs (target/release/deps/quadrature-88c215a0312ed4f6)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running tests/smoke.rs (target/release/deps/smoke-bdafb16e3950e68e)
running 2 tests
test integrate_default_runs ... ok
test integrates_a_smooth_function ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests quadrature
running 6 tests
test src/config.rs - config::Config (line 17) ... ok
test src/lib.rs - (line 11) ... ok
test src/lib.rs - integrate_default (line 49) ... ok
test src/lib.rs - (line 23) ... ok
test src/outcome.rs - outcome::IntegrationOutcome::estimate (line 37) ... ok
test src/simpson.rs - simpson::integrate (line 81) ... ok
test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.48s
[stdout]
Compiling quadrature v0.3.1 (/workspace/quadrature)
Finished `release` profile [optimized] target(s) in 1.23s
Running unittests src/lib.rs (target/release/deps/quadrature-88c215a0312ed4f6)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running tests/smoke.rs (target/release/deps/smoke-bdafb16e3950e68e)
running 2 tests
test integrate_default_runs ... ok
test integrates_a_smooth_function ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests quadrature
running 6 tests
test src/config.rs - config::Config (line 17) ... ok
test src/lib.rs - (line 11) ... ok
test src/lib.rs - integrate_default (line 49) ... ok
test src/lib.rs - (line 23) ... ok
test src/outcome.rs - outcome::IntegrationOutcome::estimate (line 37) ... ok
test src/simpson.rs - simpson::integrate (line 81) ... ok
test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.48stouch src/simpson.rs && cargo build --release 2>&1 | grep -iE "warning|error" || echo "No warnings or errors."
No warnings or errors. [stdout] No warnings or errors.
No file changes, the agent left the workspace untouched.
[test] injected hidden conformance suite
[test] running cargo test --release (offline)
Compiling quadrature v0.3.1 (/workspace/quadrature)
Finished `release` profile [optimized] target(s) in 0.74s
Running unittests src/lib.rs (target/release/deps/quadrature-88c215a0312ed4f6)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running tests/conformance.rs (target/release/deps/conformance-64dfa41745ece48a)
running 27 tests
test additive_with_many_splits ... ok
test additive_over_adjacent_intervals ... ok
test convenience_default_matches_explicit_config ... ok
test degenerate_interval_is_zero ... ok
test depth_limit_flags_non_convergence ... ok
test error_estimate_bounds_true_error ... ok
test integrates_constant ... ok
test integrates_cubic_exactly ... ok
test integrates_exponential ... ok
test integrates_linear ... ok
test integrates_gaussian_bell ... ok
test integrates_oscillatory ... ok
test integrates_quartic ... ok
test integrates_reciprocal_to_ln ... ok
test integrates_runge_function ... ok
test integrates_sine_over_half_period ... ok
test negative_domain ... ok
test rejects_non_finite_integrand ... ok
test rejects_non_finite_lower_bound ... ok
test rejects_non_finite_upper_bound ... ok
test rejects_non_positive_tolerance ... ok
test rejects_zero_max_depth ... ok
test reports_evaluations_and_depth ... ok
test reversed_bounds_are_exact_negation ... ok
test reversed_bounds_negate_result ... ok
test very_small_interval ... ok
test tighter_tolerance_costs_more_evaluations ... ok
test result: ok. 27 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running tests/smoke.rs (target/release/deps/smoke-bdafb16e3950e68e)
running 2 tests
test integrates_a_smooth_function ... ok
test integrate_default_runs ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Finished `release` profile [optimized] target(s) in 0.01s
Doc-tests quadrature
running 6 tests
test src/config.rs - config::Config (line 17) ... ok
test src/lib.rs - (line 11) ... ok
test src/lib.rs - integrate_default (line 49) ... ok
test src/lib.rs - (line 23) ... ok
test src/outcome.rs - outcome::IntegrationOutcome::estimate (line 37) ... ok
test src/simpson.rs - simpson::integrate (line 81) ... ok
test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.33s
[test] all tests passed
[test] reward = 1Reproduce this trial: git checkout 2f94510 && PYTHONPATH=src python3 scripts/build_site.py , then open trial/trial_d0d96b55a76f4f23. Re-running the agent live requires EVAL_PLATFORM_ENABLE_OAUTH_SMOKE=1 and is non-deterministic.
Trial trial_d0d96b55a76f4f23 · verifier authoritative; classifier explanatory.