stan-dev / stan

Stan development repository. The master branch contains the current release. The develop branch contains the latest stable development. See the Developer Process Wiki for details.
https://mc-stan.org
BSD 3-Clause "New" or "Revised" License
2.56k stars 366 forks source link

Feature request: vectorized phi() #3203

Open ksvanhorn opened 1 year ago

ksvanhorn commented 1 year ago

Stan has a vectorized $\Phi(x)$ (standard cumulative distribution function) but lacks a corresponding vectorized $\varphi(x)$ (standard normal density). You can't get this using std_normal_lpdf(), as it sums the log density values; I'm asking for something that returns a vectorized result.

One use case for a vectorized $\Phi(x)$ is in computing a quasi-hinge function based on $\Phi(x)$:

$h(x) = x \Phi(x) + \varphi(x)$.

This function has range $(0,\infty)$ with $h(x) \to 0$ as $x\to -\infty$, $h(x)/x \to 1$ as $x\to \infty$, and $h'(x) = \Phi(x)$.

bob-carpenter commented 1 year ago

We do have a vectorized erf(), so a standard normal is just a factor of sqrt(2) away. Did you want the same signatures as we provide for erf()? That'd be

vector phi(vector);
row_vector phi(row_vector);
matrix phi(matrix);
real[] phi(real[]);
...

There are other feature requests asking us to supply vectorized versions of all of our probability functions. They're useful for things like log likelihoods for leave-one-out cross-validation.

rok-cesnovar commented 1 year ago

The Phi function supports the following signatures in the latest few versions:

Phi(int) => real
Phi(real) => real
Phi(vector) => vector
Phi(row_vector) => row_vector
Phi(matrix) => matrix
Phi(array[] int) => array[] real
Phi(array[] real) => array[] real
Phi(array[] vector) => array[] vector
Phi(array[] row_vector) => array[] row_vector
Phi(array[] matrix) => array[] matrix
Phi(array[,] int) => array[,] real
Phi(array[,] real) => array[,] real
Phi(array[,] vector) => array[,] vector
Phi(array[,] row_vector) => array[,] row_vector
Phi(array[,] matrix) => array[,] matrix
Phi(array[,,] int) => array[,,] real
Phi(array[,,] real) => array[,,] real
Phi(array[,,] vector) => array[,,] vector
Phi(array[,,] row_vector) => array[,,] row_vector
Phi(array[,,] matrix) => array[,,] matrix
Phi(array[,,,] int) => array[,,,] real
Phi(array[,,,] real) => array[,,,] real
Phi(array[,,,] vector) => array[,,,] vector
Phi(array[,,,] row_vector) => array[,,,] row_vector
Phi(array[,,,] matrix) => array[,,,] matrix
Phi(array[,,,,] int) => array[,,,,] real
Phi(array[,,,,] real) => array[,,,,] real
Phi(array[,,,,] vector) => array[,,,,] vector
Phi(array[,,,,] row_vector) => array[,,,,] row_vector
Phi(array[,,,,] matrix) => array[,,,,] matrix
Phi(array[,,,,,] int) => array[,,,,,] real
Phi(array[,,,,,] real) => array[,,,,,] real
Phi(array[,,,,,] vector) => array[,,,,,] vector
Phi(array[,,,,,] row_vector) => array[,,,,,] row_vector
Phi(array[,,,,,] matrix) => array[,,,,,] matrix
Phi(array[,,,,,,] int) => array[,,,,,,] real
Phi(array[,,,,,,] real) => array[,,,,,,] real
Phi(array[,,,,,,] vector) => array[,,,,,,] vector
Phi(array[,,,,,,] row_vector) => array[,,,,,,] row_vector
Phi(array[,,,,,,] matrix) => array[,,,,,,] matrix
bob-carpenter commented 1 year ago

Thanks @rok-cesnovar. This is the list you get from using the automatic vectorization of a unary function, which will be an easy way to implement this. Better to provide an analytic gradient for reverse mode. Callingstd_normal_lpdf function to implement unary standard normal is probably too heavy.