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
4 changes: 2 additions & 2 deletions SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -292,8 +292,8 @@ The primary array type representing N-dimensional arrays.
**Priority**: MEDIUM

**Requirements**:
- [ ] 8.2.1: `$array->all($axis = null, $keepdims = false)` - All true
- [ ] 8.2.2: `$array->any($axis = null, $keepdims = false)` - Any true
- [x] 8.2.1: `$array->all($axis = null, $keepdims = false)` - All true
- [x] 8.2.2: `$array->any($axis = null, $keepdims = false)` - Any true

### 8.3 Index-based Operations (REQ-8.3)
**Priority**: MEDIUM
Expand Down
84 changes: 84 additions & 0 deletions docs/api/statistics.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,88 @@ print_r($row_prods->toArray());

---

## any()

Test whether any element is true over a given axis.

```php
public function any(?int $axis = null, bool $keepdims = false): bool|NDArray
```

For non-bool arrays, zero is treated as false and non-zero as true. For complex numbers, the value is false if both real and imag parts are zero.

### Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `$axis` | `int\|null` | Axis along which to check. If null, checks the entire array. Optional. Default: `null`. |
| `$keepdims` | `bool` | If true, the reduced axis is retained with size 1. Optional. Default: `false`. |

### Returns

- `bool|NDArray` - Scalar boolean if axis is null, otherwise an NDArray of bool.

### Examples

```php
$arr = NDArray::array([[0, 0], [0, 5]]);

// Check all elements
$result = $arr->any();
echo $result ? 'true' : 'false'; // true

// Check along axis 0 (columns)
$col_result = $arr->any(axis: 0);
print_r($col_result->toArray()); // [false, true]

// Check along axis 1 (rows)
$row_result = $arr->any(axis: 1);
print_r($row_result->toArray()); // [false, true]
```

---

## all()

Test whether all elements are true over a given axis.

```php
public function all(?int $axis = null, bool $keepdims = false): bool|NDArray
```

For non-bool arrays, zero is treated as false and non-zero as true. For complex numbers, the value is false if both real and imag parts are zero.

### Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `$axis` | `int\|null` | Axis along which to check. If null, checks the entire array. Optional. Default: `null`. |
| `$keepdims` | `bool` | If true, the reduced axis is retained with size 1. Optional. Default: `false`. |

### Returns

- `bool|NDArray` - Scalar boolean if axis is null, otherwise an NDArray of bool.

### Examples

```php
$arr = NDArray::array([[1, 1], [1, 0]]);

// Check all elements
$result = $arr->all();
echo $result ? 'true' : 'false'; // false

// Check along axis 0 (columns)
$col_result = $arr->all(axis: 0);
print_r($col_result->toArray()); // [true, false]

// Check along axis 1 (rows)
$row_result = $arr->all(axis: 1);
print_r($row_result->toArray()); // [true, false]
```

---

## Summary Table

| Method | Description | Returns |
Expand All @@ -268,6 +350,8 @@ print_r($row_prods->toArray());
| `min()` | Minimum value | Scalar or array |
| `max()` | Maximum value | Scalar or array |
| `product()` | Product of elements | Scalar or array |
| `any()` | Any element true | Scalar or array |
| `all()` | All elements true | Scalar or array |

---

Expand Down
4 changes: 4 additions & 0 deletions docs/guide/getting-started/numpy-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ $mask = $a->gt($b);
| `a.max()` | `$a->max()` | |
| `a.argmin()` | `$a->argmin()` | |
| `a.argmax()` | `$a->argmax()` | |
| `a.any()` | `$a->any()` | |
| `a.any(axis=0)` | `$a->any(axis: 0)` | Named arguments |
| `a.all()` | `$a->all()` | |
| `a.all(axis=0)` | `$a->all(axis: 0)` | Named arguments |

### Linear Algebra

Expand Down
42 changes: 42 additions & 0 deletions include/ndarray_php.h
Original file line number Diff line number Diff line change
Expand Up @@ -1864,6 +1864,48 @@ uintptr_t ndarray_to_string(const struct NdArrayHandle *handle,
uintptr_t edgeitems,
uintptr_t precision);

/**
* Compute whether all elements are truthy (scalar).
*/
int32_t ndarray_all(const struct NdArrayHandle *handle,
const struct ArrayMetadata *meta,
void *out_value,
uint8_t *out_dtype);

/**
* Compute whether all elements are truthy along an axis.
*/
int32_t ndarray_all_axis(const struct NdArrayHandle *handle,
const struct ArrayMetadata *meta,
int32_t axis,
bool keepdims,
struct NdArrayHandle **out_handle,
uint8_t *out_dtype,
uintptr_t *out_ndim,
uintptr_t *out_shape,
uintptr_t max_ndim);

/**
* Compute whether any element is truthy (scalar).
*/
int32_t ndarray_any(const struct NdArrayHandle *handle,
const struct ArrayMetadata *meta,
void *out_value,
uint8_t *out_dtype);

/**
* Compute whether any element is truthy along an axis.
*/
int32_t ndarray_any_axis(const struct NdArrayHandle *handle,
const struct ArrayMetadata *meta,
int32_t axis,
bool keepdims,
struct NdArrayHandle **out_handle,
uint8_t *out_dtype,
uintptr_t *out_ndim,
uintptr_t *out_shape,
uintptr_t max_ndim);

/**
* Argmax along axis.
*/
Expand Down
108 changes: 108 additions & 0 deletions rust/src/ffi/reductions/all.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
//! Boolean all reduction (logical AND).

use std::ffi::c_void;
use std::sync::Arc;

use ndarray::Axis;
use parking_lot::RwLock;

use crate::ffi::reductions::helpers::{write_reduction_scalar, ReductionScalar};
use crate::helpers::error::{set_last_error, ERR_GENERIC, ERR_SHAPE, SUCCESS};
use crate::helpers::extract_view_as_bool;
use crate::helpers::normalize_axis;
use crate::helpers::write_output_metadata;
use crate::types::dtype::DType;
use crate::types::{ArrayData, ArrayMetadata, NDArrayWrapper, NdArrayHandle};

/// Compute whether all elements are truthy (scalar).
#[no_mangle]
pub unsafe extern "C" fn ndarray_all(
handle: *const NdArrayHandle,
meta: *const ArrayMetadata,
out_value: *mut c_void,
out_dtype: *mut u8,
) -> i32 {
if handle.is_null() || meta.is_null() || out_value.is_null() || out_dtype.is_null() {
return ERR_GENERIC;
}

let meta = &*meta;

crate::ffi_guard!({
let wrapper = NdArrayHandle::as_wrapper(handle as *mut _);

let Some(view) = extract_view_as_bool(wrapper, meta) else {
set_last_error("Failed to extract view".to_string());
return ERR_GENERIC;
};

let result: u8 = view.iter().all(|&x| x != 0) as u8;
write_reduction_scalar(out_value, out_dtype, ReductionScalar::Bool(result));
SUCCESS
})
}

/// Compute whether all elements are truthy along an axis.
#[no_mangle]
pub unsafe extern "C" fn ndarray_all_axis(
handle: *const NdArrayHandle,
meta: *const ArrayMetadata,
axis: i32,
keepdims: bool,
out_handle: *mut *mut NdArrayHandle,
out_dtype: *mut u8,
out_ndim: *mut usize,
out_shape: *mut usize,
max_ndim: usize,
) -> i32 {
if handle.is_null()
|| out_handle.is_null()
|| meta.is_null()
|| out_dtype.is_null()
|| out_ndim.is_null()
|| out_shape.is_null()
{
return ERR_GENERIC;
}

let meta = &*meta;

crate::ffi_guard!({
let wrapper = NdArrayHandle::as_wrapper(handle as *mut _);
let shape_slice = meta.shape_slice();

let axis_usize = match normalize_axis(shape_slice, axis, false) {
Ok(a) => a,
Err(e) => {
set_last_error(e);
return ERR_SHAPE;
}
};

let Some(view) = extract_view_as_bool(wrapper, meta) else {
set_last_error("Failed to extract view".to_string());
return ERR_GENERIC;
};

let result = view.fold_axis(Axis(axis_usize), 1u8, |&acc, &x| acc & x);
let final_arr: ndarray::ArrayD<u8> = if keepdims {
result.insert_axis(Axis(axis_usize)).into_dyn()
} else {
result.into_dyn()
};

let result_wrapper = NDArrayWrapper {
data: ArrayData::Bool(Arc::new(RwLock::new(final_arr))),
dtype: DType::Bool,
};

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_handle = NdArrayHandle::from_wrapper(Box::new(result_wrapper));
SUCCESS
})
}
108 changes: 108 additions & 0 deletions rust/src/ffi/reductions/any.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
//! Boolean any reduction (logical OR).

use std::ffi::c_void;
use std::sync::Arc;

use ndarray::Axis;
use parking_lot::RwLock;

use crate::ffi::reductions::helpers::{write_reduction_scalar, ReductionScalar};
use crate::helpers::error::{set_last_error, ERR_GENERIC, ERR_SHAPE, SUCCESS};
use crate::helpers::extract_view_as_bool;
use crate::helpers::normalize_axis;
use crate::helpers::write_output_metadata;
use crate::types::dtype::DType;
use crate::types::{ArrayData, ArrayMetadata, NDArrayWrapper, NdArrayHandle};

/// Compute whether any element is truthy (scalar).
#[no_mangle]
pub unsafe extern "C" fn ndarray_any(
handle: *const NdArrayHandle,
meta: *const ArrayMetadata,
out_value: *mut c_void,
out_dtype: *mut u8,
) -> i32 {
if handle.is_null() || meta.is_null() || out_value.is_null() || out_dtype.is_null() {
return ERR_GENERIC;
}

let meta = &*meta;

crate::ffi_guard!({
let wrapper = NdArrayHandle::as_wrapper(handle as *mut _);

let Some(view) = extract_view_as_bool(wrapper, meta) else {
set_last_error("Failed to extract view".to_string());
return ERR_GENERIC;
};

let result: u8 = view.iter().any(|&x| x != 0) as u8;
write_reduction_scalar(out_value, out_dtype, ReductionScalar::Bool(result));
SUCCESS
})
}

/// Compute whether any element is truthy along an axis.
#[no_mangle]
pub unsafe extern "C" fn ndarray_any_axis(
handle: *const NdArrayHandle,
meta: *const ArrayMetadata,
axis: i32,
keepdims: bool,
out_handle: *mut *mut NdArrayHandle,
out_dtype: *mut u8,
out_ndim: *mut usize,
out_shape: *mut usize,
max_ndim: usize,
) -> i32 {
if handle.is_null()
|| out_handle.is_null()
|| meta.is_null()
|| out_dtype.is_null()
|| out_ndim.is_null()
|| out_shape.is_null()
{
return ERR_GENERIC;
}

let meta = &*meta;

crate::ffi_guard!({
let wrapper = NdArrayHandle::as_wrapper(handle as *mut _);
let shape_slice = meta.shape_slice();

let axis_usize = match normalize_axis(shape_slice, axis, false) {
Ok(a) => a,
Err(e) => {
set_last_error(e);
return ERR_SHAPE;
}
};

let Some(view) = extract_view_as_bool(wrapper, meta) else {
set_last_error("Failed to extract view".to_string());
return ERR_GENERIC;
};

let result = view.fold_axis(Axis(axis_usize), 0u8, |&acc, &x| acc | x);
let final_arr: ndarray::ArrayD<u8> = if keepdims {
result.insert_axis(Axis(axis_usize)).into_dyn()
} else {
result.into_dyn()
};

let result_wrapper = NDArrayWrapper {
data: ArrayData::Bool(Arc::new(RwLock::new(final_arr))),
dtype: DType::Bool,
};

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_handle = NdArrayHandle::from_wrapper(Box::new(result_wrapper));
SUCCESS
})
}
Loading
Loading