Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- Bump `pyO3` and `rust-numpy` to 0.26.0: introduce Python build 3.14/3.14t support https://github.com/light-curve/light-curve-python/pull/553
- **Breaking** macOS x86\_64 binary wheel now requires macOS 15 instead of 13 https://github.com/light-curve/light-curve-python/issues/587
- Bump `pyO3` and `rust-numpy` from 0.25.0 to 0.27.0: introduce Python build 3.14/3.14t
support https://github.com/light-curve/light-curve-python/pull/553 https://github.com/light-curve/light-curve-python/pull/590
- **Breaking** macOS x86\_64 binary wheel now requires macOS 15 instead of
13 https://github.com/light-curve/light-curve-python/issues/587

### Deprecated

Expand Down
24 changes: 12 additions & 12 deletions light-curve/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions light-curve/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ mimalloc = { version = "0.1.48", features = [
"local_dynamic_tls",
], optional = true }
ndarray = { version = "0.16.1", features = ["rayon"] }
numpy = "0.26.0"
numpy = "0.27.0"
num_cpus = "1.17.0"
num-traits = "0.2"
once_cell = "1"
pyo3 = { version = "0.26.0", features = [
pyo3 = { version = "0.27.1", features = [
"extension-module",
"multiple-pymethods",
] }
Expand Down
22 changes: 11 additions & 11 deletions light-curve/src/dmdt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ where
let wrapped_t_ = t_
.into_iter()
.enumerate()
.map(|(i, t)| match t.downcast::<PyArray1<T>>() {
.map(|(i, t)| match t.cast::<PyArray1<T>>() {
Ok(a) => Ok(a.readonly()),
Err(_) => Err(Exception::TypeError(format!(
"t_[{}] has mismatched dtype with the t_[0] which is {}",
Expand Down Expand Up @@ -189,8 +189,8 @@ where
.into_iter()
.enumerate()
.map(|(i, (t, m))| {
let t = t.downcast::<PyArray1<T>>().map(|a| a.readonly());
let m = m.downcast::<PyArray1<T>>().map(|a| a.readonly());
let t = t.cast::<PyArray1<T>>().map(|a| a.readonly());
let m = m.cast::<PyArray1<T>>().map(|a| a.readonly());
match (t, m) {
(Ok(t), Ok(m)) => Ok((t, m)),
_ => Err(Exception::TypeError(format!(
Expand Down Expand Up @@ -256,8 +256,8 @@ where
.into_iter()
.enumerate()
.map(|(i, (t, m))| {
let t = t.downcast::<PyArray1<T>>().map(|a| a.readonly());
let m = m.downcast::<PyArray1<T>>().map(|a| a.readonly());
let t = t.cast::<PyArray1<T>>().map(|a| a.readonly());
let m = m.cast::<PyArray1<T>>().map(|a| a.readonly());
match (t, m) {
(Ok(t), Ok(m)) => {
let t: ContArray<_> = t.as_array().into();
Expand Down Expand Up @@ -332,9 +332,9 @@ where
.into_iter()
.enumerate()
.map(|(i, (t, m, sigma))| {
let t = t.downcast::<PyArray1<T>>().map(|a| a.readonly());
let m = m.downcast::<PyArray1<T>>().map(|a| a.readonly());
let sigma = sigma.downcast::<PyArray1<T>>().map(|a| a.readonly());
let t = t.cast::<PyArray1<T>>().map(|a| a.readonly());
let m = m.cast::<PyArray1<T>>().map(|a| a.readonly());
let sigma = sigma.cast::<PyArray1<T>>().map(|a| a.readonly());

match (t, m, sigma) {
(Ok(t), Ok(m), Ok(sigma)) => Ok((t, m, Self::sigma_to_err2(sigma))),
Expand Down Expand Up @@ -406,9 +406,9 @@ where
.into_iter()
.enumerate()
.map(|(i, (t, m, sigma))| {
let t = t.downcast::<PyArray1<T>>().map(|a| a.readonly());
let m = m.downcast::<PyArray1<T>>().map(|a| a.readonly());
let sigma = sigma.downcast::<PyArray1<T>>().map(|a| a.readonly());
let t = t.cast::<PyArray1<T>>().map(|a| a.readonly());
let m = m.cast::<PyArray1<T>>().map(|a| a.readonly());
let sigma = sigma.cast::<PyArray1<T>>().map(|a| a.readonly());

match (t, m, sigma) {
(Ok(t), Ok(m), Ok(sigma)) => {
Expand Down
17 changes: 9 additions & 8 deletions light-curve/src/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,10 +339,10 @@ impl PyFeatureEvaluator {
.into_iter()
.enumerate()
.map(|(i, (t, m, sigma))| {
let t = t.downcast::<PyArray1<T>>().map(|a| a.readonly());
let m = m.downcast::<PyArray1<T>>().map(|a| a.readonly());
let t = t.cast::<PyArray1<T>>().map(|a| a.readonly());
let m = m.cast::<PyArray1<T>>().map(|a| a.readonly());
let sigma = match &sigma {
Some(sigma) => sigma.downcast::<PyArray1<T>>().map(|a| Some(a.readonly())),
Some(sigma) => sigma.cast::<PyArray1<T>>().map(|a| Some(a.readonly())),
None => Ok(None),
};

Expand Down Expand Up @@ -1014,7 +1014,7 @@ macro_rules! fit_evaluator {

let make_transformation = match transform {
None => false,
Some(py_transform) => match py_transform.downcast::<PyBool>() {
Some(py_transform) => match py_transform.cast::<PyBool>() {
Ok(py_bool) => py_bool.is_true(),
Err(_) => return Err(PyValueError::new_err(
"transform must be a bool or None, other types are not implemented yet",
Expand Down Expand Up @@ -1262,7 +1262,7 @@ impl Bins {
let mut eval_f32 = lcf::Bins::default();
let mut eval_f64 = lcf::Bins::default();
for x in features.try_iter()? {
let py_feature = x?.downcast::<PyFeatureEvaluator>()?.borrow();
let py_feature = x?.cast::<PyFeatureEvaluator>()?.borrow();
eval_f32.add_feature(py_feature.feature_evaluator_f32.clone());
eval_f64.add_feature(py_feature.feature_evaluator_f64.clone());
}
Expand Down Expand Up @@ -1684,7 +1684,7 @@ impl Periodogram {
const STEP_SIZE_TOLLERANCE: f64 = 10.0 * f32::EPSILON as f64;

// It is more likely for users to give f64 array
let freqs_f64 = PyArrayLike1::<f64, AllowTypeChange>::extract_bound(&freqs)?;
let freqs_f64 = PyArrayLike1::<f64, AllowTypeChange>::extract(freqs.as_borrowed())?;
let freqs_f64 = freqs_f64.readonly();
let freqs_f64 = freqs_f64.as_array();
let size = freqs_f64.len();
Expand Down Expand Up @@ -1726,7 +1726,8 @@ impl Periodogram {

let freq_grid_f32 = match &freq_grid_f64 {
FreqGrid::Arbitrary(_) => {
let freqs_f32 = PyArrayLike1::<f32, AllowTypeChange>::extract_bound(&freqs)?;
let freqs_f32 =
PyArrayLike1::<f32, AllowTypeChange>::extract(freqs.as_borrowed())?;
let freqs_f32 = freqs_f32.readonly();
let freqs_f32 = freqs_f32.as_array();
FreqGrid::from_array(freqs_f32)
Expand All @@ -1748,7 +1749,7 @@ impl Periodogram {

if let Some(features) = features {
for x in features.try_iter()? {
let py_feature = x?.downcast::<PyFeatureEvaluator>()?.borrow();
let py_feature = x?.cast::<PyFeatureEvaluator>()?.borrow();
eval_f32.add_feature(py_feature.feature_evaluator_f32.clone());
eval_f64.add_feature(py_feature.feature_evaluator_f64.clone());
}
Expand Down
44 changes: 22 additions & 22 deletions light-curve/src/np_array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ impl DType for f64 {
}

pub(crate) fn unknown_type_exception(name: &str, obj: &Bound<PyAny>) -> Exception {
let message = if let Ok(arr) = obj.downcast::<PyUntypedArray>() {
let message = if let Ok(arr) = obj.cast::<PyUntypedArray>() {
let ndim = arr.ndim();
if ndim != 1 {
format!("'{name}' is a {ndim}-d array, only 1-d arrays are supported.")
Expand Down Expand Up @@ -59,13 +59,13 @@ fn cast_fail_reason<const N: usize>(
let first_name = names.first().expect("Empty names slice");
let fist_obj = objects.first().expect("Empty objects slice");

// If the very first argument downcast
// If the very first argument cast
if idx == 0 {
return unknown_type_exception(first_name, fist_obj);
}

let maybe_first_f32_array = try_downcast_to_f32_array(objects[0]);
let maybe_first_f64_array = try_downcast_to_f64_array(objects[0], cast);
let maybe_first_f32_array = try_cast_to_f32_array(objects[0]);
let maybe_first_f64_array = try_cast_to_f64_array(objects[0], cast);
let (first_arr, first_dtype_name) = if let Some(f32_array) = maybe_first_f32_array.as_ref() {
(f32_array.as_untyped(), f32::dtype_name())
} else if let Some(f64_array) = maybe_first_f64_array.as_ref() {
Expand All @@ -79,7 +79,7 @@ fn cast_fail_reason<const N: usize>(
.get(idx)
.expect("idx is out of bounds of objects slice");

let error_message = if let Ok(fail_arr) = fail_obj.downcast::<PyUntypedArray>() {
let error_message = if let Ok(fail_arr) = fail_obj.cast::<PyUntypedArray>() {
if fail_arr.ndim() != 1 {
format!(
"'{}' is a {}-d array, only 1-d arrays are supported.",
Expand Down Expand Up @@ -125,12 +125,12 @@ impl<const N: usize> GenericPyReadonlyArrays<'_, N> {
}
}

fn try_downcast_objects_to_f32_arrays<'py, const N: usize>(
fn try_cast_objects_to_f32_arrays<'py, const N: usize>(
objects: &[&Bound<'py, PyAny>; N],
) -> [Option<PyReadonlyArray1<'py, f32>>; N] {
let mut arrays = [const { None }; N];
for (&obj, arr) in objects.iter().zip(arrays.iter_mut()) {
*arr = try_downcast_to_f32_array(obj);
*arr = try_cast_to_f32_array(obj);
// If we cannot cast an array, we stop trying for future arguments
if arr.is_none() {
break;
Expand All @@ -139,18 +139,18 @@ fn try_downcast_objects_to_f32_arrays<'py, const N: usize>(
arrays
}

fn try_downcast_to_f32_array<'py>(obj: &Bound<'py, PyAny>) -> Option<PyReadonlyArray1<'py, f32>> {
let py_array = obj.downcast::<PyArray1<f32>>().ok()?;
fn try_cast_to_f32_array<'py>(obj: &Bound<'py, PyAny>) -> Option<PyReadonlyArray1<'py, f32>> {
let py_array = obj.cast::<PyArray1<f32>>().ok()?;
Some(py_array.readonly())
}

fn try_downcast_to_f64_array<'py>(
fn try_cast_to_f64_array<'py>(
obj: &Bound<'py, PyAny>,
cast: bool,
) -> Option<PyReadonlyArray1<'py, f64>> {
match (obj.downcast::<PyArray1<f64>>(), cast) {
match (obj.cast::<PyArray1<f64>>(), cast) {
(Ok(py_array), _) => Some(py_array.readonly()),
(Err(_), true) => match PyArrayLike1::<f64, AllowTypeChange>::extract_bound(obj) {
(Err(_), true) => match PyArrayLike1::<f64, AllowTypeChange>::extract(obj.as_borrowed()) {
Ok(py_array) => Some(py_array.readonly()),
Err(_) => None,
},
Expand All @@ -168,11 +168,11 @@ const fn index<const N: usize>() -> [usize; N] {
arr
}

fn downcast_objects_cast<'py, const N: usize>(
fn cast_objects_with_dtype_change<'py, const N: usize>(
names: &'static [&'static str; N],
objects: &[&Bound<'py, PyAny>; N],
) -> Res<GenericPyReadonlyArrays<'py, N>> {
let f32_arrays = try_downcast_objects_to_f32_arrays(objects);
let f32_arrays = try_cast_objects_to_f32_arrays(objects);

if f32_arrays.iter().all(|arr| arr.is_some()) {
Ok(GenericPyReadonlyArrays::F32(
Expand All @@ -183,7 +183,7 @@ fn downcast_objects_cast<'py, const N: usize>(
let f64_arr = if let Some(f32_arr) = &f32_arrays[i] {
f32_arr.cast_array::<f64>(false)?.readonly()
} else {
try_downcast_to_f64_array(objects[i], true)
try_cast_to_f64_array(objects[i], true)
.ok_or_else(|| cast_fail_reason(i, names, objects, true))?
};
Ok(f64_arr)
Expand All @@ -193,11 +193,11 @@ fn downcast_objects_cast<'py, const N: usize>(
}
}

fn downcast_objects_no_cast<'py, const N: usize>(
fn cast_objects_no_dtype_change<'py, const N: usize>(
names: &'static [&'static str; N],
objects: &[&Bound<'py, PyAny>; N],
) -> Res<GenericPyReadonlyArrays<'py, N>> {
let f32_arrays = try_downcast_objects_to_f32_arrays(objects);
let f32_arrays = try_cast_objects_to_f32_arrays(objects);

if f32_arrays.iter().all(|arr| arr.is_some()) {
Ok(GenericPyReadonlyArrays::F32(
Expand All @@ -208,7 +208,7 @@ fn downcast_objects_no_cast<'py, const N: usize>(
let f64_arrays = objects
.map_option(|obj| {
valid_f64_count += 1;
try_downcast_to_f64_array(obj, false)
try_cast_to_f64_array(obj, false)
})
.ok_or_else(|| {
let valid_f32_count = f32_arrays.iter().filter(|arr| arr.is_some()).count();
Expand All @@ -224,7 +224,7 @@ fn downcast_objects_no_cast<'py, const N: usize>(
}
}

pub(crate) fn downcast_and_validate<'py, const N: usize>(
pub(crate) fn cast_and_validate<'py, const N: usize>(
names: &'static [&'static str; N],
objects: &[&Bound<'py, PyAny>; N],
check_size: &[bool; N],
Expand All @@ -233,9 +233,9 @@ pub(crate) fn downcast_and_validate<'py, const N: usize>(
assert_eq!(names.len(), objects.len());

let arrays = if cast {
downcast_objects_cast(names, objects)?
cast_objects_with_dtype_change(names, objects)?
} else {
downcast_objects_no_cast(names, objects)?
cast_objects_no_dtype_change(names, objects)?
};
let mut first_array_len = None;
// We checked that 1) names size matches objects size, 2) objects is not empty
Expand Down Expand Up @@ -299,7 +299,7 @@ macro_rules! dtype_dispatch {
let objects = &[&$first_arg, $(&$arg, )*];
let check_size = &[ false, $(_distinguish_eq_symbol!($eq), )* ];

let generic_arrays = crate::np_array::downcast_and_validate(names, objects, check_size, $cast)?;
let generic_arrays = crate::np_array::cast_and_validate(names, objects, check_size, $cast)?;
match generic_arrays {
crate::np_array::GenericPyReadonlyArrays::F32(arrays) => {
let func = $f32;
Expand Down
4 changes: 2 additions & 2 deletions light-curve/src/transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,13 @@ pub(crate) fn parse_transform(
match option {
None => Ok(None),
Some(py_any) => {
if let Ok(py_bool) = py_any.downcast::<PyBool>() {
if let Ok(py_bool) = py_any.cast::<PyBool>() {
if py_bool.is_true() {
Ok(Some(default))
} else {
Ok(None)
}
} else if let Ok(py_str) = py_any.downcast::<PyString>() {
} else if let Ok(py_str) = py_any.cast::<PyString>() {
// py_str.to_str() is Python 3.10+ only
let cow_string = py_str.to_cow()?;
let s = cow_string.as_ref();
Expand Down
Loading