Skip to content

Commit 923d8a2

Browse files
Softplus and more (#65) (#66)
* Initial commit * A little nicer * Fix the docs * Add rsqrt * Update CHANGELOG
1 parent 0f9e5b1 commit 923d8a2

18 files changed

Lines changed: 601 additions & 35 deletions

‎CHANGELOG.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Change Log
22

3+
- 4.1.0
4+
- Added `softplus()` and `erf()` special functions
5+
- Added reciprocal sqrt `rsqrt()` operation to unary interface
6+
- Added `sinh()` and `cosh()` trigonometric functions
7+
38
- 4.0.0
49
- Added `fromArray()` factory method to build from PHP array
510
- `build()` and `quick()` factory methods are now deprecated
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
namespace Tensor\Benchmarks\Functions;
4+
5+
use Tensor\Matrix;
6+
7+
/**
8+
* @Groups({"Functions"})
9+
* @BeforeMethods({"setUp"})
10+
*/
11+
class SigmoidMatrixBench
12+
{
13+
/**
14+
* @var Matrix
15+
*/
16+
protected $a;
17+
18+
public function setUp() : void
19+
{
20+
$this->a = Matrix::uniform(500, 500);
21+
}
22+
23+
/**
24+
* @Subject
25+
* @Iterations(5)
26+
* @OutputTimeUnit("seconds", precision=3)
27+
*/
28+
public function chained() : void
29+
{
30+
$this->a->negate()
31+
->exp()
32+
->addScalar(1.0)
33+
->reciprocal();
34+
}
35+
36+
/**
37+
* @Subject
38+
* @Iterations(5)
39+
* @OutputTimeUnit("seconds", precision=3)
40+
*/
41+
public function map() : void
42+
{
43+
$this->a->sigmoid();
44+
}
45+
}

‎composer.json‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@
3838
"Tensor\\": "src/"
3939
},
4040
"files": [
41-
"src/constants.php"
41+
"src/constants.php",
42+
"src/math.php"
4243
]
4344
},
4445
"autoload-dev": {

‎docs/Matrix.md‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,7 @@ See [Unary](interfaces/unary.md). Each method returns a new `Matrix`.
414414
- `square() : self` — square of the matrix element-wise
415415
- `sqrt() : self` — square root of the matrix
416416
- `reciprocal() : self` — element-wise reciprocal of the matrix
417+
- `rsqrt() : self` — element-wise reciprocal square root of the matrix, i.e. `1 / sqrt(x)`
417418
- `exp() : self` — exponential of the matrix
418419
- `expm1() : self` — exponential of each element minus 1
419420
- `log(float $base = M_E) : self` — logarithm of the matrix in the specified base
@@ -423,7 +424,6 @@ See [Unary](interfaces/unary.md). Each method returns a new `Matrix`.
423424
- `ceil() : self` — round up to the nearest integer
424425
- `sign() : self` — element-wise sign indication (`1.0`, `-1.0`, `0.0`)
425426
- `negate() : self` — take the negative of each value element-wise
426-
- `sigmoid() : self` — element-wise sigmoid of the matrix
427427

428428
## Trigonometric
429429

@@ -435,6 +435,9 @@ See [Trigonometric](interfaces/trigonometric.md). Each method returns a new `Mat
435435
- `acos()` — arc cosine of the matrix
436436
- `tan()` — tangent of the matrix
437437
- `atan()` — arc tangent of the matrix
438+
- `tanh()` — hyperbolic tangent of the matrix
439+
- `cosh()` — hyperbolic cosine of the matrix
440+
- `sinh()` — hyperbolic sine of the matrix
438441
- `rad2deg()` — convert angles from radians to degrees
439442
- `deg2rad()` — convert angles from degrees to radians
440443

@@ -456,7 +459,10 @@ See [Reductions](interfaces/reductions.md). For a `Matrix`, row-wise reductions
456459

457460
See [Special](interfaces/special.md).
458461

462+
- `sigmoid() : self` — the element-wise sigmoid of the matrix, i.e. `1 / (1 + exp(-x))`; always in `(0, 1)`
463+
- `softplus() : self` — the element-wise softplus of the matrix: `log(1 + e^{x})`; a smooth approximation of `max(0, x)` with derivative equal to the sigmoid
459464
- `softmax() : self` — the softmax of the matrix; each row is normalized independently so the elements of every row sum to `1.0` and the shape is preserved
465+
- `erf() : self` — the element-wise error function of the matrix; `erf(-x) = -erf(x)` and `erf(±5.0) ≈ ±1.0`
460466

461467
## Clipping
462468

‎docs/Vector.md‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ See [Unary](interfaces/unary.md). Each method returns a new `Vector`.
314314
- `square() : self` — square the vector
315315
- `sqrt() : self` — square root of the vector
316316
- `reciprocal() : self` — element-wise reciprocal of the vector
317+
- `rsqrt() : self` — element-wise reciprocal square root of the vector, i.e. `1 / sqrt(x)`
317318
- `exp() : self` — exponentiate each element
318319
- `expm1() : self` — exponential of each element minus 1
319320
- `log(float $base = M_E) : self` — log to the given base of each element
@@ -323,7 +324,6 @@ See [Unary](interfaces/unary.md). Each method returns a new `Vector`.
323324
- `ceil() : self` — round up to the nearest integer
324325
- `sign() : self` — element-wise sign indication (`1.0`, `-1.0`, `0.0`)
325326
- `negate() : self` — negate each value
326-
- `sigmoid() : self` — element-wise sigmoid of the vector
327327

328328
## Trigonometric
329329

@@ -336,6 +336,8 @@ See [Trigonometric](interfaces/trigonometric.md). Each method returns a new `Vec
336336
- `tan()` — tangent of the vector
337337
- `atan()` — arc tangent of the vector
338338
- `tanh()` — hyperbolic tangent of the vector
339+
- `cosh()` — hyperbolic cosine of the vector
340+
- `sinh()` — hyperbolic sine of the vector
339341
- `rad2deg()` — convert angles from radians to degrees
340342
- `deg2rad()` — convert angles from degrees to radians
341343

@@ -356,7 +358,10 @@ See [Reductions](interfaces/reductions.md). For a `Vector` these return scalar `
356358

357359
See [Special](interfaces/special.md).
358360

361+
- `sigmoid() : self` — the element-wise sigmoid of the vector, i.e. `1 / (1 + exp(-x))`; always in `(0, 1)`
362+
- `softplus() : self` — the element-wise softplus of the vector: `log(1 + e^{x})`; a smooth approximation of `max(0, x)` with derivative equal to the sigmoid
359363
- `softmax() : self` — the softmax of the vector; the whole vector is treated as a single row so the elements sum to `1.0`
364+
- `erf() : self` — the element-wise error function of the vector; `erf(-x) = -erf(x)` and `erf(±5.0) ≈ ±1.0`
360365

361366
## Clipping
362367

‎docs/interfaces/special.md‎

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,26 @@ Special tensor functions.
66

77
## Overview
88

9-
`Special` defines higher-level functions that combine a transform with a normalization across more than one element.
9+
`Special` defines higher-order transcendental functions — element-wise transforms (`sigmoid`, `softplus`) and row-wise normalizations (`softmax`).
1010

1111
```php
1212
interface Special
1313
```
1414

1515
## Methods
1616

17+
### `sigmoid() : mixed`
18+
19+
Return the element-wise sigmoid of the tensor, i.e. `1 / (1 + exp(-x))`.
20+
21+
The result is always in the open interval `(0, 1)` and saturates to `1.0` and `0.0` for large positive and negative inputs respectively.
22+
23+
### `softplus() : mixed`
24+
25+
Return the element-wise softplus of the tensor: `log(1 + exp(x))`.
26+
27+
A smooth approximation of `max(0, x)` whose derivative is the sigmoid. Computed as `x < 0: log(1 + exp(x))` and `x >= 0: x + log(1 + exp(-x))` so the exponentially large argument never overflows.
28+
1729
### `softmax() : mixed`
1830

1931
Return the softmax of the tensor.
@@ -23,3 +35,9 @@ The maximum of each normalized row is subtracted before exponentiating, so the r
2335
- For `Matrix` each row is normalized independently, so the elements of every row sum to `1.0` and the shape is preserved. Normalization is across the columns *within* each row, never across rows.
2436
- For `Vector` the whole vector is a single row, so its elements sum to `1.0`.
2537
- For `ColumnVector` the whole column is a single row, so its elements sum to `1.0`.
38+
39+
### `erf() : mixed`
40+
41+
Return the element-wise error function of the tensor: `erf(x) = (2 / sqrt(pi)) * integral_0^x exp(-t^2) dt`.
42+
43+
The result is in the range `[-1.0, 1.0]` and is an odd function: `erf(-x) = -erf(x)`. For `|x| >= 5.0` the value saturates to `sign(x)`.

‎docs/interfaces/tensor.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ interface Tensor extends ArrayLike, Arithmetic, Comparable,
2020
| [ArrayLike](arraylike.md) | `shape`, `shapeString`, `size`, `map`, `reduce`, `asArray` |
2121
| [Arithmetic](arithmetic.md) | `multiply`, `divide`, `add`, `subtract`, `pow`, `mod` |
2222
| [Comparable](comparable.md) | `equal`, `notEqual`, `greater`, `greaterEqual`, `less`, `lessEqual` |
23-
| [Unary](unary.md) | `abs`, `square`, `sqrt`, `reciprocal`, `exp`, `expm1`, `log`, `log1p`, `round`, `floor`, `ceil`, `clip`, `clipLower`, `clipUpper`, `sign`, `negate`, `sigmoid` |
24-
| [Trigonometric](trigonometric.md) | `sin`, `asin`, `cos`, `acos`, `tan`, `atan`, `tanh`, `rad2deg`, `deg2rad` |
23+
| [Unary](unary.md) | `abs`, `square`, `sqrt`, `reciprocal`, `rsqrt`, `exp`, `expm1`, `log`, `log1p`, `round`, `floor`, `ceil`, `clip`, `clipLower`, `clipUpper`, `sign`, `negate` |
24+
| [Trigonometric](trigonometric.md) | `sin`, `asin`, `cos`, `acos`, `tan`, `atan`, `tanh`, `cosh`, `sinh`, `rad2deg`, `deg2rad` |
2525
| [Reductions](reductions.md) | `sum`, `product`, `min`, `max`, `mean`, `variance`, `median`, `quantile` |
26-
| [Special](special.md) | `softmax` |
26+
| [Special](special.md) | `sigmoid`, `softplus`, `softmax`, `erf` |
2727

2828
Additionally, because `ArrayLike` extends `ArrayAccess`, `IteratorAggregate`, and `Countable`, every tensor is array-accessible, iterable, and countable.
2929

‎docs/interfaces/trigonometric.md‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
# Trigonometric
22

3-
Element-wise trigonometric functions.
3+
Element-wise trigonometric and hyperbolic functions.
44

55
- **Namespace:** `Tensor\Trigonometric`
66

77
## Overview
88

9-
`Trigonometric` defines element-wise trigonometric operations. Each method returns a new tensor of the same shape as `$this`. Angles are expressed in radians unless converting to/from degrees.
9+
`Trigonometric` defines element-wise trigonometric and hyperbolic operations. Each method returns a new tensor of the same shape as `$this`. Angles are expressed in radians unless converting to/from degrees.
1010

1111
```php
1212
interface Trigonometric
@@ -40,12 +40,20 @@ Compute the arc tangent of the tensor.
4040

4141
### `tanh() : mixed`
4242

43-
Return the hyperbolic tangent of the tensor.
43+
Return the hyperbolic tangent of the tensor: `tanh(x) = sinh(x) / cosh(x)`.
44+
45+
### `cosh() : mixed`
46+
47+
Return the hyperbolic cosine of the tensor: `cosh(x) = (e^x + e^(-x)) / 2`.
48+
49+
### `sinh() : mixed`
50+
51+
Return the hyperbolic sine of the tensor: `sinh(x) = (e^x - e^(-x)) / 2`.
4452

4553
### `rad2deg() : mixed`
4654

4755
Convert angles from radians to degrees.
4856

4957
### `deg2rad() : mixed`
5058

51-
Convert angles from degrees to radians.
59+
Convert angles from degrees to radians.

‎docs/interfaces/unary.md‎

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ Return the square root of the tensor.
3030

3131
Return the element-wise reciprocal of the tensor.
3232

33+
### `rsqrt() : mixed`
34+
35+
Return the reciprocal square root of the tensor, i.e. `1 / sqrt(x)`.
36+
3337
### `exp() : mixed`
3438

3539
Return the exponential of the tensor.
@@ -84,10 +88,4 @@ Return the element-wise sign indication (`1.0`, `-1.0`, or `0.0`).
8488

8589
### `negate() : mixed`
8690

87-
Negate the tensor, i.e. take the negative.
88-
89-
### `sigmoid() : mixed`
90-
91-
Return the element-wise sigmoid of the tensor, i.e. `1 / (1 + exp(-x))`.
92-
93-
The result is always in the open interval `(0, 1)` and saturates to `1.0` and `0.0` for large positive and negative inputs respectively.
91+
Negate the tensor, i.e. take the negative.

‎src/Matrix.php‎

Lines changed: 76 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1409,6 +1409,16 @@ public function sqrt() : self
14091409
return $this->map('sqrt');
14101410
}
14111411

1412+
/**
1413+
* Return the reciprocal square root of the matrix.
1414+
*
1415+
* @return self
1416+
*/
1417+
public function rsqrt() : self
1418+
{
1419+
return $this->map('\Tensor\rsqrt');
1420+
}
1421+
14121422
/**
14131423
* Return the exponential of the matrix.
14141424
*
@@ -1532,6 +1542,26 @@ public function tanh() : self
15321542
return $this->map('tanh');
15331543
}
15341544

1545+
/**
1546+
* Return the hyperbolic cosine of the matrix.
1547+
*
1548+
* @return self
1549+
*/
1550+
public function cosh() : self
1551+
{
1552+
return $this->map('cosh');
1553+
}
1554+
1555+
/**
1556+
* Return the hyperbolic sine of the matrix.
1557+
*
1558+
* @return self
1559+
*/
1560+
public function sinh() : self
1561+
{
1562+
return $this->map('sinh');
1563+
}
1564+
15351565
/**
15361566
* Convert angles from radians to degrees.
15371567
*
@@ -1929,10 +1959,7 @@ public function negate() : self
19291959
*/
19301960
public function sigmoid() : self
19311961
{
1932-
return $this->negate()
1933-
->exp()
1934-
->addScalar(1.0)
1935-
->reciprocal();
1962+
return $this->map('\Tensor\sigmoid');
19361963
}
19371964

19381965
/**
@@ -1943,12 +1970,55 @@ public function sigmoid() : self
19431970
*/
19441971
public function softmax() : self
19451972
{
1946-
$a = $this->subtractColumnVector($this->max())
1947-
->exp();
1973+
$a = $this->subtractColumnVector($this->max())->exp();
19481974

19491975
return $a->divideColumnVector($a->sum());
19501976
}
19511977

1978+
/**
1979+
* Return the element-wise error function of the matrix.
1980+
*
1981+
* @return self
1982+
*/
1983+
public function erf() : self
1984+
{
1985+
$b = [];
1986+
1987+
foreach ($this->a as $rowA) {
1988+
$rowB = [];
1989+
1990+
foreach ($rowA as $valueA) {
1991+
$rowB[] = erf($valueA);
1992+
}
1993+
1994+
$b[] = $rowB;
1995+
}
1996+
1997+
return self::fromArray($b, false);
1998+
}
1999+
2000+
/**
2001+
* Return the element-wise softplus of the matrix.
2002+
*
2003+
* @return self
2004+
*/
2005+
public function softplus() : self
2006+
{
2007+
$b = [];
2008+
2009+
foreach ($this->a as $rowA) {
2010+
$rowB = [];
2011+
2012+
foreach ($rowA as $valueA) {
2013+
$rowB[] = softplus($valueA);
2014+
}
2015+
2016+
$b[] = $rowB;
2017+
}
2018+
2019+
return self::fromArray($b, false);
2020+
}
2021+
19522022
/**
19532023
* Attach matrix b above this matrix.
19542024
*

0 commit comments

Comments
 (0)