Skip to content

Commit 293e5f7

Browse files
authored
Merge pull request #9 from espdev/fix-3d-layout
Fix 3D spline evaluation layout handling for multivariate data
2 parents 1f5fd14 + ef1ea05 commit 293e5f7

8 files changed

Lines changed: 78 additions & 39 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# These are backup files generated by rustfmt
88
**/*.rs.bk
99

10-
# IDE
10+
# IDE & Agents
1111
.idea/
1212
.vscode/
13+
.codex

CHANGELOG.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
# Changelog
22

3-
## v0.4.0 (2022)
3+
## v0.5.0 (01.05.2026)
4+
5+
* Fix 3D spline evaluation layout handling for multivariate data.
6+
* Update dependencies for `ndarray` 0.17 and replace deprecated reshape calls with explicit layout-aware reshaping.
7+
8+
9+
## v0.4.0 (20.04.2022)
410
* Update to ndarray 0.15 and sprs 0.11
511

12+
613
## v0.3.0 (22.03.2020)
714

815
* Add `Real` pub trait. Trait `Real` is only implemented for `f32` and `f64`,

Cargo.lock

Lines changed: 14 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "csaps"
3-
version = "0.4.0"
3+
version = "0.5.0"
44
authors = ["Eugene Prilepin <esp.home@gmail.com>"]
55

66
description = "Cubic spline approximation (smoothing)"
@@ -17,19 +17,18 @@ edition = "2021"
1717

1818

1919
[badges]
20-
travis-ci = { repository = "espdev/csaps-rs", branch = "master" }
2120
coveralls = { repository = "espdev/csaps-rs", branch = "master", service = "github" }
2221

2322

2423
[dependencies]
2524
num-traits = "0.2.14"
26-
ndarray = "0.16.1"
25+
ndarray = "0.17.2"
2726
sprs-ldl = "0.10.0"
28-
sprs = "0.11.2"
27+
sprs = "0.11.4"
2928
almost = "0.2.0"
30-
itertools = "0.13.0"
31-
thiserror = "1.0.30"
29+
itertools = "0.14.0"
30+
thiserror = "2.0.18"
3231

3332
[dev-dependencies]
3433
approx = "0.5.1"
35-
ndarray = {version = "0.16.1", features = ["approx"]}
34+
ndarray = {version = "0.17.2", features = ["approx"]}

src/ndarrayext.rs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,25 @@
11
use itertools::Itertools;
2-
use ndarray::{prelude::*, IntoDimension, Slice};
2+
use ndarray::{prelude::*, IntoDimension, Order, Slice};
33

44
use crate::{
55
util::dim_from_vec,
66
CsapsError::{ReshapeFrom2d, ReshapeTo2d},
77
Real, Result,
88
};
99

10+
pub(crate) fn reshape_order<T, D>(data: &ArrayView<'_, T, D>) -> Order
11+
where
12+
D: Dimension,
13+
{
14+
if data.is_standard_layout() {
15+
Order::RowMajor
16+
} else if data.ndim() > 1 && data.raw_view().reversed_axes().is_standard_layout() {
17+
Order::ColumnMajor
18+
} else {
19+
Order::RowMajor
20+
}
21+
}
22+
1023
pub fn diff<'a, T: 'a, D, V>(data: V, axis: Option<Axis>) -> Array<T, D>
1124
where
1225
T: Real<T>,
@@ -43,7 +56,10 @@ where
4356
let axis_size = shape[axis.0];
4457
let new_shape = [numel / axis_size, axis_size];
4558

46-
match data_view.permuted_axes(axes).into_shape(new_shape) {
59+
let data_view = data_view.permuted_axes(axes);
60+
let order = reshape_order(&data_view);
61+
62+
match data_view.into_shape_with_order((new_shape, order)) {
4763
Ok(view_2d) => Ok(view_2d),
4864
Err(error) => Err(ReshapeTo2d {
4965
input_shape: shape,
@@ -62,7 +78,9 @@ where
6278
let shape = data.shape().to_vec();
6379
let new_shape = [shape[0..(ndim - 1)].iter().product(), shape[ndim - 1]];
6480

65-
match data.into_shape(new_shape) {
81+
let order = reshape_order(&data);
82+
83+
match data.into_shape_with_order((new_shape, order)) {
6684
Ok(data_2d) => Ok(data_2d),
6785
Err(error) => Err(ReshapeTo2d {
6886
input_shape: shape,
@@ -93,7 +111,9 @@ where
93111
let new_shape: D = dim_from_vec(ndim, new_shape_vec.clone());
94112
let data_view = data.into();
95113

96-
match data_view.into_shape(new_shape) {
114+
let order = reshape_order(&data_view);
115+
116+
match data_view.into_shape_with_order((new_shape, order)) {
97117
Ok(view_nd) => {
98118
let mut axes_tmp: Vec<usize> = (0..ndim).collect();
99119
let end_axis = axes_tmp.pop().unwrap();

src/ndg/evaluate.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
use ndarray::{Array, ArrayView1, Dimension};
22

3-
use crate::{ndarrayext::to_2d_simple, util::dim_from_vec, NdSpline, Real};
3+
use crate::{
4+
ndarrayext::{reshape_order, to_2d_simple},
5+
util::dim_from_vec,
6+
NdSpline, Real,
7+
};
48

59
use super::{util::permute_axes, GridCubicSmoothingSpline, NdGridSpline};
610

@@ -36,8 +40,10 @@ where
3640
coeffs_shape[ndim_m1] = xi_ax.len();
3741
let shape: D = dim_from_vec(self.ndim, coeffs_shape);
3842

43+
let order = reshape_order(&coeffs_2d.view());
44+
3945
coeffs_2d
40-
.into_shape(shape)
46+
.into_shape_with_order((shape, order))
4147
.unwrap()
4248
.permuted_axes(permuted_axes.clone())
4349
.to_owned()

src/ndg/make.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
use ndarray::Dimension;
22

33
use crate::util::dim_from_vec;
4-
use crate::{ndarrayext::to_2d_simple, CubicSmoothingSpline, Real, RealRef, Result};
4+
use crate::{
5+
ndarrayext::{reshape_order, to_2d_simple},
6+
CubicSmoothingSpline, Real, RealRef, Result,
7+
};
58

69
use super::{util::permute_axes, GridCubicSmoothingSpline, NdGridSpline};
710

@@ -54,9 +57,11 @@ where
5457
coeffs_shape[ndim_m1] = spline.pieces() * spline.order();
5558
let new_shape: D = dim_from_vec(ndim, coeffs_shape);
5659

57-
spline
58-
.coeffs()
59-
.into_shape(new_shape)
60+
let coeffs = spline.coeffs();
61+
let order = reshape_order(&coeffs);
62+
63+
coeffs
64+
.into_shape_with_order((new_shape, order))
6065
.unwrap()
6166
.permuted_axes(permuted_axes.clone())
6267
.to_owned()

src/umv/evaluate.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,15 @@ where
4444
// Returns NxM array of coeffs values for given 1xM indices array
4545
// where N is ndim and M is the size of xi
4646
let get_indexed_coeffs = |inds: &Array1<usize>| {
47-
// Returns Nx1 2-d array of coeffs by given index
48-
let coeffs_by_index = |&index| coeffs.slice(s![.., index]).insert_axis(Axis(1));
47+
let mut indexed_coeffs = Array2::<T>::zeros((coeffs.nrows(), inds.len()));
4948

50-
// Get the M-sized vector of coeffs values Nx1 arrays
51-
// for all dimensions for 1xM indices array
52-
let indexed_coeffs: Vec<_> = inds.iter().map(coeffs_by_index).collect();
49+
for (col, &index) in inds.iter().enumerate() {
50+
indexed_coeffs
51+
.column_mut(col)
52+
.assign(&coeffs.slice(s![.., index]));
53+
}
5354

54-
concatenate(Axis(1), &indexed_coeffs).unwrap()
55+
indexed_coeffs
5556
};
5657

5758
// Vectorized computing the spline pieces (polynoms) on the given data sites

0 commit comments

Comments
 (0)