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
26 changes: 18 additions & 8 deletions docs/api/mathematical-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -1184,18 +1184,18 @@ print_r($result->toArray());
## minimum()

```php
public function minimum(NDArray $other): NDArray
public function minimum(Complex|float|int|NDArray $other): NDArray
```

Element-wise minimum of two arrays.
Element-wise minimum of two arrays, or of an array and a scalar.

Compares two arrays element-wise and returns a new array containing the smaller value at each position. Supports broadcasting.
Compares two arrays element-wise and returns a new array containing the smaller value at each position. Supports broadcasting. If a scalar is provided, each array element is compared against that value.

### Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `$other` | `NDArray` | The array to compare with. |
| `$other` | `Complex\|float\|int\|NDArray` | The array or scalar to compare with. |

### Returns

Expand All @@ -1210,6 +1210,11 @@ $result = $a->minimum($b);
print_r($result->toArray());
// Output: [1, 4, 3, 7]

// With a scalar — each element compared to 4
$result = $a->minimum(4);
print_r($result->toArray());
// Output: [1, 4, 3, 4]

// With broadcasting
$a = NDArray::array([[1, 2, 3], [4, 5, 6]]);
$b = NDArray::array([2, 2, 2]);
Expand All @@ -1223,18 +1228,18 @@ print_r($result->toArray());
## maximum()

```php
public function maximum(NDArray $other): NDArray
public function maximum(Complex|float|int|NDArray $other): NDArray
```

Element-wise maximum of two arrays.
Element-wise maximum of two arrays, or of an array and a scalar.

Compares two arrays element-wise and returns a new array containing the larger value at each position. Supports broadcasting.
Compares two arrays element-wise and returns a new array containing the larger value at each position. Supports broadcasting. If a scalar is provided, each array element is compared against that value.

### Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `$other` | `NDArray` | The array to compare with. |
| `$other` | `Complex\|float\|int\|NDArray` | The array or scalar to compare with. |

### Returns

Expand All @@ -1249,6 +1254,11 @@ $result = $a->maximum($b);
print_r($result->toArray());
// Output: [2, 5, 6, 8]

// With a scalar — each element compared to 4
$result = $a->maximum(4);
print_r($result->toArray());
// Output: [4, 5, 4, 8]

// With broadcasting
$a = NDArray::array([[1, 2, 3], [4, 5, 6]]);
$b = NDArray::array([2, 2, 2]);
Expand Down
24 changes: 24 additions & 0 deletions include/ndarray_php.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,18 @@ int32_t ndarray_maximum(const struct NdArrayHandle *a,
uintptr_t *out_shape,
uintptr_t max_ndim);

/**
* Element-wise maximum with a scalar.
*/
int32_t ndarray_maximum_scalar(const struct NdArrayHandle *a,
const struct ArrayMetadata *meta,
double scalar,
struct NdArrayHandle **out,
uint8_t *out_dtype,
uintptr_t *out_ndim,
uintptr_t *out_shape,
uintptr_t max_ndim);

/**
* Element-wise minimum with broadcasting.
*/
Expand All @@ -140,6 +152,18 @@ int32_t ndarray_minimum(const struct NdArrayHandle *a,
uintptr_t *out_shape,
uintptr_t max_ndim);

/**
* Element-wise minimum with a scalar.
*/
int32_t ndarray_minimum_scalar(const struct NdArrayHandle *a,
const struct ArrayMetadata *meta,
double scalar,
struct NdArrayHandle **out,
uint8_t *out_dtype,
uintptr_t *out_ndim,
uintptr_t *out_shape,
uintptr_t max_ndim);

/**
* Multiply two arrays.
*/
Expand Down
140 changes: 139 additions & 1 deletion rust/src/ffi/arithmetic/maximum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,15 @@ use crate::binary_op_arithmetic;
use crate::helpers::elementwise_minmax::ElementwiseMaximum;
use crate::helpers::error::{set_last_error, ERR_GENERIC, SUCCESS};
use crate::helpers::write_output_metadata;
use crate::types::{ArrayMetadata, NdArrayHandle};
use crate::helpers::{
extract_array_f32, extract_array_f64, extract_array_i16, extract_array_i32,
extract_array_i64, extract_array_i8, extract_array_u16, extract_array_u32,
extract_array_u64, extract_array_u8,
};
use crate::types::dtype::DType;
use crate::types::{ArrayData, ArrayMetadata, NDArrayWrapper, NdArrayHandle};
use parking_lot::RwLock;
use std::sync::Arc;

#[inline(always)]
fn maximum<T: ElementwiseMaximum>(a: &T, b: &T) -> T {
Expand Down Expand Up @@ -62,3 +70,133 @@ pub unsafe extern "C" fn ndarray_maximum(
SUCCESS
})
}

/// Element-wise maximum with a scalar.
#[no_mangle]
pub unsafe extern "C" fn ndarray_maximum_scalar(
a: *const NdArrayHandle,
meta: *const ArrayMetadata,
scalar: f64,
out: *mut *mut NdArrayHandle,
out_dtype: *mut u8,
out_ndim: *mut usize,
out_shape: *mut usize,
max_ndim: usize,
) -> i32 {
if a.is_null() || meta.is_null() || out.is_null() || out_dtype.is_null()
|| out_ndim.is_null() || out_shape.is_null()
{
return ERR_GENERIC;
}

crate::ffi_guard!({
let a_wrapper = NdArrayHandle::as_wrapper(a as *mut _);
let meta = &*meta;

let result_wrapper = match a_wrapper.dtype {
DType::Float64 => {
let Some(arr) = extract_array_f64(a_wrapper, meta) else {
set_last_error("Failed to extract f64 array".to_string());
return ERR_GENERIC;
};
let result = arr.mapv(|x| x.max(scalar));
NDArrayWrapper { data: ArrayData::Float64(Arc::new(RwLock::new(result))), dtype: DType::Float64 }
}
DType::Float32 => {
let Some(arr) = extract_array_f32(a_wrapper, meta) else {
set_last_error("Failed to extract f32 array".to_string());
return ERR_GENERIC;
};
let result = arr.mapv(|x| x.max(scalar as f32));
NDArrayWrapper { data: ArrayData::Float32(Arc::new(RwLock::new(result))), dtype: DType::Float32 }
}
DType::Int64 => {
let Some(arr) = extract_array_i64(a_wrapper, meta) else {
set_last_error("Failed to extract i64 array".to_string());
return ERR_GENERIC;
};
let s = scalar as i64;
let result = arr.mapv(|x| x.max(s));
NDArrayWrapper { data: ArrayData::Int64(Arc::new(RwLock::new(result))), dtype: DType::Int64 }
}
DType::Int32 => {
let Some(arr) = extract_array_i32(a_wrapper, meta) else {
set_last_error("Failed to extract i32 array".to_string());
return ERR_GENERIC;
};
let s = scalar as i32;
let result = arr.mapv(|x| x.max(s));
NDArrayWrapper { data: ArrayData::Int32(Arc::new(RwLock::new(result))), dtype: DType::Int32 }
}
DType::Int16 => {
let Some(arr) = extract_array_i16(a_wrapper, meta) else {
set_last_error("Failed to extract i16 array".to_string());
return ERR_GENERIC;
};
let s = scalar as i16;
let result = arr.mapv(|x| x.max(s));
NDArrayWrapper { data: ArrayData::Int16(Arc::new(RwLock::new(result))), dtype: DType::Int16 }
}
DType::Int8 => {
let Some(arr) = extract_array_i8(a_wrapper, meta) else {
set_last_error("Failed to extract i8 array".to_string());
return ERR_GENERIC;
};
let s = scalar as i8;
let result = arr.mapv(|x| x.max(s));
NDArrayWrapper { data: ArrayData::Int8(Arc::new(RwLock::new(result))), dtype: DType::Int8 }
}
DType::Uint64 => {
let Some(arr) = extract_array_u64(a_wrapper, meta) else {
set_last_error("Failed to extract u64 array".to_string());
return ERR_GENERIC;
};
let s = (scalar.max(0.0)) as u64;
let result = arr.mapv(|x| x.max(s));
NDArrayWrapper { data: ArrayData::Uint64(Arc::new(RwLock::new(result))), dtype: DType::Uint64 }
}
DType::Uint32 => {
let Some(arr) = extract_array_u32(a_wrapper, meta) else {
set_last_error("Failed to extract u32 array".to_string());
return ERR_GENERIC;
};
let s = (scalar.max(0.0)) as u32;
let result = arr.mapv(|x| x.max(s));
NDArrayWrapper { data: ArrayData::Uint32(Arc::new(RwLock::new(result))), dtype: DType::Uint32 }
}
DType::Uint16 => {
let Some(arr) = extract_array_u16(a_wrapper, meta) else {
set_last_error("Failed to extract u16 array".to_string());
return ERR_GENERIC;
};
let s = (scalar.max(0.0)) as u16;
let result = arr.mapv(|x| x.max(s));
NDArrayWrapper { data: ArrayData::Uint16(Arc::new(RwLock::new(result))), dtype: DType::Uint16 }
}
DType::Uint8 => {
let Some(arr) = extract_array_u8(a_wrapper, meta) else {
set_last_error("Failed to extract u8 array".to_string());
return ERR_GENERIC;
};
let s = (scalar.max(0.0)) as u8;
let result = arr.mapv(|x| x.max(s));
NDArrayWrapper { data: ArrayData::Uint8(Arc::new(RwLock::new(result))), dtype: DType::Uint8 }
}
DType::Bool => {
set_last_error("maximum_scalar() not supported for Bool type".to_string());
return ERR_GENERIC;
}
DType::Complex64 | DType::Complex128 => {
set_last_error("maximum_scalar() not supported for complex dtypes".to_string());
return ERR_GENERIC;
}
};

if let Err(e) = write_output_metadata(&result_wrapper, out_dtype, out_ndim, out_shape, max_ndim) {
set_last_error(e);
return ERR_GENERIC;
}
*out = NdArrayHandle::from_wrapper(Box::new(result_wrapper));
SUCCESS
})
}
Loading