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
72 changes: 72 additions & 0 deletions docs/api/array-creation.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,45 @@ $full = NDArray::full(100, [10], DType::Int32);

---

## NDArray::fromScalar()

Create a 0-dimensional (scalar) array from a single PHP value.

```php
public static function fromScalar(
float|int|bool|Complex $value,
?DType $dtype = null
): self
```

The resulting array has shape `[]` (empty shape) and contains the single value as its element. Use [`toScalar()`](/api/array-import-export#toscalar) to retrieve the value back, or let it broadcast naturally in element-wise operations.

**Parameters:**
- `float|int|bool|Complex $value` - The scalar value
- `?DType $dtype` - Data type (inferred from value if null)

**Examples:**

```php
$scalar = NDArray::fromScalar(42);
echo $scalar->shape(); // []
echo $scalar->toScalar(); // 42
echo $scalar->ndim(); // 0

// With explicit type
$floatScalar = NDArray::fromScalar(3.14, DType::Float32);

// Broadcasting works naturally
$ones = NDArray::ones([3, 4]);
$result = $ones->multiply(NDArray::fromScalar(5));
// All elements are now 5
```

**See Also:**
- [toScalar()](/api/array-import-export#toscalar) — Extract value back to PHP scalar

---

## NDArray::fromArray()

Create an NDArray from a PHP array with optional explicit shape.
Expand Down Expand Up @@ -822,6 +861,37 @@ print_r($ints->toArray()); // [1, 2, 3]

---

## cast()

Cast array to a different dtype, without copy when already the target type.

```php
public function cast(DType $dtype): self
```

Unlike [`astype()`](#astype) which always creates a new copy, this method returns the same array instance (zero-cost) when the current dtype already matches the requested one. Only when an actual type conversion is needed does it delegate to `astype()` and allocate new memory.

### Parameters

| Name | Type | Description |
|------|------|-------------|
| `$dtype` | `DType` | Target data type |

### Returns

- `NDArray` - Array in the target dtype (may be the same instance).

### Examples

```php
$arr = NDArray::array([1, 2, 3], DType::Int64);

$same = $arr->cast(DType::Int64); // No copy, same instance
$converted = $arr->cast(DType::Float64); // Copy, dtype differs
```

---

## Summary Table

| Method | Purpose | Use Case |
Expand All @@ -835,6 +905,7 @@ print_r($ints->toArray()); // [1, 2, 3]
| `onesLike()` | Ones like input | Same shape as array |
| `fullLike()` | Filled like input | Same shape as array |
| `fromArray()` | From PHP array (with shape) | Import with explicit shape |
| `fromScalar()` | 0-dimensional from value | Broadcasting, scalar-in-array context |
| `fromBuffer()` | From C pointer | FFI interoperability |
| `fromBytes()` | From binary string | File I/O, network data |
| `eye()` | Identity matrix | Linear algebra |
Expand All @@ -849,6 +920,7 @@ print_r($ints->toArray()); // [1, 2, 3]
| `randomInt()` | Random integers | Discrete random |
| `copy()` | Deep copy | Independent array from existing |
| `astype()` | Type conversion | New array with different dtype |
| `cast()` | Conditional type conversion | Same instance if dtype matches, copy otherwise |

## Next Steps

Expand Down
1 change: 1 addition & 0 deletions docs/guide/getting-started/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ $cube = NDArray::array([
$zeros = NDArray::zeros([3, 3]); // 3x3 matrix of zeros
$ones = NDArray::ones([2, 4]); // 2x4 matrix of ones
$full = NDArray::full(5, [2, 2]); // 2x2 matrix filled with 5
$scalar = NDArray::fromScalar(42); // 0-dimensional scalar array (shape [])

$identity = NDArray::eye(3); // 3x3 identity matrix
// [[1. 0. 0.]
Expand Down
10 changes: 10 additions & 0 deletions src/Functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,16 @@ function full(bool|Complex|float|int $value, array $shape, ?DType $dtype = null)
return NDArray::full($value, $shape, $dtype);
}

/**
* Create a 0-dimensional (scalar) array from a single PHP value.
*
* @see NDArray::fromScalar()
*/
function from_scalar(bool|Complex|float|int $value, ?DType $dtype = null): NDArray
{
return NDArray::fromScalar($value, $dtype);
}

/**
* Create an array from an external C buffer pointer.
*
Expand Down
17 changes: 17 additions & 0 deletions src/Traits/CreatesArrays.php
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,23 @@ public static function full(bool|Complex|float|int $value, array $shape, ?DType
return new self($outHandle, new ArrayMetadata($shape), $dtype);
}

/**
* Create a 0-dimensional (scalar) array from a single PHP value.
*
* The resulting array has shape [] and contains a single element.
* Use {@see toScalar()} to retrieve the value back, or let it broadcast
* naturally in element-wise operations.
*
* @param bool|Complex|float|int $value The scalar value
* @param null|DType $dtype Data type (inferred from value if null)
*
* @return self A 0-dimensional array
*/
public static function fromScalar(bool|Complex|float|int $value, ?DType $dtype = null): self
{
return self::full($value, [], $dtype);
}

/**
* Create array from PHP array (alias for array()).
*
Expand Down
62 changes: 62 additions & 0 deletions tests/Unit/CreationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@

namespace PhpMlKit\NDArray\Tests\Unit;

use PhpMlKit\NDArray\Complex;
use PhpMlKit\NDArray\DType;
use PhpMlKit\NDArray\Exceptions\DTypeException;
use PhpMlKit\NDArray\Exceptions\ShapeException;
use PhpMlKit\NDArray\NDArray;
use PHPUnit\Framework\TestCase;

use function PhpMlKit\NDArray\from_scalar;
use function PhpMlKit\NDArray\meshgrid;

/**
Expand Down Expand Up @@ -71,6 +73,66 @@ public function testFullInferred(): void
$this->assertSame([true, true], $arrBool->toArray());
}

public function testFromScalarInt(): void
{
$arr = NDArray::fromScalar(42);
$this->assertSame([], $arr->shape());
$this->assertSame(0, $arr->ndim());
$this->assertSame(42, $arr->toScalar());
$this->assertSame(DType::Int64, $arr->dtype());
}

public function testFromScalarFloat(): void
{
$arr = NDArray::fromScalar(3.14);
$this->assertSame(0, $arr->ndim());
$this->assertSame(3.14, $arr->toScalar());
$this->assertSame(DType::Float64, $arr->dtype());
}

public function testFromScalarBool(): void
{
$arr = NDArray::fromScalar(true);
$this->assertSame(0, $arr->ndim());
$this->assertTrue($arr->toScalar());
$this->assertSame(DType::Bool, $arr->dtype());
}

public function testFromScalarComplex(): void
{
$arr = NDArray::fromScalar(new Complex(1.0, 2.0));
$this->assertSame(0, $arr->ndim());
$result = $arr->toScalar();
$this->assertInstanceOf(Complex::class, $result);
$this->assertSame(1.0, $result->real);
$this->assertSame(2.0, $result->imag);
$this->assertSame(DType::Complex128, $arr->dtype());
}

public function testFromScalarExplicitDtype(): void
{
$arr = NDArray::fromScalar(42, DType::Float64);
$this->assertSame(0, $arr->ndim());
$this->assertSame(42.0, $arr->toScalar());
$this->assertSame(DType::Float64, $arr->dtype());
}

public function testFromScalarBroadcasts(): void
{
$scalar = NDArray::fromScalar(5);
$arr = NDArray::ones([3, 4]);
$result = $arr->multiply($scalar);
$this->assertSame([3, 4], $result->shape());
$this->assertEquals(array_fill(0, 3, array_fill(0, 4, 5)), $result->toArray());
}

public function testFromScalarGlobalFunction(): void
{
$arr = from_scalar(7.5);
$this->assertSame(0, $arr->ndim());
$this->assertSame(7.5, $arr->toScalar());
}

public function testMeshgridDenseXyIndexing(): void
{
$x = NDArray::array([1, 2, 3], DType::Int64);
Expand Down
Loading