Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@ function factory( x0, gamma ) {
if ( isnan( p ) || p < 0.0 || p > 1.0 ) {
return NaN;
}
// `p-0.5` rounds away the low-order bits of a `p` close to `0` (`tan(PI*(p-0.5)) = -1/tan(PI*p)`), so evaluate each tail from its own probability, which is exact (`1-p` for `p > 0.75`):
if ( p < 0.25 ) {
return x0 - ( gamma / tan( PI*p ) );
}
if ( p > 0.75 ) {
return x0 + ( gamma / tan( PI*( 1.0-p ) ) );
}
return x0 + ( gamma * tan( PI*( p-0.5 ) ) );
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,13 @@ function quantile( p, x0, gamma ) {
) {
return NaN;
}
// `p-0.5` rounds away the low-order bits of a `p` close to `0` (`tan(PI*(p-0.5)) = -1/tan(PI*p)`), so evaluate each tail from its own probability, which is exact (`1-p` for `p > 0.75`):
if ( p < 0.25 ) {
return x0 - ( gamma / tan( PI*p ) );
}
if ( p > 0.75 ) {
return x0 + ( gamma / tan( PI*( 1.0-p ) ) );
}
return x0 + ( gamma * tan( PI*( p-0.5 ) ) );
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,12 @@ double stdlib_base_dists_cauchy_quantile( const double p, const double x0, const
) {
return 0.0/0.0; // NaN
}
// `p-0.5` rounds away the low-order bits of a `p` close to `0` (`tan(PI*(p-0.5)) = -1/tan(PI*p)`), so evaluate each tail from its own probability, which is exact (`1-p` for `p > 0.75`):
if ( p < 0.25 ) {
return x0 - ( gamma / stdlib_base_tan( STDLIB_CONSTANT_FLOAT64_PI * p ) );
}
if ( p > 0.75 ) {
return x0 + ( gamma / stdlib_base_tan( STDLIB_CONSTANT_FLOAT64_PI * ( 1.0 - p ) ) );
}
return x0 + gamma * stdlib_base_tan( STDLIB_CONSTANT_FLOAT64_PI * ( p - 0.5 ) );
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
Distributions 0.23.8
julia 1.5
JSON 0.21

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import Distributions: quantile, Cauchy
import JSON

"""
gen( p, x0, gamma, name )

Generate fixture data and write to file.

The quantile is evaluated in extended precision from the textbook formula `x0 + gamma*tan(pi*(p - 0.5))`, as forming `p - 0.5` in double precision rounds away the low-order bits of a `p` close to `0`.

# Arguments

* `p`: input value
Expand All @@ -41,9 +42,8 @@ julia> gen( p, x0, gamma, \"data.json\" );
```
"""
function gen( p, x0, gamma, name )
z = Array{Float64}( undef, length(p) );
for i in eachindex(p)
z[ i ] = quantile( Cauchy( x0[i], gamma[i] ), p[i] );
z = setprecision( BigFloat, 2048 ) do
Float64.( BigFloat.( x0 ) .+ ( BigFloat.( gamma ) .* tan.( BigFloat( pi ) .* ( BigFloat.( p ) .- 0.5 ) ) ) )
end

# Store data to be written to file as a collection:
Expand Down Expand Up @@ -87,3 +87,9 @@ p = rand( 1000 );
x0 = rand( 1000 ) .* 1.0;
gamma = rand( 1000 ) .* 50.0;
gen( p, x0, gamma, "large_gamma.json" );

# Tails (`p` and `1-p` log-spaced; `x0 = 0`, so that the result does not also measure the rounding of `x0 + ...`):
p = vcat( exp10.( range( -300.0, stop = -1.0, length = 500 ) ), 1.0 .- exp10.( range( -16.0, stop = -1.0, length = 500 ) ) );
x0 = zeros( 1000 );
gamma = ( rand( 1000 ) .* 20.0 ) .+ 0.1;
gen( p, x0, gamma, "tails.json" );

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,9 @@

var tape = require( 'tape' );
var isnan = require( '@stdlib/math/base/assert/is-nan' );
var abs = require( '@stdlib/math/base/special/abs' );
var isAlmostSameValue = require( '@stdlib/assert/is-almost-same-value' );
var PINF = require( '@stdlib/constants/float64/pinf' );
var NINF = require( '@stdlib/constants/float64/ninf' );
var EPS = require( '@stdlib/constants/float64/eps' );
var factory = require( './../lib/factory.js' );


Expand All @@ -34,6 +33,7 @@ var factory = require( './../lib/factory.js' );
var largeGamma = require( './fixtures/julia/large_gamma.json' );
var negativeMedian = require( './fixtures/julia/negative_median.json' );
var positiveMedian = require( './fixtures/julia/positive_median.json' );
var tails = require( './fixtures/julia/tails.json' );


// TESTS //
Expand Down Expand Up @@ -131,36 +131,11 @@ tape( 'if provided a nonpositive `gamma`, the created function always returns `N
tape( 'the created function evaluates the quantile function at `p` given parameters `x0` and `gamma` (large `gamma`)', function test( t ) {
var expected;
var quantile;
var delta;
var gamma;
var tol;
var x0;
var i;
var p;
var y;
var i;

/*
* Higher tolerance than EPS because Julia gives slightly different results for
* |x| ~<= 3*pi/4:
*
* Example 1:
* x = -1.35646279095478;
* Julia (tan): -4.593961172862999
* stdlib (tan): -4.593961172863
* Mathematica: -4.59396117286300026311049650877442413097818001966176559315
*
* Example 2:
* x = 1.4710248292410089
* Julia (tan): 9.989623320530624
* stdlib (tan): 9.989623320530626
* Mathematica: 9.989623320530629158499137574831736702146195199133529403233
*
* Example 3:
* x = 1.528545878614728
* Julia (tan): 23.654302824341386
* stdlib (tan): 23.65430282434139
* Mathematica: 23.65430282434144042648214719732782590575979471046811610915...
*/

expected = largeGamma.expected;
p = largeGamma.p;
Expand All @@ -169,27 +144,19 @@ tape( 'the created function evaluates the quantile function at `p` given paramet
for ( i = 0; i < p.length; i++ ) {
quantile = factory( x0[i], gamma[i] );
y = quantile( p[i] );
if ( y === expected[i] ) {
t.strictEqual( y, expected[i], 'p: '+p[i]+', x0: '+x0[i]+', gamma: '+gamma[i]+', y: '+y+', expected: '+expected[i] );
} else {
delta = abs( y - expected[ i ] );
tol = 50.0 * EPS * abs( expected[ i ] );
t.ok( delta <= tol, 'within tolerance. p: '+p[ i ]+'. x0: '+x0[i]+'. gamma: '+gamma[i]+'. y: '+y+'. E: '+expected[ i ]+'. Δ: '+delta+'. tol: '+tol+'.' );
}
t.strictEqual( isAlmostSameValue( y, expected[ i ], 28 ), true, 'returns expected value' );
}
t.end();
});

tape( 'the created function evaluates the quantile function at `p` given `x0` and `gamma` (`x0 > 0`)', function test( t ) {
var expected;
var quantile;
var delta;
var gamma;
var tol;
var x0;
var i;
var p;
var y;
var i;

expected = positiveMedian.expected;
p = positiveMedian.p;
Expand All @@ -198,27 +165,19 @@ tape( 'the created function evaluates the quantile function at `p` given `x0` an
for ( i = 0; i < p.length; i++ ) {
quantile = factory( x0[i], gamma[i] );
y = quantile( p[i] );
if ( y === expected[i] ) {
t.strictEqual( y, expected[i], 'p: '+p[i]+', x0: '+x0[i]+', gamma: '+gamma[i]+', y: '+y+', expected: '+expected[i] );
} else {
delta = abs( y - expected[ i ] );
tol = 125.0 * EPS * abs( expected[ i ] );
t.ok( delta <= tol, 'within tolerance. p: '+p[ i ]+'. x0: '+x0[i]+'. gamma: '+gamma[i]+'. y: '+y+'. E: '+expected[ i ]+'. Δ: '+delta+'. tol: '+tol+'.' );
}
t.strictEqual( isAlmostSameValue( y, expected[ i ], 162 ), true, 'returns expected value' );
}
t.end();
});

tape( 'the created function evaluates the quantile function at `p` given `x0` and `gamma` (`x0 < 0`)', function test( t ) {
var expected;
var quantile;
var delta;
var gamma;
var tol;
var x0;
var i;
var p;
var y;
var i;

expected = negativeMedian.expected;
p = negativeMedian.p;
Expand All @@ -227,13 +186,28 @@ tape( 'the created function evaluates the quantile function at `p` given `x0` an
for ( i = 0; i < p.length; i++ ) {
quantile = factory( x0[i], gamma[i] );
y = quantile( p[i] );
if ( y === expected[i] ) {
t.strictEqual( y, expected[i], 'p: '+p[i]+', x0: '+x0[i]+', gamma: '+gamma[i]+', y: '+y+', expected: '+expected[i] );
} else {
delta = abs( y - expected[ i ] );
tol = 50.0 * EPS * abs( expected[ i ] );
t.ok( delta <= tol, 'within tolerance. p: '+p[ i ]+'. x0: '+x0[i]+'. gamma: '+gamma[i]+'. y: '+y+'. E: '+expected[ i ]+'. Δ: '+delta+'. tol: '+tol+'.' );
}
t.strictEqual( isAlmostSameValue( y, expected[ i ], 326 ), true, 'returns expected value' );
}
t.end();
});

tape( 'the created function evaluates the quantile function at `p` in the tails (`p` or `1-p` close to `0`)', function test( t ) {
var expected;
var quantile;
var gamma;
var x0;
var i;
var p;
var y;

expected = tails.expected;
p = tails.p;
x0 = tails.x0;
gamma = tails.gamma;
for ( i = 0; i < p.length; i++ ) {
quantile = factory( x0[i], gamma[i] );
y = quantile( p[i] );
t.strictEqual( isAlmostSameValue( y, expected[ i ], 2 ), true, 'returns expected value' );
}
t.end();
});
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,17 @@ var resolve = require( 'path' ).resolve;
var tape = require( 'tape' );
var tryRequire = require( '@stdlib/utils/try-require' );
var isnan = require( '@stdlib/math/base/assert/is-nan' );
var abs = require( '@stdlib/math/base/special/abs' );
var isAlmostSameValue = require( '@stdlib/assert/is-almost-same-value' );
var PINF = require( '@stdlib/constants/float64/pinf' );
var NINF = require( '@stdlib/constants/float64/ninf' );
var EPS = require( '@stdlib/constants/float64/eps' );


// FIXTURES //

var largeGamma = require( './fixtures/julia/large_gamma.json' );
var negativeMedian = require( './fixtures/julia/negative_median.json' );
var positiveMedian = require( './fixtures/julia/positive_median.json' );
var tails = require( './fixtures/julia/tails.json' );


// VARIABLES //
Expand Down Expand Up @@ -104,103 +104,75 @@ tape( 'if provided a nonpositive `gamma`, the function always returns `NaN`', op
tape( 'the function evaluates the quantile function at `p` given `x0` and `gamma` (large `gamma`)', opts, function test( t ) {
var expected;
var gamma;
var delta;
var tol;
var x0;
var i;
var p;
var y;
var i;

/*
* Higher tolerance than EPS because Julia gives slightly different results for
* |x| ~<= 3*pi/4:
*
* Example 1:
* x = -1.35646279095478;
* Julia (tan): -4.593961172862999
* stdlib (tan): -4.593961172863
* Mathematica: -4.59396117286300026311049650877442413097818001966176559315
*
* Example 2:
* x = 1.4710248292410089
* Julia (tan): 9.989623320530624
* stdlib (tan): 9.989623320530626
* Mathematica: 9.989623320530629158499137574831736702146195199133529403233
*
* Example 3:
* x = 1.528545878614728
* Julia (tan): 23.654302824341386
* stdlib (tan): 23.65430282434139
* Mathematica: 23.65430282434144042648214719732782590575979471046811610915...
*/

expected = largeGamma.expected;
p = largeGamma.p;
x0 = largeGamma.x0;
gamma = largeGamma.gamma;
for ( i = 0; i < p.length; i++ ) {
y = quantile( p[i], x0[i], gamma[i] );
if ( y === expected[i] ) {
t.strictEqual( y, expected[i], 'p: '+p[i]+', x0:'+x0[i]+', gamma: '+gamma[i]+', y: '+y+', expected: '+expected[i] );
} else {
delta = abs( y - expected[ i ] );
tol = 50.0 * EPS * abs( expected[ i ] );
t.ok( delta <= tol, 'within tolerance. p: '+p[ i ]+'. x0: '+x0[i]+'. gamma: '+gamma[i]+'. y: '+y+'. E: '+expected[ i ]+'. Δ: '+delta+'. tol: '+tol+'.' );
}
t.strictEqual( isAlmostSameValue( y, expected[ i ], 28 ), true, 'returns expected value' );
}
t.end();
});

tape( 'the function evaluates the quantile function at `p` given `x0` and `gamma` (`x0 > 0`)', opts, function test( t ) {
var expected;
var delta;
var gamma;
var tol;
var x0;
var i;
var p;
var y;
var i;

expected = positiveMedian.expected;
p = positiveMedian.p;
x0 = positiveMedian.x0;
gamma = positiveMedian.gamma;
for ( i = 0; i < p.length; i++ ) {
y = quantile( p[i], x0[i], gamma[i] );
if ( y === expected[i] ) {
t.strictEqual( y, expected[i], 'p: '+p[i]+', x0:'+x0[i]+', gamma: '+gamma[i]+', y: '+y+', expected: '+expected[i] );
} else {
delta = abs( y - expected[ i ] );
tol = 125.0 * EPS * abs( expected[ i ] );
t.ok( delta <= tol, 'within tolerance. p: '+p[ i ]+'. x0: '+x0[i]+'. gamma: '+gamma[i]+'. y: '+y+'. E: '+expected[ i ]+'. Δ: '+delta+'. tol: '+tol+'.' );
}
t.strictEqual( isAlmostSameValue( y, expected[ i ], 162 ), true, 'returns expected value' );
}
t.end();
});

tape( 'the function evaluates the quantile function at `p` given `x0` and `gamma` (`x0 < 0`)', opts, function test( t ) {
var expected;
var delta;
var gamma;
var tol;
var x0;
var i;
var p;
var y;
var i;

expected = negativeMedian.expected;
p = negativeMedian.p;
x0 = negativeMedian.x0;
gamma = negativeMedian.gamma;
for ( i = 0; i < p.length; i++ ) {
y = quantile( p[i], x0[i], gamma[i] );
if ( y === expected[i] ) {
t.strictEqual( y, expected[i], 'p: '+p[i]+', x0:'+x0[i]+', gamma: '+gamma[i]+', y: '+y+', expected: '+expected[i] );
} else {
delta = abs( y - expected[ i ] );
tol = 90.0 * EPS * abs( expected[ i ] );
t.ok( delta <= tol, 'within tolerance. p: '+p[ i ]+'. x0: '+x0[i]+'. gamma: '+gamma[i]+'. y: '+y+'. E: '+expected[ i ]+'. Δ: '+delta+'. tol: '+tol+'.' );
}
t.strictEqual( isAlmostSameValue( y, expected[ i ], 326 ), true, 'returns expected value' );
}
t.end();
});

tape( 'the function evaluates the quantile function at `p` in the tails (`p` or `1-p` close to `0`)', opts, function test( t ) {
var expected;
var gamma;
var x0;
var i;
var p;
var y;

expected = tails.expected;
p = tails.p;
x0 = tails.x0;
gamma = tails.gamma;
for ( i = 0; i < p.length; i++ ) {
y = quantile( p[i], x0[i], gamma[i] );
t.strictEqual( isAlmostSameValue( y, expected[ i ], 2 ), true, 'returns expected value' );
}
t.end();
});
Loading
Loading