Skip to content

Commit 901158f

Browse files
committed
Simplify some of the const useage
Thanks to a github comment on an issue I was running into with consts it turns out we can simplify things more which is great: rust-lang/rust#60551 (comment) I will look at simplifying things further now that my testing has also shown that not all the additional const bounds are needed for various functions if the compiler can deduce them.
1 parent da4dd95 commit 901158f

File tree

4 files changed

+72
-48
lines changed

4 files changed

+72
-48
lines changed

benches/solver_bench.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ extern crate helix_snail;
66
extern crate num_traits as libnum;
77

88
use divan::black_box;
9+
#[cfg(feature = "linear_algebra")]
910
use helix_snail::linear_algebra::math::*;
1011
use helix_snail::nonlinear_solver::*;
1112
use log::{error, info};
@@ -20,11 +21,17 @@ where
2021
pub logging_level: i32,
2122
}
2223

23-
impl<F> NonlinearProblem<F> for Broyden<F>
24+
impl<F> NonlinearSystemSize for Broyden<F>
2425
where
2526
F: helix_snail::FloatType,
2627
{
2728
const NDIM: usize = 8;
29+
}
30+
31+
impl<F> NonlinearProblem<F> for Broyden<F>
32+
where
33+
F: helix_snail::FloatType,
34+
{
2835
fn compute_resid_jacobian<const NDIM: usize>(
2936
&mut self,
3037
x: &[F],
@@ -114,7 +121,7 @@ mod nonlinear_solver {
114121
};
115122

116123
let mut solver =
117-
TrustRegionDoglegSolver::<{ Broyden::<f64>::NDIM }, f64, Broyden<f64>>::new(
124+
TrustRegionDoglegSolver::<f64, Broyden<f64>>::new(
118125
&dc,
119126
&mut broyden,
120127
);
@@ -154,7 +161,7 @@ mod nonlinear_solver {
154161
};
155162

156163
let mut solver =
157-
TrustRegionDoglegSolver::<{ Broyden::<f32>::NDIM }, f32, Broyden<f32>>::new(
164+
TrustRegionDoglegSolver::<f32, Broyden<f32>>::new(
158165
&dc,
159166
&mut broyden,
160167
);
@@ -186,6 +193,7 @@ mod nonlinear_solver {
186193
sample_size = 500,
187194
sample_count = 1000,
188195
)]
196+
#[cfg(feature = "linear_algebra")]
189197
mod math {
190198
use crate::*;
191199
const NDIM: usize = 12;

src/nonlinear_solver/mod.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,17 @@ pub use self::delta_control::*;
99
pub use self::dogleg::*;
1010
pub use self::tr_dogleg_solver::*;
1111

12+
pub trait NonlinearSystemSize {
13+
/// Size of nonlinear system of equations which should be consistent with nonlinear problem
14+
const NDIM: usize;
15+
}
16+
1217
/// Nonlinear Solver trait which contains functions that should be shared between solvers. These solvers currently
1318
/// expect a square system of equations in order to work.
14-
pub trait NonlinearSolver<F>
19+
pub trait NonlinearSolver<F> : NonlinearSystemSize
1520
where
1621
F: crate::FloatType,
1722
{
18-
/// Size of nonlinear system of equations which should be consistent with nonlinear problem
19-
const NDIM: usize;
2023
/// Values required to setup solver
2124
///
2225
/// # Arguments
@@ -75,13 +78,10 @@ where
7578
}
7679

7780
/// Nonlinear problems must implement the following trait in-order to be useable within this crates solvers
78-
pub trait NonlinearProblem<F>
81+
pub trait NonlinearProblem<F> : NonlinearSystemSize
7982
where
8083
F: crate::FloatType,
8184
{
82-
/// Dimension of the nonlinear system of equations
83-
const NDIM: usize;
84-
8585
/// This function at a minimum computes the residual / function evaluation of the system of nonlinear equations
8686
/// that we are solving for. It is expected that fcn_eval and opt_jacobian have been scaled such that the solution
8787
/// variable x nominally remains in the neighborhood of [-1, 1] as this provides better numerical stability of

src/nonlinear_solver/tr_dogleg_solver.rs

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@ use log::info;
1010
/// This nonlinear solver makes use of a model trust-region method that makes use of a dogleg solver
1111
/// for the sub-problem of the nonlinear problem. It reduces down to taking a full newton raphson step
1212
/// when a given step is near the solution.
13-
pub struct TrustRegionDoglegSolver<'a, const NP_NDIM: usize, F, NP>
13+
pub struct TrustRegionDoglegSolver<'a, F, NP>
1414
where
1515
F: crate::FloatType,
16-
NP: NonlinearProblem<F>,
16+
NP: NonlinearProblem<F> + Sized,
17+
[f64; NP::NDIM]: Sized,
1718
{
1819
/// The field we're solving for. Although, we typically are solving for a scaled version of this in order to have
1920
/// a numerically stable system of equations.
20-
pub x: [F; NP_NDIM],
21+
pub x: [F; NP::NDIM],
2122
/// This controls the step size that our solver takes while iterating for a solution
2223
delta_control: &'a TrustRegionDeltaControl<F>,
2324
/// The total number of function evaluations our solver took
@@ -44,13 +45,14 @@ where
4445
logging_level: i32,
4546
}
4647

47-
impl<'a, const NP_NDIM: usize, F, NP> TrustRegionDoglegSolver<'a, NP_NDIM, F, NP>
48+
impl<'a, F, NP> TrustRegionDoglegSolver<'a, F, NP>
4849
where
4950
F: crate::FloatType,
5051
NP: NonlinearProblem<F>,
52+
[f64; NP::NDIM]: Sized,
5153
{
5254
/// The size of the jacobian
53-
const NDIM2: usize = NP_NDIM * NP_NDIM;
55+
const NDIM2: usize = NP::NDIM * NP::NDIM;
5456

5557
/// Creates a new solver with default values for a number of fields when provided the delta control
5658
/// and the nonlinear problem structure
@@ -64,14 +66,14 @@ where
6466
pub fn new(
6567
delta_control: &'a TrustRegionDeltaControl<F>,
6668
crj: &'a mut NP,
67-
) -> TrustRegionDoglegSolver<'a, { NP_NDIM }, F, NP> {
68-
TrustRegionDoglegSolver::<'a, { NP_NDIM }, F, NP> {
69-
x: [F::zero(); NP_NDIM],
69+
) -> TrustRegionDoglegSolver<'a, F, NP> {
70+
TrustRegionDoglegSolver::<'a, F, NP> {
71+
x: [F::zero(); NP::NDIM],
7072
delta_control,
7173
function_evals: 0,
7274
jacobian_evals: 0,
7375
num_iterations: 0,
74-
max_iterations: NP_NDIM * 1000,
76+
max_iterations: NP::NDIM * 1000,
7577
solution_tolerance: F::from(1e-12).unwrap(),
7678
l2_error: -F::one(),
7779
delta: F::from(1e8).unwrap(),
@@ -86,37 +88,45 @@ where
8688
fn compute_newton_step(
8789
&self,
8890
residual: &[F],
89-
jacobian: &mut [[F; NP_NDIM]],
91+
jacobian: &mut [[F; NP::NDIM]],
9092
newton_step: &mut [F],
9193
) -> Result<(), crate::helix_error::SolverError>
9294
where
93-
[F; NP_NDIM + 1]: Sized,
95+
[F; NP::NDIM + 1]: Sized,
9496
{
95-
lup_solver::<{ NP_NDIM }, F>(residual, jacobian, newton_step)?;
96-
for item in newton_step.iter_mut().take(NP_NDIM) {
97+
lup_solver::<{ NP::NDIM }, F>(residual, jacobian, newton_step)?;
98+
for item in newton_step.iter_mut().take(NP::NDIM) {
9799
*item *= -F::one();
98100
}
99101
Ok(())
100102
}
101103

102104
/// Rejects the current iterations solution and returns the solution to its previous value
103105
fn reject(&mut self, delta_x: &[F]) {
104-
assert!(delta_x.len() >= NP_NDIM);
106+
assert!(delta_x.len() >= NP::NDIM);
105107

106-
for (i_x, item) in delta_x.iter().enumerate().take(NP_NDIM) {
108+
for (i_x, item) in delta_x.iter().enumerate().take(NP::NDIM) {
107109
self.x[i_x] -= *item;
108110
}
109111
}
110112
}
111113

112-
impl<'a, const NP_NDIM: usize, F, NP> NonlinearSolver<F>
113-
for TrustRegionDoglegSolver<'a, NP_NDIM, F, NP>
114+
impl<'a, F, NP> NonlinearSystemSize for TrustRegionDoglegSolver<'a, F, NP>
115+
where
116+
F: crate::FloatType,
117+
NP: NonlinearProblem<F>,
118+
[F; NP::NDIM]: Sized,
119+
{
120+
const NDIM: usize = NP::NDIM;
121+
}
122+
123+
impl<'a, F, NP> NonlinearSolver<F>
124+
for TrustRegionDoglegSolver<'a, F, NP>
114125
where
115126
F: crate::FloatType,
116127
NP: NonlinearProblem<F>,
117-
[F; NP_NDIM + 1]: Sized,
128+
[F; NP::NDIM + 1]: Sized,
118129
{
119-
const NDIM: usize = NP_NDIM;
120130
fn setup_options(&mut self, max_iter: usize, tolerance: F, output_level: Option<i32>) {
121131
self.converged = false;
122132
self.function_evals = 0;
@@ -148,14 +158,14 @@ where
148158
info!("Initial delta = {:?}", self.delta);
149159
}
150160

151-
let mut residual = [F::zero(); NP_NDIM];
152-
let mut jacobian = [[F::zero(); NP_NDIM]; NP_NDIM];
161+
let mut residual = [F::zero(); NP::NDIM];
162+
let mut jacobian = [[F::zero(); NP::NDIM]; NP::NDIM];
153163

154-
if !self.compute_residual_jacobian::<{ NP_NDIM }>(&mut residual, &mut jacobian) {
164+
if !self.compute_residual_jacobian(&mut residual, &mut jacobian) {
155165
return Err(crate::helix_error::SolverError::InitialEvalFailure);
156166
}
157167

158-
self.l2_error = norm::<{ NP_NDIM }, F>(&residual);
168+
self.l2_error = norm::<{ NP::NDIM }, F>(&residual);
159169

160170
let mut l2_error_0 = self.l2_error;
161171

@@ -165,28 +175,28 @@ where
165175

166176
let mut reject_previous = false;
167177

168-
let mut newton_raphson_step = [F::zero(); NP_NDIM];
169-
let mut gradient = [F::zero(); NP_NDIM];
170-
let mut delta_x = [F::zero(); NP_NDIM];
178+
let mut newton_raphson_step = [F::zero(); NP::NDIM];
179+
let mut gradient = [F::zero(); NP::NDIM];
180+
let mut delta_x = [F::zero(); NP::NDIM];
171181
let mut jacob_grad_2 = F::zero();
172182

173183
while self.num_iterations < self.max_iterations {
174184
self.num_iterations += 1;
175185

176186
if !reject_previous {
177-
mat_t_vec_mult::<{ NP_NDIM }, { NP_NDIM }, F>(&jacobian, &residual, &mut gradient);
178-
let mut temp = [F::zero(); NP_NDIM];
179-
mat_vec_mult::<{ NP_NDIM }, { NP_NDIM }, F>(&jacobian, &gradient, &mut temp);
180-
jacob_grad_2 = dot_prod::<{ NP_NDIM }, F>(&temp, &temp);
187+
mat_t_vec_mult::<{ NP::NDIM }, { NP::NDIM }, F>(&jacobian, &residual, &mut gradient);
188+
let mut temp = [F::zero(); NP::NDIM];
189+
mat_vec_mult::<{ NP::NDIM }, { NP::NDIM }, F>(&jacobian, &gradient, &mut temp);
190+
jacob_grad_2 = dot_prod::<{ NP::NDIM }, F>(&temp, &temp);
181191
self.compute_newton_step(&residual, &mut jacobian, &mut newton_raphson_step)?;
182192
}
183193

184194
let mut predicted_residual = -F::one();
185195
let mut use_newton_raphson = false;
186196

187-
let newton_raphson_l2_norm = norm::<{ NP_NDIM }, F>(&newton_raphson_step);
197+
let newton_raphson_l2_norm = norm::<{ NP::NDIM }, F>(&newton_raphson_step);
188198

189-
dogleg::<{ NP_NDIM }, F>(
199+
dogleg::<{ NP::NDIM }, F>(
190200
self.delta,
191201
l2_error_0,
192202
newton_raphson_l2_norm,
@@ -203,8 +213,8 @@ where
203213

204214
{
205215
let resid_jacob_success =
206-
self.compute_residual_jacobian::<{ NP_NDIM }>(&mut residual, &mut jacobian);
207-
let converged = self.delta_control.update::<{ NP_NDIM }>(
216+
self.compute_residual_jacobian(&mut residual, &mut jacobian);
217+
let converged = self.delta_control.update::<{ NP::NDIM }>(
208218
&residual,
209219
l2_error_0,
210220
predicted_residual,
@@ -259,10 +269,10 @@ where
259269
jacobian: &mut [[F; NDIM]],
260270
) -> bool {
261271
assert!(
262-
NP_NDIM == NDIM,
272+
NP::NDIM == NDIM,
263273
"Self::NDIM/NP_NDIM and const NDIMs are not equal..."
264274
);
265275
self.crj
266-
.compute_resid_jacobian::<{ NDIM }>(&self.x, fcn_eval, &mut Some(jacobian))
276+
.compute_resid_jacobian(&self.x, fcn_eval, &mut Some(jacobian))
267277
}
268278
}

tests/broyden.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,17 @@ where
3737
pub logging_level: i32,
3838
}
3939

40-
impl<F> NonlinearProblem<F> for Broyden<F>
40+
impl<F> NonlinearSystemSize for Broyden<F>
4141
where
4242
F: helix_snail::FloatType,
4343
{
4444
const NDIM: usize = 8;
45+
}
46+
47+
impl<F> NonlinearProblem<F> for Broyden<F>
48+
where
49+
F: helix_snail::FloatType,
50+
{
4551
fn compute_resid_jacobian<const NDIM: usize>(
4652
&mut self,
4753
x: &[F],
@@ -133,7 +139,7 @@ macro_rules! broyden_tr_dogleg_tests {
133139
..Default::default()
134140
};
135141
{
136-
let mut solver = TrustRegionDoglegSolver::<{Broyden::<$type>::NDIM}, $type, Broyden<$type>>::new(&dc, &mut broyden);
142+
let mut solver = TrustRegionDoglegSolver::<$type, Broyden<$type>>::new(&dc, &mut broyden);
137143

138144
for i in 0..Broyden::<$type>::NDIM {
139145
solver.x[i] = 0.0;

0 commit comments

Comments
 (0)