diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/README.md b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/README.md new file mode 100644 index 000000000000..d9e17ff1c823 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/README.md @@ -0,0 +1,519 @@ + + +# dsgdTrainer + +> Train a linear model via [stochastic gradient descent][stochastic-gradient-descent]. + +
+ +## Usage + +```javascript +var dsgdTrainer = require( '@stdlib/ml/strided/dsgd-trainer' ); +``` + +#### dsgdTrainer( order, M, N, X, LDX, y, sy, w, sw, ws, sws, params ) + +Trains a linear model via stochastic gradient descent, where `X` is an `M` by `N` input matrix, `y` is a target vector, and `w` is a weight vector. + +```javascript +var Float64Array = require( '@stdlib/array/float64' ); +var Params = require( '@stdlib/ml/base/sgd/params/float64' ); + +var params = new Params({ + 'penaltyParams': new Float64Array( [ 1.0e-4, 0.0 ] ), + 'learningRateParams': new Float64Array( [ 0.01, 0.0 ] ), + 'lossFunctionParams': new Float64Array( [ 0.1 ] ), + 'intercept': 0.0, + 'maxIter': 5, + 'penalty': 'l2', + 'learningRate': 'constant', + 'lossFunction': 'hinge', + 'fitIntercept': true +}); + +var X = new Float64Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 ] ); +var y = new Float64Array( [ 1.0, -1.0, 1.0 ] ); +var w = new Float64Array( 3 ); +var ws = new Float64Array( 2 ); + +var out = dsgdTrainer( 'row-major', 3, 2, X, 2, y, 1, w, 1, ws, 1, params ); +// returns + +var bool = ( out === w ); +// returns true +``` + +The function has the following parameters: + +- **order**: storage layout. +- **M**: number of samples (i.e., number of rows in the matrix `X`). +- **N**: number of features (i.e., number of columns in the matrix `X`). +- **X**: input matrix stored in linear memory as a [`Float64Array`][@stdlib/array/float64]. +- **LDX**: stride of the first dimension of `X` (a.k.a., leading dimension of the matrix `X`). +- **y**: target [`Float64Array`][@stdlib/array/float64]. +- **sy**: stride length for `y`. +- **w**: weight [`Float64Array`][@stdlib/array/float64]. Upon return, `w` contains the fitted model coefficients. +- **sw**: stride length for `w`. +- **ws**: workspace [`Float64Array`][@stdlib/array/float64]. +- **sws**: stride length for `ws`. +- **params**: [SGD trainer parameters object][@stdlib/ml/base/sgd/params/float64]. + +The stride parameters determine how elements are accessed. For example, to iterate over every other element of `y`, + +```javascript +var Float64Array = require( '@stdlib/array/float64' ); +var Params = require( '@stdlib/ml/base/sgd/params/float64' ); + +var params = new Params({ + 'penaltyParams': new Float64Array( [ 1.0e-4, 0.0 ] ), + 'learningRateParams': new Float64Array( [ 0.01, 0.0 ] ), + 'lossFunctionParams': new Float64Array( [ 0.1 ] ), + 'intercept': 0.0, + 'maxIter': 5, + 'penalty': 'l2', + 'learningRate': 'constant', + 'lossFunction': 'hinge', + 'fitIntercept': true +}); + +var X = new Float64Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 ] ); +var y = new Float64Array( [ 1.0, 0.0, -1.0, 0.0, 1.0, 0.0 ] ); +var w = new Float64Array( 3 ); +var ws = new Float64Array( 2 ); + +var out = dsgdTrainer( 'row-major', 3, 2, X, 2, y, 2, w, 1, ws, 1, params ); +// returns +``` + +Note that indexing is relative to the first index. To introduce an offset, use [`typed array`][mdn-typed-array] views. + + + +```javascript +var Float64Array = require( '@stdlib/array/float64' ); +var Params = require( '@stdlib/ml/base/sgd/params/float64' ); + +var params = new Params({ + 'penaltyParams': new Float64Array( [ 1.0e-4, 0.0 ] ), + 'learningRateParams': new Float64Array( [ 0.01, 0.0 ] ), + 'lossFunctionParams': new Float64Array( [ 0.1 ] ), + 'intercept': 0.0, + 'maxIter': 5, + 'penalty': 'l2', + 'learningRate': 'constant', + 'lossFunction': 'hinge', + 'fitIntercept': true +}); + +var X = new Float64Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 ] ); + +// Initial arrays... +var y0 = new Float64Array( [ 0.0, 1.0, -1.0, 1.0 ] ); +var w0 = new Float64Array( 4 ); + +// Create offset views... +var y1 = new Float64Array( y0.buffer, y0.BYTES_PER_ELEMENT*1 ); // start at 2nd element +var w1 = new Float64Array( w0.buffer, w0.BYTES_PER_ELEMENT*1 ); // start at 2nd element + +var ws = new Float64Array( 2 ); + +dsgdTrainer( 'row-major', 3, 2, X, 2, y1, 1, w1, 1, ws, 1, params ); +// w0 => +``` + + + +#### dsgdTrainer.ndarray( M, N, X, sx1, sx2, ox, y, sy, oy, w, sw, ow, ws, sws, ows, params ) + +Trains a linear model via stochastic gradient descent using alternative indexing semantics. + +```javascript +var Float64Array = require( '@stdlib/array/float64' ); +var Params = require( '@stdlib/ml/base/sgd/params/float64' ); + +var params = new Params({ + 'penaltyParams': new Float64Array( [ 1.0e-4, 0.0 ] ), + 'learningRateParams': new Float64Array( [ 0.01, 0.0 ] ), + 'lossFunctionParams': new Float64Array( [ 0.1 ] ), + 'intercept': 0.0, + 'maxIter': 5, + 'penalty': 'l2', + 'learningRate': 'constant', + 'lossFunction': 'hinge', + 'fitIntercept': true +}); + +var X = new Float64Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 ] ); +var y = new Float64Array( [ 1.0, -1.0, 1.0 ] ); +var w = new Float64Array( 3 ); +var ws = new Float64Array( 2 ); + +var out = dsgdTrainer.ndarray( 3, 2, X, 2, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, params ); +// returns +``` + +The function has the following additional parameters: + +- **sx1**: stride of the first dimension of `X`. +- **sx2**: stride of the second dimension of `X`. +- **ox**: starting index for `X`. +- **oy**: starting index for `y`. +- **ow**: starting index for `w`. +- **ows**: starting index for `ws`. + +While [`typed array`][mdn-typed-array] views mandate a view offset based on the underlying buffer, the offset parameters support indexing semantics based on starting indices. For example, + +```javascript +var Float64Array = require( '@stdlib/array/float64' ); +var Params = require( '@stdlib/ml/base/sgd/params/float64' ); + +var params = new Params({ + 'penaltyParams': new Float64Array( [ 1.0e-4, 0.0 ] ), + 'learningRateParams': new Float64Array( [ 0.01, 0.0 ] ), + 'lossFunctionParams': new Float64Array( [ 0.1 ] ), + 'intercept': 0.0, + 'maxIter': 5, + 'penalty': 'l2', + 'learningRate': 'constant', + 'lossFunction': 'hinge', + 'fitIntercept': true +}); + +var X = new Float64Array( [ 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 ] ); +var y = new Float64Array( [ 0.0, 1.0, -1.0, 1.0 ] ); +var w = new Float64Array( 4 ); +var ws = new Float64Array( 2 ); + +var out = dsgdTrainer.ndarray( 3, 2, X, 2, 1, 1, y, 1, 1, w, 1, 1, ws, 1, 0, params ); +// returns +``` + +
+ + + +
+ +## Notes + +- Both functions perform in-place training, mutating and returning `w`. + +- When `params.fitIntercept` is `true`, `w` must have `N+1` indexed elements, as the fitted intercept is stored in the element following the `N` feature weights (i.e., at index `ow + (sw*N)`). + +- `ws` must have `N` indexed elements. The workspace tracks the cumulative L1 shrinkage applied to each feature and should be zero-filled prior to training. + +- Upon return, `w` contains effective (i.e., fully rescaled) weights. + +- If `M` or `N` is equal to `0`, both functions return `w` unchanged. + +- `params` is an [SGD trainer parameters object][@stdlib/ml/base/sgd/params/float64] specifying the [penalty][@stdlib/ml/base/sgd/penalties], the [learning rate scheduler][@stdlib/ml/base/sgd/learning-rates], the [loss function][@stdlib/ml/base/sgd/loss-functions], and their respective parameter lists, along with the initial intercept, the maximum number of epochs, and whether to fit an intercept. + +
+ + + +
+ +## Examples + + + +```javascript +var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); +var uniform = require( '@stdlib/random/array/uniform' ); +var Float64Array = require( '@stdlib/array/float64' ); +var Params = require( '@stdlib/ml/base/sgd/params/float64' ); +var dsgdTrainer = require( '@stdlib/ml/strided/dsgd-trainer' ); + +var opts = { + 'dtype': 'float64' +}; + +var M = 50; +var N = 5; + +var X = uniform( M*N, -5.0, 5.0, opts ); +var y = discreteUniform( M, -1, 1, opts ); + +// Allocate `N+1` elements in order to accommodate the fitted intercept: +var w = new Float64Array( N+1 ); +var workspace = new Float64Array( N ); + +var params = new Params({ + 'penaltyParams': new Float64Array( [ 1.0e-4, 0.15 ] ), + 'learningRateParams': new Float64Array( [ 0.01, 0.25 ] ), + 'lossFunctionParams': new Float64Array( [ 0.1 ] ), + 'intercept': 0.0, + 'maxIter': 500, + 'penalty': 'l2', + 'learningRate': 'constant', + 'lossFunction': 'hinge', + 'fitIntercept': true +}); + +dsgdTrainer( 'row-major', M, N, X, N, y, 1, w, 1, workspace, 1, params ); +console.log( w ); +``` + +
+ + + + + +* * * + +
+ +## C APIs + + + +
+ +
+ + + + + +
+ +### Usage + +```c +#include "stdlib/ml/strided/dsgd_trainer.h" +``` + +#### stdlib_strided_dsgd_trainer( layout, M, N, \*X, LDX, \*y, strideY, \*w, strideW, \*ws, strideWS, \*params ) + +Trains a linear model via stochastic gradient descent, where `X` is an `M` by `N` input matrix, `y` is a target vector, and `w` is a weight vector. + +```c +#include "stdlib/ml/base/sgd/learning_rates.h" +#include "stdlib/ml/base/sgd/loss_functions.h" +#include "stdlib/ml/base/sgd/penalties.h" +#include "stdlib/blas/base/shared.h" +#include + +const double X[] = { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 }; +const double y[] = { 1.0, -1.0, 1.0 }; +double w[] = { 0.0, 0.0, 0.0 }; +double ws[] = { 0.0, 0.0 }; + +struct stdlib_ml_sgd_float64_params params = { + .penaltyParams = { 1.0e-4, 0.0 }, + .learningRateParams = { 0.01, 0.0 }, + .lossFunctionParams = { 0.1 }, + .intercept = 0.0, + .maxIter = 500, + .penalty = STDLIB_ML_SGD_L2, + .learningRate = STDLIB_ML_SGD_CONSTANT, + .lossFunction = STDLIB_ML_SGD_HINGE, + .fitIntercept = true +}; + +stdlib_strided_dsgd_trainer( CblasRowMajor, 3, 2, X, 2, y, 1, w, 1, ws, 1, ¶ms ); +``` + +The function accepts the following arguments: + +- **layout**: `[in] CBLAS_LAYOUT` storage layout of `X`. +- **M**: `[in] CBLAS_INT` number of samples. +- **N**: `[in] CBLAS_INT` number of features. +- **X**: `[in] double*` input matrix. +- **LDX**: `[in] CBLAS_INT` stride of the major dimension of `X` (a.k.a., leading dimension of the matrix `X`). +- **y**: `[in] double*` target array. +- **strideY**: `[in] CBLAS_INT` stride length for `y`. +- **w**: `[inout] double*` weight array. +- **strideW**: `[in] CBLAS_INT` stride length for `w`. +- **ws**: `[inout] double*` workspace array. +- **strideWS**: `[in] CBLAS_INT` stride length for `ws`. +- **params**: `[in] struct stdlib_ml_sgd_float64_params*` parameters. + +```c +double* stdlib_strided_dsgd_trainer( const CBLAS_LAYOUT layout, const CBLAS_INT M, const CBLAS_INT N, const double *X, const CBLAS_INT LDX, const double *y, const CBLAS_INT strideY, double *w, const CBLAS_INT strideW, double *ws, const CBLAS_INT strideWS, const struct stdlib_ml_sgd_float64_params *params ) +``` + +#### stdlib_strided_dsgd_trainer_ndarray( M, N, \*X, sx1, sx2, ox, \*y, sy, oy, \*w, sw, ow, \*ws, sws, ows, \*params ) + +Trains a linear model via stochastic gradient descent using alternative indexing semantics. + +```c +#include "stdlib/ml/base/sgd/learning_rates.h" +#include "stdlib/ml/base/sgd/loss_functions.h" +#include "stdlib/ml/base/sgd/penalties.h" +#include "stdlib/blas/base/shared.h" +#include + +const double X[] = { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 }; +const double y[] = { 1.0, -1.0, 1.0 }; +double w[] = { 0.0, 0.0, 0.0 }; +double ws[] = { 0.0, 0.0 }; + +struct stdlib_ml_sgd_float64_params params = { + .penaltyParams = { 1.0e-4, 0.0 }, + .learningRateParams = { 0.01, 0.0 }, + .lossFunctionParams = { 0.1 }, + .intercept = 0.0, + .maxIter = 500, + .penalty = STDLIB_ML_SGD_L2, + .learningRate = STDLIB_ML_SGD_CONSTANT, + .lossFunction = STDLIB_ML_SGD_HINGE, + .fitIntercept = true +}; + +stdlib_strided_dsgd_trainer_ndarray( 3, 2, X, 2, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, ¶ms ); +``` + +The function accepts the following arguments: + +- **M**: `[in] CBLAS_INT` number of samples. +- **N**: `[in] CBLAS_INT` number of features. +- **X**: `[in] double*` input matrix. +- **strideX1**: `[in] CBLAS_INT` stride of the first dimension of `X`. +- **strideX2**: `[in] CBLAS_INT` stride of the second dimension of `X`. +- **offsetX**: `[in] CBLAS_INT` starting index for `X`. +- **y**: `[in] double*` target array. +- **strideY**: `[in] CBLAS_INT` stride length for `y`. +- **offsetY**: `[in] CBLAS_INT` starting index for `y`. +- **w**: `[inout] double*` weight array. +- **strideW**: `[in] CBLAS_INT` stride length for `w`. +- **offsetW**: `[in] CBLAS_INT` starting index for `w`. +- **ws**: `[inout] double*` workspace array. +- **strideWS**: `[in] CBLAS_INT` stride length for `ws`. +- **offsetWS**: `[in] CBLAS_INT` starting index for `ws`. +- **params**: `[in] struct stdlib_ml_sgd_float64_params*` parameters. + +```c +double* stdlib_strided_dsgd_trainer_ndarray( const CBLAS_INT M, const CBLAS_INT N, const double *X, const CBLAS_INT strideX1, const CBLAS_INT strideX2, const CBLAS_INT offsetX, const double *y, const CBLAS_INT strideY, const CBLAS_INT offsetY, double *w, const CBLAS_INT strideW, const CBLAS_INT offsetW, double *ws, const CBLAS_INT strideWS, const CBLAS_INT offsetWS, const struct stdlib_ml_sgd_float64_params *params ) +``` + +
+ + + + + +
+ +
+ + + + + +
+ +### Examples + +```c +#include "stdlib/ml/strided/dsgd_trainer.h" +#include "stdlib/ml/base/sgd/learning_rates.h" +#include "stdlib/ml/base/sgd/loss_functions.h" +#include "stdlib/ml/base/sgd/penalties.h" +#include "stdlib/blas/base/shared.h" +#include +#include + +int main( void ) { + // Define a 3x2 input matrix stored in row-major order: + const double X[ 3*2 ] = { + 1.0, 2.0, + 3.0, 4.0, + 5.0, 6.0 + }; + + // Define a target vector: + const double y[ 3 ] = { 1.0, -1.0, 1.0 }; + + // Specify the number of samples and features: + const CBLAS_INT M = 3; + const CBLAS_INT N = 2; + + // Allocate `N+1` elements in order to accommodate the fitted intercept: + double w[ 2+1 ] = { 0.0, 0.0, 0.0 }; + + // Allocate a zero-filled workspace: + double ws[ 2 ] = { 0.0, 0.0 }; + + // Define the training parameters: + struct stdlib_ml_sgd_float64_params params = { + .penaltyParams = { 1.0e-4, 0.0 }, + .learningRateParams = { 0.01, 0.0 }, + .lossFunctionParams = { 0.1 }, + .intercept = 0.0, + .maxIter = 500, + .penalty = STDLIB_ML_SGD_L2, + .learningRate = STDLIB_ML_SGD_CONSTANT, + .lossFunction = STDLIB_ML_SGD_HINGE, + .fitIntercept = true + }; + + // Train a linear model: + stdlib_strided_dsgd_trainer( CblasRowMajor, M, N, X, N, y, 1, w, 1, ws, 1, ¶ms ); + + // Print the result: + for ( int i = 0; i < N; i++ ) { + printf( "w[ %i ] = %lf\n", i, w[ i ] ); + } + printf( "intercept = %lf\n", w[ N ] ); +} +``` + +
+ + + +
+ + + + + + + + + + + + + + diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/benchmark/benchmark.js b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/benchmark/benchmark.js new file mode 100644 index 000000000000..ff9a4e3eee43 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/benchmark/benchmark.js @@ -0,0 +1,122 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2018 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var bench = require( '@stdlib/bench' ); +var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); +var format = require( '@stdlib/string/format' ); +var isnan = require( '@stdlib/math/base/assert/is-nan' ); +var pow = require( '@stdlib/math/base/special/pow' ); +var floor = require( '@stdlib/math/base/special/floor' ); +var Float64Array = require( '@stdlib/array/float64' ); +var Params = require( '@stdlib/ml/base/sgd/params/float64' ); +var pkg = require( './../package.json' ).name; +var dsgdTrainer = require( './../lib/dsgd_trainer.js' ); + + +// VARIABLES // + +var options = { + 'dtype': 'float64' +}; + + +// FUNCTIONS // + +/** +* Creates a benchmark function. +* +* @private +* @param {PositiveInteger} N - array length +* @returns {Function} benchmark function +*/ +function createBenchmark( N ) { + var ws = new Float64Array( N ); + var x = discreteUniform( N*N, -10.0, 10.0, options ); + var y = discreteUniform( N, -10.0, 10.0, options ); + var w = discreteUniform( N, -0.05, 0.05, options ); + return benchmark; + + /** + * Benchmark function. + * + * @private + * @param {Benchmark} b - benchmark instance + */ + function benchmark( b ) { + var params; + var z; + var i; + + params = new Params(); + + params.penalty = 'l2'; + params.learningRate = 'constant'; + params.lossFunction = 'hinge'; + params.intercept = 0.0; + params.maxIter = 500; + params.penaltyParams = new Float64Array( [ 2.5, 0.0 ] ); + params.learningRateParams = new Float64Array( [ 0.01, 0.0 ] ); + params.lossFunctionParams = new Float64Array( [ 0.0 ] ); + params.fitIntercept = true; + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + z = dsgdTrainer( 'row-major', N, N, x, N, y, 1, w, 1, ws, 1, params ); + if ( isnan( z[ i%z.length ] ) ) { + b.fail( 'should not return NaN' ); + } + } + b.toc(); + if ( isnan( z[ i%z.length ] ) ) { + b.fail( 'should not return NaN' ); + } + b.pass( 'benchmark finished' ); + b.end(); + } +} + + +// MAIN // + +/** +* Main execution sequence. +* +* @private +*/ +function main() { + var min; + var max; + var N; + var f; + var i; + + min = 1; // 10^min + max = 6; // 10^max + + for ( i = min; i <= max; i++ ) { + N = floor( pow( pow( 10, i ), 1.0/2.0 ) ); + f = createBenchmark( N ); + bench( format( '%s:size=%d', pkg, N*N ), f ); + } +} + +main(); diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/benchmark/benchmark.native.js b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/benchmark/benchmark.native.js new file mode 100644 index 000000000000..d6dcc9d1d96c --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/benchmark/benchmark.native.js @@ -0,0 +1,127 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2025 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var resolve = require( 'path' ).resolve; +var bench = require( '@stdlib/bench' ); +var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); +var format = require( '@stdlib/string/format' ); +var isnan = require( '@stdlib/math/base/assert/is-nan' ); +var pow = require( '@stdlib/math/base/special/pow' ); +var floor = require( '@stdlib/math/base/special/floor' ); +var Float64Array = require( '@stdlib/array/float64' ); +var Params = require( '@stdlib/ml/base/sgd/params/float64' ); +var tryRequire = require( '@stdlib/utils/try-require' ); +var pkg = require( './../package.json' ).name; + + +// VARIABLES // + +var dsgdTrainer = tryRequire( resolve( __dirname, './../lib/dsgd_trainer.native.js' ) ); +var opts = { + 'skip': ( dsgdTrainer instanceof Error ) +}; +var options = { + 'dtype': 'float64' +}; + + +// FUNCTIONS // + +/** +* Creates a benchmark function. +* +* @private +* @param {PositiveInteger} N - array length +* @returns {Function} benchmark function +*/ +function createBenchmark( N ) { + var ws = new Float64Array( N ); + var x = discreteUniform( N*N, -10.0, 10.0, options ); + var y = discreteUniform( N, -10.0, 10.0, options ); + var w = discreteUniform( N, -0.05, 0.05, options ); + return benchmark; + + /** + * Benchmark function. + * + * @private + * @param {Benchmark} b - benchmark instance + */ + function benchmark( b ) { + var params; + var z; + var i; + + params = new Params(); + + params.penalty = 'l2'; + params.learningRate = 'constant'; + params.lossFunction = 'hinge'; + params.intercept = 0.0; + params.maxIter = 500; + params.penaltyParams = new Float64Array( [ 2.5, 0.0 ] ); + params.learningRateParams = new Float64Array( [ 0.01, 0.0 ] ); + params.lossFunctionParams = new Float64Array( [ 0.0 ] ); + params.fitIntercept = true; + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + z = dsgdTrainer( 'row-major', N, N, x, N, y, 1, w, 1, ws, 1, params ); + if ( isnan( z[ i%z.length ] ) ) { + b.fail( 'should not return NaN' ); + } + } + b.toc(); + if ( isnan( z[ i%z.length ] ) ) { + b.fail( 'should not return NaN' ); + } + b.pass( 'benchmark finished' ); + b.end(); + } +} + + +// MAIN // + +/** +* Main execution sequence. +* +* @private +*/ +function main() { + var min; + var max; + var N; + var f; + var i; + + min = 1; // 10^min + max = 6; // 10^max + + for ( i = min; i <= max; i++ ) { + N = floor( pow( pow( 10, i ), 1.0/2.0 ) ); + f = createBenchmark( N ); + bench( format( '%s::native:size=%d', pkg, N*N ), opts, f ); + } +} + +main(); diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/benchmark/benchmark.ndarray.js b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/benchmark/benchmark.ndarray.js new file mode 100644 index 000000000000..99f2db595df3 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/benchmark/benchmark.ndarray.js @@ -0,0 +1,122 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2018 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var bench = require( '@stdlib/bench' ); +var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); +var format = require( '@stdlib/string/format' ); +var isnan = require( '@stdlib/math/base/assert/is-nan' ); +var pow = require( '@stdlib/math/base/special/pow' ); +var floor = require( '@stdlib/math/base/special/floor' ); +var Float64Array = require( '@stdlib/array/float64' ); +var Params = require( '@stdlib/ml/base/sgd/params/float64' ); +var pkg = require( './../package.json' ).name; +var dsgdTrainer = require( './../lib/ndarray.js' ); + + +// VARIABLES // + +var options = { + 'dtype': 'float64' +}; + + +// FUNCTIONS // + +/** +* Creates a benchmark function. +* +* @private +* @param {PositiveInteger} N - array length +* @returns {Function} benchmark function +*/ +function createBenchmark( N ) { + var ws = new Float64Array( N ); + var x = discreteUniform( N*N, -10.0, 10.0, options ); + var y = discreteUniform( N, -10.0, 10.0, options ); + var w = discreteUniform( N, -0.05, 0.05, options ); + return benchmark; + + /** + * Benchmark function. + * + * @private + * @param {Benchmark} b - benchmark instance + */ + function benchmark( b ) { + var params; + var z; + var i; + + params = new Params(); + + params.penalty = 'l2'; + params.learningRate = 'constant'; + params.lossFunction = 'hinge'; + params.intercept = 0.0; + params.maxIter = 500; + params.penaltyParams = new Float64Array( [ 2.5, 0.0 ] ); + params.learningRateParams = new Float64Array( [ 0.01, 0.0 ] ); + params.lossFunctionParams = new Float64Array( [ 0.0 ] ); + params.fitIntercept = true; + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + z = dsgdTrainer( N, N, x, N, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, params ); // eslint-disable-line max-len + if ( isnan( z[ i%z.length ] ) ) { + b.fail( 'should not return NaN' ); + } + } + b.toc(); + if ( isnan( z[ i%z.length ] ) ) { + b.fail( 'should not return NaN' ); + } + b.pass( 'benchmark finished' ); + b.end(); + } +} + + +// MAIN // + +/** +* Main execution sequence. +* +* @private +*/ +function main() { + var min; + var max; + var N; + var f; + var i; + + min = 1; // 10^min + max = 6; // 10^max + + for ( i = min; i <= max; i++ ) { + N = floor( pow( pow( 10, i ), 1.0/2.0 ) ); + f = createBenchmark( N ); + bench( format( '%s:ndarray:size=%d', pkg, N*N ), f ); + } +} + +main(); diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/benchmark/benchmark.ndarray.native.js b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/benchmark/benchmark.ndarray.native.js new file mode 100644 index 000000000000..14856b87d1ba --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/benchmark/benchmark.ndarray.native.js @@ -0,0 +1,127 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2025 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var resolve = require( 'path' ).resolve; +var bench = require( '@stdlib/bench' ); +var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); +var format = require( '@stdlib/string/format' ); +var isnan = require( '@stdlib/math/base/assert/is-nan' ); +var pow = require( '@stdlib/math/base/special/pow' ); +var floor = require( '@stdlib/math/base/special/floor' ); +var Float64Array = require( '@stdlib/array/float64' ); +var Params = require( '@stdlib/ml/base/sgd/params/float64' ); +var tryRequire = require( '@stdlib/utils/try-require' ); +var pkg = require( './../package.json' ).name; + + +// VARIABLES // + +var dsgdTrainer = tryRequire( resolve( __dirname, './../lib/ndarray.native.js' ) ); +var opts = { + 'skip': ( dsgdTrainer instanceof Error ) +}; +var options = { + 'dtype': 'float64' +}; + + +// FUNCTIONS // + +/** +* Creates a benchmark function. +* +* @private +* @param {PositiveInteger} N - array length +* @returns {Function} benchmark function +*/ +function createBenchmark( N ) { + var ws = new Float64Array( N ); + var x = discreteUniform( N*N, -10.0, 10.0, options ); + var y = discreteUniform( N, -10.0, 10.0, options ); + var w = discreteUniform( N, -0.05, 0.05, options ); + return benchmark; + + /** + * Benchmark function. + * + * @private + * @param {Benchmark} b - benchmark instance + */ + function benchmark( b ) { + var params; + var z; + var i; + + params = new Params(); + + params.penalty = 'l2'; + params.learningRate = 'constant'; + params.lossFunction = 'hinge'; + params.intercept = 0.0; + params.maxIter = 500; + params.penaltyParams = new Float64Array( [ 2.5, 0.0 ] ); + params.learningRateParams = new Float64Array( [ 0.01, 0.0 ] ); + params.lossFunctionParams = new Float64Array( [ 0.0 ] ); + params.fitIntercept = true; + + b.tic(); + for ( i = 0; i < b.iterations; i++ ) { + z = dsgdTrainer( N, N, x, N, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, params ); // eslint-disable-line max-len + if ( isnan( z[ i%z.length ] ) ) { + b.fail( 'should not return NaN' ); + } + } + b.toc(); + if ( isnan( z[ i%z.length ] ) ) { + b.fail( 'should not return NaN' ); + } + b.pass( 'benchmark finished' ); + b.end(); + } +} + + +// MAIN // + +/** +* Main execution sequence. +* +* @private +*/ +function main() { + var min; + var max; + var N; + var f; + var i; + + min = 1; // 10^min + max = 6; // 10^max + + for ( i = min; i <= max; i++ ) { + N = floor( pow( pow( 10, i ), 1.0/2.0 ) ); + f = createBenchmark( N ); + bench( format( '%s::native:ndarray:size=%d', pkg, N*N ), opts, f ); + } +} + +main(); diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/benchmark/c/Makefile b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/benchmark/c/Makefile new file mode 100644 index 000000000000..cce2c865d7ad --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/benchmark/c/Makefile @@ -0,0 +1,146 @@ +#/ +# @license Apache-2.0 +# +# Copyright (c) 2025 The Stdlib Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#/ + +# VARIABLES # + +ifndef VERBOSE + QUIET := @ +else + QUIET := +endif + +# Determine the OS ([1][1], [2][2]). +# +# [1]: https://en.wikipedia.org/wiki/Uname#Examples +# [2]: http://stackoverflow.com/a/27776822/2225624 +OS ?= $(shell uname) +ifneq (, $(findstring MINGW,$(OS))) + OS := WINNT +else +ifneq (, $(findstring MSYS,$(OS))) + OS := WINNT +else +ifneq (, $(findstring CYGWIN,$(OS))) + OS := WINNT +else +ifneq (, $(findstring Windows_NT,$(OS))) + OS := WINNT +endif +endif +endif +endif + +# Define the program used for compiling C source files: +ifdef C_COMPILER + CC := $(C_COMPILER) +else + CC := gcc +endif + +# Define the command-line options when compiling C files: +CFLAGS ?= \ + -std=c99 \ + -O3 \ + -Wall \ + -pedantic + +# Determine whether to generate position independent code ([1][1], [2][2]). +# +# [1]: https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html#Code-Gen-Options +# [2]: http://stackoverflow.com/questions/5311515/gcc-fpic-option +ifeq ($(OS), WINNT) + fPIC ?= +else + fPIC ?= -fPIC +endif + +# List of includes (e.g., `-I /foo/bar -I /beep/boop/include`): +INCLUDE ?= + +# List of source files: +SOURCE_FILES ?= + +# List of libraries (e.g., `-lopenblas -lpthread`): +LIBRARIES ?= + +# List of library paths (e.g., `-L /foo/bar -L /beep/boop`): +LIBPATH ?= + +# List of C targets: +c_targets := benchmark.length.out + + +# RULES # + +#/ +# Compiles source files. +# +# @param {string} [C_COMPILER] - C compiler (e.g., `gcc`) +# @param {string} [CFLAGS] - C compiler options +# @param {(string|void)} [fPIC] - compiler flag determining whether to generate position independent code (e.g., `-fPIC`) +# @param {string} [INCLUDE] - list of includes (e.g., `-I /foo/bar -I /beep/boop/include`) +# @param {string} [SOURCE_FILES] - list of source files +# @param {string} [LIBPATH] - list of library paths (e.g., `-L /foo/bar -L /beep/boop`) +# @param {string} [LIBRARIES] - list of libraries (e.g., `-lopenblas -lpthread`) +# +# @example +# make +# +# @example +# make all +#/ +all: $(c_targets) + +.PHONY: all + +#/ +# Compiles C source files. +# +# @private +# @param {string} CC - C compiler (e.g., `gcc`) +# @param {string} CFLAGS - C compiler options +# @param {(string|void)} fPIC - compiler flag determining whether to generate position independent code (e.g., `-fPIC`) +# @param {string} INCLUDE - list of includes (e.g., `-I /foo/bar`) +# @param {string} SOURCE_FILES - list of source files +# @param {string} LIBPATH - list of library paths (e.g., `-L /foo/bar`) +# @param {string} LIBRARIES - list of libraries (e.g., `-lopenblas`) +#/ +$(c_targets): %.out: %.c + $(QUIET) $(CC) $(CFLAGS) $(fPIC) $(INCLUDE) -o $@ $(SOURCE_FILES) $< $(LIBPATH) -lm $(LIBRARIES) + +#/ +# Runs compiled benchmarks. +# +# @example +# make run +#/ +run: $(c_targets) + $(QUIET) ./$< + +.PHONY: run + +#/ +# Removes generated files. +# +# @example +# make clean +#/ +clean: + $(QUIET) -rm -f *.o *.out + +.PHONY: clean diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/benchmark/c/benchmark.length.c b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/benchmark/c/benchmark.length.c new file mode 100644 index 000000000000..647045598fda --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/benchmark/c/benchmark.length.c @@ -0,0 +1,256 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2025 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "stdlib/ml/strided/dsgd_trainer.h" +#include "stdlib/ml/base/sgd/penalties.h" +#include "stdlib/ml/base/sgd/learning_rates.h" +#include "stdlib/ml/base/sgd/loss_functions.h" +#include +#include +#include +#include +#include + +#define NAME "dsgd_trainer" +#define ITERATIONS 10000000 +#define REPEATS 3 +#define MIN 1 +#define MAX 6 + +/** +* Prints the TAP version. +*/ +static void print_version( void ) { + printf( "TAP version 13\n" ); +} + +/** +* Prints the TAP summary. +* +* @param total total number of tests +* @param passing total number of passing tests +*/ +static void print_summary( int total, int passing ) { + printf( "#\n" ); + printf( "1..%d\n", total ); // TAP plan + printf( "# total %d\n", total ); + printf( "# pass %d\n", passing ); + printf( "#\n" ); + printf( "# ok\n" ); +} + +/** +* Prints benchmarks results. +* +* @param iterations number of iterations +* @param elapsed elapsed time in seconds +*/ +static void print_results( int iterations, double elapsed ) { + double rate = (double)iterations / elapsed; + printf( " ---\n" ); + printf( " iterations: %d\n", iterations ); + printf( " elapsed: %0.9f\n", elapsed ); + printf( " rate: %0.9f\n", rate ); + printf( " ...\n" ); +} + +/** +* Returns a clock time. +* +* @return clock time +*/ +static double tic( void ) { + struct timeval now; + gettimeofday( &now, NULL ); + return (double)now.tv_sec + (double)now.tv_usec/1.0e6; +} + +/** +* Generates a random number on the interval [min,max). +* +* @param min minimum value (inclusive) +* @param max maximum value (exclusive) +* @return random number +*/ +static double random_uniform( const double min, const double max ) { + double v = (double)rand() / ( (double)RAND_MAX + 1.0 ); + return min + ( v*(max-min) ); +} + +/** +* Runs a benchmark. +* +* @param iterations number of iterations +* @param N number of features +* @return elapsed time in seconds +*/ +static double benchmark1( int iterations, int N ) { + double elapsed; + double *ws; + double *x; + double *y; + double *w; + double t; + int i; + int j; + + x = (double *) malloc( N * N * sizeof( double ) ); + y = (double *) malloc( N * sizeof( double ) ); + w = (double *) malloc( N * sizeof( double ) ); + ws = (double *) malloc( N * sizeof( double ) ); + for ( i = 0; i < N; i++ ) { + y[ i ] = random_uniform( -10.0, 10.0 ); // sgd regression + for ( j = 0; j < N; j++ ) { + w[ ( i*N ) + j ] = random_uniform( -0.05, 0.05 ); + } + x[ i ] = random_uniform( -10.0, 10.0 ); + } + struct stdlib_ml_sgd_float64_params params = { + .penaltyParams = { 2.5, 0.0 }, + .learningRateParams = { 0.01, 0.0 }, + .lossFunctionParams = { 0.0 }, + .intercept = 0.0, + .maxIter = 500, + .penalty = STDLIB_ML_SGD_L2, + .learningRate = STDLIB_ML_SGD_CONSTANT, + .lossFunction = STDLIB_ML_SGD_HINGE, + .fitIntercept = true + }; + + + t = tic(); + for ( i = 0; i < iterations; i++ ) { + for ( j = 0; j < N; j++ ) { + ws[ j ] = 0.0; + } + stdlib_strided_dsgd_trainer( CblasRowMajor, N, N, x, N, y, 1, w, 1, ws, 1, ¶ms ); + if ( y[ i%N ] != y[ i%N ] ) { + printf( "should not return NaN\n" ); + break; + } + } + elapsed = tic() - t; + if ( y[ i%N ] != y[ i%N ] ) { + printf( "should not return NaN\n" ); + } + free( x ); + free( y ); + free( w ); + free( ws ); + return elapsed; +} + +/** +* Runs a benchmark. +* +* @param iterations number of iterations +* @param N number of features +* @return elapsed time in seconds +*/ +static double benchmark2( int iterations, int N ) { + double elapsed; + double *ws; + double *x; + double *y; + double *w; + double t; + int i; + int j; + + x = (double *) malloc( N * N * sizeof( double ) ); + y = (double *) malloc( N * sizeof( double ) ); + w = (double *) malloc( N * sizeof( double ) ); + ws = (double *) malloc( N * sizeof( double ) ); + for ( i = 0; i < N; i++ ) { + y[ i ] = random_uniform( -10.0, 10.0 ); // sgd regression + for ( j = 0; j < N; j++ ) { + w[ ( i*N ) + j ] = random_uniform( -0.05, 0.05 ); + } + x[ i ] = random_uniform( -10.0, 10.0 ); + } + struct stdlib_ml_sgd_float64_params params = { + .penaltyParams = { 2.5, 0.0 }, + .learningRateParams = { 0.01, 0.0 }, + .lossFunctionParams = { 0.0 }, + .intercept = 0.0, + .maxIter = 500, + .penalty = 2, // l2 + .learningRate = 0, // constant + .lossFunction = 0, // hinge + .fitIntercept = true + }; + + + t = tic(); + for ( i = 0; i < iterations; i++ ) { + for ( j = 0; j < N; j++ ) { + ws[ j ] = 0.0; + } + stdlib_strided_dsgd_trainer_ndarray( N, N, x, N, N, 0, y, 1, 0, w, 1, 0, ws, 1, 0, ¶ms ); + if ( y[ i%N ] != y[ i%N ] ) { + printf( "should not return NaN\n" ); + break; + } + } + elapsed = tic() - t; + if ( y[ i%N ] != y[ i%N ] ) { + printf( "should not return NaN\n" ); + } + free( x ); + free( y ); + free( w ); + free( ws ); + return elapsed; +} + +/** +* Main execution sequence. +*/ +int main( void ) { + double elapsed; + int count; + int iter; + int N; + int i; + int j; + + // Use the current time to seed the random number generator: + srand( time( NULL ) ); + + print_version(); + count = 0; + for ( i = MIN; i <= MAX; i++ ) { + N = floor( pow( pow( 10, i ), 1.0/2.0 ) ); + iter = ITERATIONS / pow( 10, i-1 ); + for ( j = 0; j < REPEATS; j++ ) { + count += 1; + printf( "# c::%s:size=%d\n", NAME, N*N ); + elapsed = benchmark1( iter, N ); + print_results( iter, elapsed ); + printf( "ok %d benchmark finished\n", count ); + } + for ( j = 0; j < REPEATS; j++ ) { + count += 1; + printf( "# c::%s:ndarray:size=%d\n", NAME, N*N ); + elapsed = benchmark2( iter, N ); + print_results( iter, elapsed ); + printf( "ok %d benchmark finished\n", count ); + } + } + print_summary( count, count ); +} diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/docs/repl.txt b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/docs/repl.txt new file mode 100644 index 000000000000..5f39e8690d66 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/docs/repl.txt @@ -0,0 +1,224 @@ + +{{alias}}( order, M, N, X, LDX, y, sy, w, sw, ws, sws, params ) + Trains a linear model via stochastic gradient descent. + + Indexing is relative to the first index. To introduce an offset, use typed + array views. + + If `M` or `N` is equal to `0`, the function returns `w` unchanged. + + Parameters + ---------- + order: string + Row-major (C-style) or column-major (Fortran-style) order. + + M: integer + Number of samples. + + N: integer + Number of features. + + X: Float64Array + Input matrix. + + LDX: integer + Stride of the first dimension of `X` (a.k.a., leading dimension of the + matrix `X`). + + y: Float64Array + Target array. + + sy: integer + Stride length for `y`. + + w: Float64Array + Weight array. + + sw: integer + Stride length for `w`. + + ws: Float64Array + Workspace array. + + sws: integer + Stride length for `ws`. + + params: Object + Params object. + + Returns + ------- + w: Float64Array + Weight array. + + Examples + -------- + // Standard usage: + > var ord = 'row-major'; + > var M = 50; + > var N = 5; + > var lambda = 2.5; + > var eta0 = 0.01; + > + > var y = {{alias:@stdlib/random/array/discrete-uniform}}( M, 0.0, 30.0 ); + > var X = {{alias:@stdlib/random/array/discrete-uniform}}( M*N, -5.0, 5.0 ); + > var w = {{alias:@stdlib/random/array/discrete-uniform}}( N, -0.05, 0.05 ); + > var workspace = new {{alias:@stdlib/array/float64}}( N ); + > + > var params = {{alias:@stdlib/ml/base/sgd/params/float64}}({ + ... 'penaltyParams': new Float64Array( [ lambda, 0.0 ] ), + ... 'learningRateParams': new Float64Array( [ eta0, 0.0 ] ), + ... 'intercept': 0.0, + ... 'maxIter': 500, + ... 'penalty': 'l2', + ... 'learningRate': 'constant', + ... 'lossFunction': 'hinge', + ... 'fitIntercept': true + > }); + > {{alias}}( ord, M, N, X, N, y, 1, w, 1, workspace, 1, params ); + [ ... ] + + // Advanced indexing: + > var ord = 'row-major'; + > var M = 50; + > var N = 5; + > var lambda = 2.5; + > var eta0 = 0.01; + > + > var y = {{alias:@stdlib/random/array/discrete-uniform}}( M, 0.0, 30.0 ); + > var X = {{alias:@stdlib/random/array/discrete-uniform}}( M*N, -5.0, 5.0 ); + > var w = {{alias:@stdlib/random/array/discrete-uniform}}( N, -0.05, 0.05 ); + > var workspace = new {{alias:@stdlib/array/float64}}( N ); + > + > var params = {{alias:@stdlib/ml/base/sgd/params/float64}}({ + ... 'penaltyParams': new Float64Array( [ lambda, 0.0 ] ), + ... 'learningRateParams': new Float64Array( [ eta0, 0.0 ] ), + ... 'intercept': 0.0, + ... 'maxIter': 500, + ... 'penalty': 'l2', + ... 'learningRate': 'constant', + ... 'lossFunction': 'hinge', + ... 'fitIntercept': true + }); + > {{alias}}( M, N, X, N, y, -1, w, -1, workspace, -1, params ); + [ ... ] + + // Using typed array views: + > var ord = 'row-major'; + > var M = 50; + > var N = 5; + > var lambda = 2.5; + > var eta0 = 0.01; + > + > var y0 = {{alias:@stdlib/random/array/discrete-uniform}}( M, 0.0, 30.0 ); + > var X0 = {{alias:@stdlib/random/array/discrete-uniform}}( M*N, -5.0, 5.0 ); + > var w0 = {{alias:@stdlib/random/array/discrete-uniform}}( N, -0.05, 0.05 ); + > var workspace = new {{alias:@stdlib/array/float64}}( N ); + > + > var y1 = new {{alias:@stdlib/array/float64}}( y0.buffer, y0.BYTES_PER_ELEMENT*1 ); + > var w1 = new {{alias:@stdlib/array/float64}}( w0.buffer, w0.BYTES_PER_ELEMENT*1 ); + > var params = {{alias:@stdlib/ml/base/sgd/params/float64}}({ + ... 'penaltyParams': new Float64Array( [ lambda, 0.0 ] ), + ... 'learningRateParams': new Float64Array( [ eta0, 0.0 ] ), + ... 'intercept': 0.0, + ... 'maxIter': 500, + ... 'penalty': 'l2', + ... 'learningRate': 'constant', + ... 'lossFunction': 'hinge', + ... 'fitIntercept': true + }); + > {{alias}}( M, N, X, N, y1, -1, w1, -1, workspace, -1, params ); + [ ... ] + + +{{alias}}.ndarray( M,N, X, sx1,sx2,ox, y, sy,oy, w, sw,ow, ws, sws,ows, params ) + Trains a linear model via stochastic gradient descent using alternative + indexing semantics. + + While typed array views mandate a view offset based on the underlying + buffer, the offset parameters support indexing semantics based on starting + indices. + + Parameters + ---------- + M: integer + Number of samples. + + N: integer + Number of features. + + X: Float64Array + Input matrix. + + sx1: integer + First stride length for `X`. + + sx2: integer + Second stride length for `X`. + + ox: integer + Starting index for `X`. + + y: Float64Array + Target array. + + sy: integer + Stride length for `y`. + + oy: integer + Starting index for `y`. + + w: Float64Array + Weight array. + + sw: integer + Stride length for `w`. + + ow: integer + Starting index for `w`. + + ws: Float64Array + Workspace array. + + sws: integer + Stride length for `ws`. + + ows: integer + Starting index for `ws`. + + params: Object + Params object. + + Returns + ------- + w: Float64Array + Weight array. + + Examples + -------- + > var ord = 'row-major'; + > var M = 50; + > var N = 5; + > var lambda = 2.5; + > var eta0 = 0.01; + > + > var y = {{alias:@stdlib/random/array/discrete-uniform}}( M, 0.0, 30.0 ); + > var X = {{alias:@stdlib/random/array/discrete-uniform}}( M*N, -5.0, 5.0 ); + > var w = {{alias:@stdlib/random/array/discrete-uniform}}( N, -0.05, 0.05 ); + > var workspace = new {{alias:@stdlib/array/float64}}( N ); + > + > var params = {{alias:@stdlib/ml/base/sgd/params/float64}}({ + ... 'penaltyParams': new Float64Array( [ lambda, 0.0 ] ), + ... 'learningRateParams': new Float64Array( [ eta0, 0.0 ] ), + ... 'intercept': 0.0, + ... 'maxIter': 500, + ... 'penalty': 'l2', + ... 'learningRate': 'constant', + ... 'lossFunction': 'hinge', + ... 'fitIntercept': true + > }); + > {{alias}}( M, N, X, N, 1, 0, y, 1, 0, w, 1, 0, workspace, 1, 0, params ); + [ ... ] + + See Also + -------- diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/docs/types/index.d.ts b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/docs/types/index.d.ts new file mode 100644 index 000000000000..129d2edd4a0d --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/docs/types/index.d.ts @@ -0,0 +1,311 @@ +/* +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +// TypeScript Version: 4.1 + +/// + +import { Layout } from '@stdlib/types/blas'; + +/** +* Interface describing SGD trainer parameters. +*/ +interface Params { + /** + * Parameters specific to the regularization function being used. + * + * ## Notes + * + * - Must be an array having length `2`, with any unused elements set to zero. The expected array contents depend on the penalty: + * + * - **l1**: `[ lambda, 0.0 ]` + * - **l2**: `[ lambda, 0.0 ]` + * - **elasticnet**: `[ lambda, l1Ratio ]` + * - **none**: `[ 0.0, 0.0 ]` (unused) + * + * where: + * + * - **lambda**: regularization parameter which determines the amount of shrinkage inflicted on the model coefficients. + * - **l1Ratio**: mixing parameter on the interval `[0,1]` which determines the relative contribution of the L1 and L2 penalties. + */ + penaltyParams?: Float64Array; + + /** + * Parameters specific to the learning rate scheduler being used. + * + * ## Notes + * + * - Must be an array having length `2`, with any unused elements set to zero. The expected array contents depend on the learning rate scheduler: + * + * - **basic**: `[ 0.0, 0.0 ]` (unused) + * - **constant**: `[ eta0, 0.0 ]` + * - **invscaling**: `[ eta0, powerT ]` + * - **pegasos**: `[ lambda, 0.0 ]` + * + * where: + * + * - **eta0**: initial learning rate. + * - **powerT**: exponent controlling how quickly the learning rate decreases. + * - **lambda**: regularization parameter. + */ + learningRateParams?: Float64Array; + + /** + * Parameters specific to the loss function being used. + * + * ## Notes + * + * - Must be an array having length `1`. The expected array contents depend on the loss function: + * + * - **epsilon-insensitive**: `[ epsilon ]` + * - **squared-epsilon-insensitive**: `[ epsilon ]` + * - **huber**: `[ threshold ]` + * - all other loss functions: `[ 0.0 ]` (unused) + * + * where: + * + * - **epsilon**: insensitivity parameter (i.e., errors whose absolute value is less than `epsilon` incur no penalty). + * - **threshold**: error magnitude at which the loss transitions from squared-error loss to linear loss. + */ + lossFunctionParams?: Float64Array; + + /** + * Initial intercept value. + * + * ## Notes + * + * - Only applicable when `fitIntercept` is `true`. + */ + intercept?: number; + + /** + * Maximum number of iterations to run. + */ + maxIter?: number; + + /** + * Regularization function. + */ + penalty?: number; + + /** + * Learning rate scheduler. + */ + learningRate?: number; + + /** + * Loss function. + */ + lossFunction?: number; + + /** + * Boolean indicating whether to include intercept. + * + * ## Notes + * + * - If `true`, an element equal to one is implicitly added to each provided feature vector. If `false`, the model assumes that feature vectors are already centered. + */ + fitIntercept?: boolean; +} + +/** +* Interface describing `dsgdTrainer`. +*/ +interface Routine { + /** + * Trains a linear model via stochastic gradient descent. + * + * @param order - storage layout + * @param M - number of samples + * @param N - number of features + * @param X - input matrix + * @param LDX - stride of the first dimension of `X` (a.k.a., leading dimension of the matrix `X`) + * @param y - target array + * @param strideY - stride length for `y` + * @param w - weight array + * @param strideW - stride length for `w` + * @param workspace - workspace array of size `N` for tracking total L1 shrinkage applied to each feature + * @param strideWS - stride length for `workspace` + * @param params - parameters object + * @returns `w` + * + * @example + * var Float64Array = require( '@stdlib/array/float64' ); + * var Params = require( '@stdlib/ml/base/sgd/params/float64' ); + * var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); + * + * var M = 50; + * var N = 5; + * var lambda = 2.5; + * var eta0 = 0.01; + * + * var params = new Params({ + * 'penaltyParams': new Float64Array( [ lambda, 0.0 ] ), + * 'learningRateParams': new Float64Array( [ eta0, 0.0 ] ), + * 'intercept': 0.0, + * 'maxIter': 500, + * 'penalty': 'l2', + * 'learningRate': 'constant', + * 'lossFunction': 'hinge', + * 'fitIntercept': true + * }); + * + * var y = discreteUniform( M, 0.0, 30.0 ); + * var x = discreteUniform( M*N, -5.0, 5.0 ); + * var w = discreteUniform( N, -0.05, 0.05 ); + * var workspace = new Float64Array( N ); + * + * var out = dsgdTrainer( 'row-major', M, N, x, N, y, 1, w, 1, workspace, 1, params ); + * returns + */ + ( order: Layout, M: number, N: number, X: Float64Array, LDX: number, y: Float64Array, strideY: number, w: Float64Array, strideW: number, ws: Float64Array, strideWS: number, params: Params ): Float64Array; + + /** + * Trains a linear model via stochastic gradient descent using alternative indexing semantics. + * + * @param M - number of samples + * @param N - number of features + * @param X - input matrix + * @param strideX1 - stride of the first dimension of `X` + * @param strideX2 - stride of the second dimension of `X` + * @param offsetX - starting index for `X` + * @param y - target array + * @param strideY - stride length for `y` + * @param offsetY - starting index for `y` + * @param w - weight array + * @param strideW - stride length for `w` + * @param offsetW - starting index for `w` + * @param workspace - workspace array + * @param strideWS - stride length for `workspace` + * @param offsetWS - starting index for `workspace` + * @param {Params} params - parameters object + * @returns `y` + * + * @example + * var Float64Array = require( '@stdlib/array/float64' ); + * var Params = require( '@stdlib/ml/base/sgd/params/float64' ); + * var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); + * + * var M = 50; + * var N = 5; + * var lambda = 2.5; + * var eta0 = 0.01; + * + * var params = new Params({ + * 'penaltyParams': new Float64Array( [ lambda, 0.0 ] ), + * 'learningRateParams': new Float64Array( [ eta0, 0.0 ] ), + * 'intercept': 0.0, + * 'maxIter': 500, + * 'penalty': 'l2', + * 'learningRate': 'constant', + * 'lossFunction': 'hinge', + * 'fitIntercept': true + * }); + * + * var y = discreteUniform( M, 0.0, 30.0 ); + * var X = discreteUniform( M*N, -5.0, 5.0 ); + * var w = discreteUniform( N, -0.05, 0.05 ); + * var workspace = new Float64Array( N ); + * + * var out = dsgdTrainer.ndarray( M, N, X, N, 1, 0, y, 1, 0, w, 1, 0, workspace, 1, 0, params ); + * returns + */ + ndarray( M: number, N: number, X: Float64Array, strideX1: number, strideX2: number, offsetX: number, y: Float64Array, strideY: number, offset: number, w: Float64Array, strideW: number, offsetW: number, ws: Float64Array, strideWS: number, offsetWS: number, params: Params ): Float64Array; +} + +/** +* Trains a linear model via stochastic gradient descent. +* +* @param order - storage layout +* @param M - number of samples +* @param N - number of features +* @param X - input matrix +* @param LDX - stride of the first dimension of `X` (a.k.a., leading dimension of the matrix `X`) +* @param y - target array +* @param strideY - stride length for `y` +* @param w - weight array +* @param strideW - stride length for `w` +* @param workspace - workspace array of size `N` for tracking total L1 shrinkage applied to each feature +* @param strideWS - stride length for `workspace` +* @param params - parameters object +* @returns `w` +* +* @example +* var Float64Array = require( '@stdlib/array/float64' ); +* var Params = require( '@stdlib/ml/base/sgd/params/float64' ); +* var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); +* +* var M = 50; +* var N = 5; +* var lambda = 2.5; +* var eta0 = 0.01; +* +* var params = new Params({ +* 'penaltyParams': new Float64Array( [ lambda, 0.0 ] ), +* 'learningRateParams': new Float64Array( [ eta0, 0.0 ] ), +* 'intercept': 0.0, +* 'maxIter': 500, +* 'penalty': 'l2', +* 'learningRate': 'constant', +* 'lossFunction': 'hinge', +* 'fitIntercept': true +* }); +* +* var y = discreteUniform( M, 0.0, 30.0 ); +* var x = discreteUniform( M*N, -5.0, 5.0 ); +* var w = discreteUniform( N, -0.05, 0.05 ); +* var workspace = new Float64Array( N ); +* +* var out = dsgdTrainer( 'row-major', M, N, x, N, y, 1, w, 1, workspace, 1, params ); +* returns +* +* @example +* var Float64Array = require( '@stdlib/array/float64' ); +* var Params = require( '@stdlib/ml/base/sgd/params/float64' ); +* var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); +* +* var M = 50; +* var N = 5; +* var lambda = 2.5; +* var eta0 = 0.01; +* +* var params = new Params({ +* 'penaltyParams': new Float64Array( [ lambda, 0.0 ] ), +* 'learningRateParams': new Float64Array( [ eta0, 0.0 ] ), +* 'intercept': 0.0, +* 'maxIter': 500, +* 'penalty': 'l2', +* 'learningRate': 'constant', +* 'lossFunction': 'hinge', +* 'fitIntercept': true +* }); +* +* var y = discreteUniform( M, 0.0, 30.0 ); +* var X = discreteUniform( M*N, -5.0, 5.0 ); +* var w = discreteUniform( N, -0.05, 0.05 ); +* var workspace = new Float64Array( N ); +* +* var out = dsgdTrainer.ndarray( M, N, X, N, 1, 0, y, 1, 0, w, 1, 0, workspace, 1, 0, params ); +* returns +*/ +declare var dsgdTrainer: Routine; + + +// EXPORTS // + +export = dsgdTrainer; diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/docs/types/test.ts b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/docs/types/test.ts new file mode 100644 index 000000000000..81d6bc70eec3 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/docs/types/test.ts @@ -0,0 +1,564 @@ +/* +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import Float64Params = require( '@stdlib/ml/base/sgd/params/float64' ); +import dsgdTrainer = require( './index' ); + + +// TESTS // + + +// The function returns a Float64Array... +{ + const x = new Float64Array( 10*10 ); + const y = new Float64Array( 10 ); + const w = new Float64Array( 10 ); + const ws = new Float64Array( 10 ); + + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectType Float64Array +} + +// The compiler throws an error if the function is provided a first argument which is not a string... +{ + const x = new Float64Array( 10*10 ); + const y = new Float64Array( 10 ); + const w = new Float64Array( 10 ); + const ws = new Float64Array( 10 ); + + dsgdTrainer( 10, 10, 10, x, 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( true, 10, 10, x, 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( false, 10, 10, x, 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( null, 10, 10, x, 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( undefined, 10, 10, x, 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( [], 10, 10, x, 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( {}, 10, 10, x, 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( ( x: number ): number => x, 10, 10, x, 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError +} + +// The compiler throws an error if the function is provided a second argument which is not a number... +{ + const x = new Float64Array( 10*10 ); + const y = new Float64Array( 10 ); + const w = new Float64Array( 10 ); + const ws = new Float64Array( 10 ); + + dsgdTrainer( 'row-major', '10', 10, x, 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', true, 10, x, 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', false, 10, x, 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', null, 10, x, 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', undefined, 10, x, 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', [ '1' ], 10, x, 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', {}, 10, x, 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', ( x: number ): number => x, 10, x, 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError +} + +// The compiler throws an error if the function is provided a third argument which is not a number... +{ + const x = new Float64Array( 10*10 ); + const y = new Float64Array( 10 ); + const w = new Float64Array( 10 ); + const ws = new Float64Array( 10 ); + + dsgdTrainer( 'row-major', 10, '10', x, 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, true, x, 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, false, x, 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, null, x, 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, undefined, x, 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, [], x, 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, {}, x, 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, ( x: number ): number => x, x, 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError +} + +// The compiler throws an error if the function is provided a fourth argument which is not a Float64Array... +{ + const y = new Float64Array( 10 ); + const w = new Float64Array( 10 ); + const ws = new Float64Array( 10 ); + + dsgdTrainer( 'row-major', 10, 10, '10', 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, true, 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, false, 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, null, 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, undefined, 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, [], 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, {}, 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, ( x: number ): number => x, 10, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError +} + +// The compiler throws an error if the function is provided a fifth argument which is not a number... +{ + const x = new Float64Array( 10*10 ); + const y = new Float64Array( 10 ); + const w = new Float64Array( 10 ); + const ws = new Float64Array( 10 ); + + dsgdTrainer( 'row-major', 10, 10, x, '10', y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, true, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, false, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, null, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, undefined, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, [], y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, {}, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, ( x: number ): number => x, y, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError +} + +// The compiler throws an error if the function is provided a sixth argument which is not a Float64Array... +{ + const x = new Float64Array( 10*10 ); + const w = new Float64Array( 10 ); + const ws = new Float64Array( 10 ); + + dsgdTrainer( 'row-major', 10, 10, x, 10, 10, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, '10', 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, true, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, false, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, null, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, undefined, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, [ '1' ], 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, {}, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, ( x: number ): number => x, 1, w, 1, ws, 1, new Float64Params() ); // $ExpectError +} + +// The compiler throws an error if the function is provided a seventh argument which is not a number... +{ + const x = new Float64Array( 10*10 ); + const y = new Float64Array( 10 ); + const w = new Float64Array( 10 ); + const ws = new Float64Array( 10 ); + + dsgdTrainer( 'row-major', 10, 10, x, 10, y, '10', w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, true, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, false, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, null, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, undefined, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, [], w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, {}, w, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, ( x: number ): number => x, w, 1, ws, 1, new Float64Params() ); // $ExpectError +} + +// The compiler throws an error if the function is provided an eighth argument which is not a Float64Array... +{ + const x = new Float64Array( 10*10 ); + const y = new Float64Array( 10 ); + const ws = new Float64Array( 10 ); + + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, 10, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, '10', 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, true, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, false, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, null, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, undefined, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, [ '1' ], 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, {}, 1, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, ( x: number ): number => x, 1, ws, 1, new Float64Params() ); // $ExpectError +} + +// The compiler throws an error if the function is provided a ninth argument which is not a number... +{ + const x = new Float64Array( 10*10 ); + const y = new Float64Array( 10 ); + const w = new Float64Array( 10 ); + const ws = new Float64Array( 10 ); + + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, '10', ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, true, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, false, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, null, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, undefined, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, [], ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, {}, ws, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, ( x: number ): number => x, ws, 1, new Float64Params() ); // $ExpectError +} + +// The compiler throws an error if the function is provided a tenth argument which is not a Float64Array... +{ + const x = new Float64Array( 10*10 ); + const y = new Float64Array( 10 ); + const w = new Float64Array( 10 ); + + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, 1, '10', 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, 1, true, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, 1, false, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, 1, null, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, 1, undefined, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, 1, [], 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, 1, {}, 1, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, 1, ( x: number ): number => x, 1, new Float64Params() ); // $ExpectError +} + +// The compiler throws an error if the function is provided an eleventh argument which is not a number... +{ + const x = new Float64Array( 10*10 ); + const y = new Float64Array( 10 ); + const w = new Float64Array( 10 ); + const ws = new Float64Array( 10 ); + + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, 1, ws, 10, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, 1, ws, '10', new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, 1, ws, true, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, 1, ws, false, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, 1, ws, null, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, 1, ws, undefined, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, 1, ws, [ '1' ], new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, 1, ws, {}, new Float64Params() ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, 1, ws, ( x: number ): number => x, new Float64Params() ); // $ExpectError +} + +// The compiler throws an error if the function is provided a twelfth argument which is not a params object... +{ + const x = new Float64Array( 10*10 ); + const y = new Float64Array( 10 ); + const w = new Float64Array( 10 ); + const ws = new Float64Array( 10 ); + + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, 1, ws, 1, 10 ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, 1, ws, 1, '10' ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, 1, ws, 1, true ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, 1, ws, 1, false ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, 1, ws, 1, null ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, 1, ws, 1, undefined ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, 1, ws, 1, [ '1' ] ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, 1, ws, 1, {} ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, 1, ws, 1, ( x: number ): number => x ); // $ExpectError +} + +// The compiler throws an error if the function is provided an unsupported number of arguments... +{ + const x = new Float64Array( 10*10 ); + const y = new Float64Array( 10 ); + const w = new Float64Array( 10 ); + const ws = new Float64Array( 10 ); + + dsgdTrainer(); // $ExpectError + dsgdTrainer( 'row-major' ); // $ExpectError + dsgdTrainer( 'row-major', 10 ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10 ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10 ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1 ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, 1 ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, 1, ws ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, 1, ws, 1 ); // $ExpectError + dsgdTrainer( 'row-major', 10, 10, x, 10, y, 1, w, 1, ws, 1, new Float64Params(), 10 ); // $ExpectError +} + +// Attached to main export is an `ndarray` method which returns a Float64Array... +{ + const x = new Float64Array( 10*10 ); + const y = new Float64Array( 10 ); + const w = new Float64Array( 10 ); + const ws = new Float64Array( 10 ); + + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectType Float64Array +} + +// The compiler throws an error if the function is provided a first argument which is not a number... +{ + const x = new Float64Array( 10*10 ); + const y = new Float64Array( 10 ); + const w = new Float64Array( 10 ); + const ws = new Float64Array( 10 ); + + dsgdTrainer.ndarray( '10', 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( true, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( false, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( null, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( undefined, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( [ '1' ], 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( {}, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( ( x: number ): number => x, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError +} + +// The compiler throws an error if the function is provided a second argument which is not a number... +{ + const x = new Float64Array( 10*10 ); + const y = new Float64Array( 10 ); + const w = new Float64Array( 10 ); + const ws = new Float64Array( 10 ); + + dsgdTrainer.ndarray( 10, '10', x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, true, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, false, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, null, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, undefined, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, [], x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, {}, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, ( x: number ): number => x, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError +} + +// The compiler throws an error if the function is provided a third argument which is not a Float64Array... +{ + const y = new Float64Array( 10 ); + const w = new Float64Array( 10 ); + const ws = new Float64Array( 10 ); + + dsgdTrainer.ndarray( 10, 10, '10', 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, true, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, false, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, null, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, undefined, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, [], 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, {}, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, ( x: number ): number => x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError +} + +// The compiler throws an error if the function is provided a fourth argument which is not a number... +{ + const x = new Float64Array( 10*10 ); + const y = new Float64Array( 10 ); + const w = new Float64Array( 10 ); + const ws = new Float64Array( 10 ); + + dsgdTrainer.ndarray( 10, 10, x, '10', 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, true, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, false, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, null, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, undefined, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, [], 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, {}, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, ( x: number ): number => x, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError +} + +// The compiler throws an error if the function is provided a fifth argument which is not a number... +{ + const x = new Float64Array( 10*10 ); + const y = new Float64Array( 10 ); + const w = new Float64Array( 10 ); + const ws = new Float64Array( 10 ); + + dsgdTrainer.ndarray( 10, 10, x, 10, '10', 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, true, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, false, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, null, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, undefined, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, [], 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, {}, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, ( x: number ): number => x, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError +} + +// The compiler throws an error if the function is provided a sixth argument which is not a number... +{ + const x = new Float64Array( 10*10 ); + const y = new Float64Array( 10 ); + const w = new Float64Array( 10 ); + const ws = new Float64Array( 10 ); + + dsgdTrainer.ndarray( 10, 10, x, 10, 1, '10', y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, true, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, false, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, null, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, undefined, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, [], y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, {}, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, ( x: number ): number => x, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError +} + +// The compiler throws an error if the function is provided a seventh argument which is not a Float64Array... +{ + const x = new Float64Array( 10*10 ); + const w = new Float64Array( 10 ); + const ws = new Float64Array( 10 ); + + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, '10', 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, true, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, false, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, null, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, undefined, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, [], 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, {}, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, ( x: number ): number => x, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError +} + +// The compiler throws an error if the function is provided an eighth argument which is not a number... +{ + const x = new Float64Array( 10*10 ); + const y = new Float64Params( 10 ); + const w = new Float64Array( 10 ); + const ws = new Float64Array( 10 ); + + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, '10', 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, true, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, false, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, null, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, undefined, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, [], 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, {}, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, ( x: number ): number => x, 0, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError +} + +// The compiler throws an error if the function is provided a ninth argument which is not a number... +{ + const x = new Float64Array( 10*10 ); + const y = new Float64Params( 10 ); + const w = new Float64Array( 10 ); + const ws = new Float64Array( 10 ); + + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, '10', w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, true, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, false, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, null, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, undefined, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, [], w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, {}, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, ( x: number ): number => x, w, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError +} + +// The compiler throws an error if the function is provided a tenth argument which is not a Float64Array... +{ + const x = new Float64Array( 10*10 ); + const y = new Float64Params( 10 ); + const ws = new Float64Array( 10 ); + + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, '10', 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, true, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, false, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, null, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, undefined, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, [], 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, {}, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, ( x: number ): number => x, 1, 0, ws, 1, 0, new Float64Params() ); // $ExpectError +} + +// The compiler throws an error if the function is provided an eleventh argument which is not a number... +{ + const x = new Float64Array( 10*10 ); + const y = new Float64Params( 10 ); + const w = new Float64Array( 10 ); + const ws = new Float64Array( 10 ); + + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, '10', 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, true, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, false, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, null, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, undefined, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, [], 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, {}, 0, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, ( x: number ): number => x, 0, ws, 1, 0, new Float64Params() ); // $ExpectError +} + +// The compiler throws an error if the function is provided a twelfth argument which is not a number... +{ + const x = new Float64Array( 10*10 ); + const y = new Float64Params( 10 ); + const w = new Float64Array( 10 ); + const ws = new Float64Array( 10 ); + + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, '10', ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, true, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, false, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, null, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, undefined, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, [], ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, {}, ws, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, ( x: number ): number => x, ws, 1, 0, new Float64Params() ); // $ExpectError +} + +// The compiler throws an error if the function is provided a thirteenth argument which is not a Float64Array... +{ + const x = new Float64Array( 10*10 ); + const y = new Float64Params( 10 ); + const w = new Float64Array( 10 ); + + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, '10', 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, true, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, false, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, null, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, undefined, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, [], 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, {}, 1, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ( x: number ): number => x, 1, 0, new Float64Params() ); // $ExpectError +} + +// The compiler throws an error if the function is provided a fourteenth argument which is not a number... +{ + const x = new Float64Array( 10*10 ); + const y = new Float64Params( 10 ); + const w = new Float64Array( 10 ); + const ws = new Float64Array( 10 ); + + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, '10', 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, true, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, false, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, null, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, undefined, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, [], 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, {}, 0, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, ( x: number ): number => x, 0, new Float64Params() ); // $ExpectError +} + +// The compiler throws an error if the function is provided a fifteenth argument which is not a number... +{ + const x = new Float64Array( 10*10 ); + const y = new Float64Params( 10 ); + const w = new Float64Array( 10 ); + const ws = new Float64Array( 10 ); + + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, '10', new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, true, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, false, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, null, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, undefined, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, [], new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, {}, new Float64Params() ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, ( x: number ): number => x, new Float64Params() ); // $ExpectError +} + +// The compiler throws an error if the function is provided a sixteenth argument which is not a params object... +{ + const x = new Float64Array( 10*10 ); + const y = new Float64Params( 10 ); + const w = new Float64Array( 10 ); + const ws = new Float64Array( 10 ); + + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, '10' ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, true ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, false ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, null ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, undefined ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, [] ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, {} ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, ( x: number ): number => x ); // $ExpectError +} + +// The compiler throws an error if the `ndarray` method is provided an unsupported number of arguments... +{ + const x = new Float64Array( 10*10 ); + const y = new Float64Array( 10 ); + const w = new Float64Array( 10 ); + const ws = new Float64Array( 10 ); + + dsgdTrainer.ndarray(); // $ExpectError + dsgdTrainer.ndarray( 10 ); // $ExpectError + dsgdTrainer.ndarray( 10, 10 ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10 ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1 ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0 ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1 ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0 ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1 ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0 ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1 ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0 ); // $ExpectError + dsgdTrainer.ndarray( 10, 10, x, 10, 1, 0, y, 1, 0, w, 1, 0, ws, 1, 0, new Float64Params(), 10 ); // $ExpectError +} diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/include/stdlib/ml/strided/dsgd_trainer.h b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/include/stdlib/ml/strided/dsgd_trainer.h new file mode 100644 index 000000000000..2e4d26cec812 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/include/stdlib/ml/strided/dsgd_trainer.h @@ -0,0 +1,85 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2025 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/** +* Header file containing function declarations for training a linear model via stochastic gradient descent. +*/ +#ifndef STDLIB_ML_STRIDED_DSGD_TRAINER_H +#define STDLIB_ML_STRIDED_DSGD_TRAINER_H + +#include "stdlib/ml/base/sgd/learning_rates.h" +#include "stdlib/ml/base/sgd/loss_functions.h" +#include "stdlib/ml/base/sgd/penalties.h" +#include "stdlib/blas/base/shared.h" +#include +#include + +/* +* If C++, prevent name mangling so that the compiler emits a binary file having undecorated names, thus mirroring the behavior of a C compiler. +*/ +#ifdef __cplusplus +extern "C" { +#endif + +/** +* Struct for storing SGD parameters. +*/ +struct stdlib_ml_sgd_float64_params { + // Parameters specific to the regularization function being used: + double penaltyParams[ 2 ]; + + // Parameters specific to the learning rate scheduler being used: + double learningRateParams[ 2 ]; + + // Parameters specific to the loss function being used: + double lossFunctionParams[ 1 ]; + + // Initial intercept value: + double intercept; + + // Maximum number of iterations to run: + int32_t maxIter; + + // Regularization function to be used: + int8_t penalty; + + // Learning rate scheduler to be used: + int8_t learningRate; + + // Loss function to be used: + int8_t lossFunction; + + // Boolean indicating whether to include intercept: + bool fitIntercept; +}; + +/** +* Trains a linear model via stochastic gradient descent. +*/ +double* API_SUFFIX(stdlib_strided_dsgd_trainer)( const CBLAS_LAYOUT layout, const CBLAS_INT M, const CBLAS_INT N, const double *X, const CBLAS_INT LDX, const double *y, const CBLAS_INT strideY, double *w, const CBLAS_INT strideW, double *ws, const CBLAS_INT strideWS, const struct stdlib_ml_sgd_float64_params *params ); + +/** +* Trains a linear model via stochastic gradient descent using alternative indexing semantics. +*/ +double* API_SUFFIX(stdlib_strided_dsgd_trainer_ndarray)( const CBLAS_INT M, const CBLAS_INT N, const double *x, const CBLAS_INT strideX1, const CBLAS_INT strideX2, const CBLAS_INT offsetX, const double *y, const CBLAS_INT strideY, const CBLAS_INT offsetY, double *w, const CBLAS_INT strideW, const CBLAS_INT offsetW, double *ws, const CBLAS_INT strideWS, const CBLAS_INT offsetWS, const struct stdlib_ml_sgd_float64_params *params ); + +#ifdef __cplusplus +} +#endif + +#endif // !STDLIB_ML_STRIDED_DSGD_TRAINER_H diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/base.js b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/base.js new file mode 100644 index 000000000000..d147653ed083 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/base.js @@ -0,0 +1,226 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +// var params2json = require( '@stdlib/ml/base/sgd/params/to-json' ); + +var logger = require( 'debug' ); +var ddot = require( '@stdlib/blas/base/ddot' ).ndarray; +var daxpy = require( '@stdlib/blas/base/daxpy' ).ndarray; +var dscal = require( '@stdlib/blas/base/dscal' ).ndarray; +var LEARNING_RATE_METHODS = require( './learning_rate.js' ); +var LOSS_FUNCTIONS = require( './loss.js' ); +var DECAYS = require( './decay.js' ); +var TRUNCATIONS = require( './truncation.js' ); + + +// VARIABLES // + +var debug = logger( 'ml:dsgd-trainer-squared-epsilon-insensitive' ); + +var MIN_SCALE = 1.0e-9; +var MAX_DLOSS = 1e12; + + +// MAIN // + +/** +* Trains a linear model via stochastic gradient descent. +* +* @private +* @param {NonNegativeInteger} M - number of samples +* @param {NonNegativeInteger} N - number of features +* @param {Float64Array} X - input matrix +* @param {integer} strideX1 - stride of the first dimension of `X` +* @param {integer} strideX2 - stride of the second dimension of `X` +* @param {NonNegativeInteger} offsetX - starting index for `X` +* @param {Float64Array} y - target array +* @param {integer} strideY - stride length for `y` +* @param {NonNegativeInteger} offsetY - starting index for `y` +* @param {Float64Array} w - weight array +* @param {integer} strideW - stride length for `w` +* @param {NonNegativeInteger} offsetW - starting index for `w` +* @param {Float64Array} workspace - workspace array +* @param {integer} strideWS - stride length for `workspace` +* @param {NonNegativeInteger} offsetWS - starting index for `workspace` +* @param {Params} params - parameters object +* @returns {Float64Array} `w` +* +* @example +* var Float64Array = require( '@stdlib/array/float64' ); +* var Params = require( '@stdlib/ml/base/sgd/params/float64' ); +* var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); +* +* var M = 50; +* var N = 5; +* var lambda = 2.5; +* var eta0 = 0.01; +* +* var params = new Params({ +* 'penaltyParams': new Float64Array( [ lambda, 0.0 ] ), +* 'learningRateParams': new Float64Array( [ eta0, 0.0 ] ), +* 'intercept': 0.0, +* 'maxIter': 500, +* 'penalty': 'l2', +* 'learningRate': 'constant', +* 'lossFunction': 'hinge', +* 'fitIntercept': true +* }); +* +* var y = discreteUniform( M, 0.0, 30.0 ); +* var X = discreteUniform( M*N, -5.0, 5.0 ); +* var w = discreteUniform( N, -0.05, 0.05 ); +* var workspace = new Float64Array( N ); +* +* var out = dsgdTrainer( M, N, X, N, 1, 0, y, 1, 0, w, 1, 0, workspace, 1, 0, params ); +* returns +*/ +function dsgdTrainer( M, N, X, strideX1, strideX2, offsetX, y, strideY, offsetY, w, strideW, offsetW, workspace, strideWS, offsetWS, params ) { // eslint-disable-line max-params, max-len + var lossFunctionParams; + var learningRateParams; + var penaltyParams; + var lossFunction; + var learningRate; + var fitIntercept; + var scaleFactor; + var intercept; + var penalty; + var decayFn; + var truncFn; + var maxIter; + var lossFn; + var update; + var epoch; + var dloss; + var lrFn; + var eta; + var ox; + var oy; + var t; + var p; + var i; + var u; + + penalty = params.penalty; + lossFunction = params.lossFunction; + learningRate = params.learningRate; + lrFn = LEARNING_RATE_METHODS[ learningRate ]; + lossFn = LOSS_FUNCTIONS[ lossFunction ]; + decayFn = DECAYS[ penalty ]; + truncFn = TRUNCATIONS[ penalty ]; + lossFunctionParams = params.lossFunctionParams; + learningRateParams = params.learningRateParams; + penaltyParams = params.penaltyParams; + fitIntercept = params.fitIntercept; + intercept = params.intercept; + maxIter = params.maxIter; + + debug( 'Starting SGD trainer with %i samples and %i features', M, N ); + // debug( params2json( params ) ); + + /* + * w - N (feature vector) + * X - M*N (input vector) + * y - M (output vector) + */ + + /** + * y -> [ ... ] (vector of size M, as there are M samples) + * w -> [ ... ] (vector of size N, as there are N weights, one per input feature) + * X -> [ ... ] (vector of size MxN, as there are N features per sample, M samples) + */ + + if ( penalty === 2 ) { // l2 + penaltyParams[ 1 ] = 0.0; + } else if ( penalty === 1 ) { // l1 + penaltyParams[ 1 ] = 1.0; + } + + t = 1; + u = 0.0; + scaleFactor = 1.0; + for ( epoch = 1; epoch <= maxIter; epoch++ ) { + ox = offsetX; + oy = offsetY; + for ( i = 0; i < M; i++ ) { + p = ( scaleFactor*ddot( N, w, strideW, offsetW, X, strideX2, ox ) ) + intercept; // eslint-disable-line max-len + + eta = lrFn( t, learningRateParams ); // eslint-disable-line max-len + + dloss = lossFn( y[ oy ], p, lossFunctionParams ); // eslint-disable-line max-len + if ( dloss < -MAX_DLOSS ) { + dloss = -MAX_DLOSS; + } else if ( dloss > MAX_DLOSS ) { + dloss = MAX_DLOSS; + } + update = -eta * dloss; + + scaleFactor = decayFn( scaleFactor, eta, penaltyParams ); // eslint-disable-line max-len + // if ( penalty === 'l2' || penalty === 'elasticnet' ) { + // factor = 1.0 - ( ( 1.0 - l1Ratio ) * eta * lambda ); + // scaleFactor *= max( 0.0, factor ); + // } + + if ( scaleFactor < MIN_SCALE ) { + dscal( N, scaleFactor, w, strideW, offsetW ); + scaleFactor = 1.0; + } + + // Gradient step: as `w_eff = w_stored*scaleFactor`, adding `update*X` to the effective weights amounts to adding `(update/scaleFactor)*X` to the stored weights... + daxpy( N, update/scaleFactor, X, strideX2, ox, w, strideW, offsetW ); // eslint-disable-line max-len + + if ( fitIntercept ) { + intercept += update; + } + + u = truncFn( N, u, eta, scaleFactor, w, strideW, offsetW, workspace, strideWS, offsetWS, penaltyParams ); // eslint-disable-line max-len + // if ( penalty === 'l1' || penalty === 'elasticnet' ) { + // u += ( l1Ratio * eta * lambda ); + // l1Penalty( N, u, scaleFactor, w, strideW, offsetW, workspace, strideWS, offsetWS ); // eslint-disable-line max-len + // } + + t += 1; + ox += strideX1; + oy += strideY; + } + debug( 'Epoch %d done...', epoch ); + } + + // Fold any residual scale back into the weights so `w` holds effective values: + if ( scaleFactor !== 1 ) { + dscal( N, scaleFactor, w, strideW, offsetW ); + scaleFactor = 1.0; + } + + debug( 'Finished SGD trainer.' ); + + if ( fitIntercept ) { + w[ offsetW + ( strideW * N ) ] = intercept; // We expect consumer to pass an array of size N+1 if they set `fitIntercept = true` + // TODO: Do a size check in sgd_trainer.js + // if `fitIntercept = true`, check if its size is N+1 + } + return w; +} + + +// EXPORTS // + +module.exports = dsgdTrainer; diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/decay.js b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/decay.js new file mode 100644 index 000000000000..6a6f74cf8327 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/decay.js @@ -0,0 +1,87 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var max = require( '@stdlib/math/base/special/max' ); + + +// VARIABLES // + +/** +* Weight decay functions. +* +* @private +* @name DECAYS +* @constant +* @type {Object} +* +* @example +* var f = DECAYS[ 'l1' ]; +* // returns +*/ +var DECAYS = { + 'elasticnet': l2Decay, + 'l1': identityDecay, + 'l2': l2Decay, + 'none': identityDecay +}; + + +// FUNCTIONS // + +/** +* Computes scale factor by applying the identity decay. +* +* Note: +* +* - Here **params**: `[ ]` (empty) +* +* @private +* @param {NonNegativeInteger} scaleFactor - current iteration. +* @param {NonNegativeInteger} eta - learning rate. +* @param {Float64Array} params - strided array containing regularizer specific parameters. +* @returns {number} scale factor +*/ +function identityDecay( scaleFactor, eta, params ) { // eslint-disable-line no-unused-vars + return scaleFactor; +} + +/** +* Computes scale factor by applying the L2 decay. +* +* Note: +* +* - Here **params**: `[ lambda, l1Ratio ]` +* +* @private +* @param {NonNegativeInteger} scaleFactor - current iteration. +* @param {NonNegativeInteger} eta - learning rate. +* @param {Float64Array} params - strided array containing regularizer specific parameters. +* @returns {number} scale factor +*/ +function l2Decay( scaleFactor, eta, params ) { + return scaleFactor * max( 0.0, 1.0 - ( ( 1.0 - params[ 1 ] ) * eta * params[ 0 ] ) ); // eslint-disable-line max-len +} + + +// EXPORTS // + +module.exports = DECAYS; diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/dsgd_trainer.js b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/dsgd_trainer.js new file mode 100644 index 000000000000..2a35f6acbb07 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/dsgd_trainer.js @@ -0,0 +1,164 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var isLayout = require( '@stdlib/blas/base/assert/is-layout' ); +var resolvePenaltyStr = require( '@stdlib/ml/base/sgd/penalty-resolve-str' ); +var resolveLRStr = require( '@stdlib/ml/base/sgd/learning-rate-resolve-str' ); +var resolveLossFnStr = require( '@stdlib/ml/base/sgd/loss-function-resolve-str' ); +var isColumnMajor = require( '@stdlib/ndarray/base/assert/is-column-major-string' ); +var stride2offset = require( '@stdlib/strided/base/stride2offset' ); +var max = require( '@stdlib/math/base/special/fast/max' ); +var format = require( '@stdlib/string/format' ); +var base = require( './base.js' ); + + +// MAIN // + +/** +* Trains a linear model via stochastic gradient descent. +* +* @private +* @param {string} order - storage layout +* @param {NonNegativeInteger} M - number of samples +* @param {NonNegativeInteger} N - number of features +* @param {Float64Array} X - input matrix +* @param {NonNegativeInteger} LDX - stride of the first dimension of `X` (a.k.a., leading dimension of the matrix `X`) +* @param {Float64Array} y - target array +* @param {integer} strideY - stride length for `y` +* @param {Float64Array} w - weight array +* @param {integer} strideW - stride length for `w` +* @param {Float64Array} workspace - workspace array +* @param {integer} strideWS - stride length for `workspace` +* @param {Params} params - parameters object +* @throws {TypeError} first argument must be a valid order +* @throws {TypeError} thirteenth argument's `penalty` member must be a valid penalty +* @throws {TypeError} thirteenth argument's `learningRate` member must be a valid learning rate +* @throws {TypeError} thirteenth argument's `lossFunction` member must be a valid loss function +* @throws {RangeError} second argument must be a nonnegative integer +* @throws {RangeError} third argument must be a nonnegative integer +* @throws {RangeError} fifth argument must be non-zero +* @throws {RangeError} seventh argument must be a valid stride +* @throws {RangeError} ninth argument must be a valid stride +* @throws {RangeError} eleventh argument must be a valid stride +* @returns {Float64Array} `w` +* +* @example +* var Float64Array = require( '@stdlib/array/float64' ); +* var Params = require( '@stdlib/ml/base/sgd/params/float64' ); +* var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); +* +* var M = 50; +* var N = 5; +* var lambda = 2.5; +* var eta0 = 0.01; +* +* var params = new Params({ +* 'penaltyParams': new Float64Array( [ lambda, 0.0 ] ), +* 'learningRateParams': new Float64Array( [ eta0, 0.0 ] ), +* 'intercept': 0.0, +* 'maxIter': 500, +* 'penalty': 'l2', +* 'learningRate': 'constant', +* 'lossFunction': 'hinge', +* 'fitIntercept': true +* }); +* +* var y = discreteUniform( M, 0.0, 30.0 ); +* var X = discreteUniform( M*N, -5.0, 5.0 ); +* var w = discreteUniform( N, -0.05, 0.05 ); +* var workspace = new Float64Array( N ); +* +* var out = dsgdTrainer( 'row-major', M, N, X, N, y, 1, w, 1, workspace, 1, params ); +* returns +*/ +function dsgdTrainer( order, M, N, X, LDX, y, strideY, w, strideW, workspace, strideWS, params ) { // eslint-disable-line max-params, max-len + var iscm; + var lsfn; + var vala; + var sx1; + var sx2; + var ows; + var oy; + var ow; + var lr; + var p; + + if ( !isLayout( order ) ) { + throw new TypeError( format( 'invalid argument. First argument must be a valid order. Value: `%s`.', order ) ); + } + p = resolvePenaltyStr( params.penalty ); + lr = resolveLRStr( params.learningRate ); + lsfn = resolveLossFnStr( params.lossFunction ); + if ( p === null ) { + throw new TypeError( format( 'invalid argument. Thirteenth argument\'s `penalty` member must be a valid penalty. Value: `%s`.', params.penalty ) ); + } + if ( lr === null ) { + throw new TypeError( format( 'invalid argument. Thirteenth argument\'s `learningRate` member must be a valid learning rate. Value: `%s`.', params.learningRate ) ); + } + if ( lsfn === null ) { + throw new TypeError( format( 'invalid argument. Thirteenth argument\'s `lossFunction` member must be a valid loss function. Value: `%s`.', params.lossFunction ) ); + } + if ( M < 0 ) { + throw new RangeError( format( 'invalid argument. Second argument must be a nonnegative integer. Value: `%d`.', M ) ); + } + if ( N < 0 ) { + throw new RangeError( format( 'invalid argument. Third argument must be a nonnegative integer. Value: `%d`.', N ) ); + } + iscm = isColumnMajor( order ); + if ( iscm ) { + vala = M; + } else { + vala = N; + } + if ( LDX < max( 1, vala ) ) { + throw new RangeError( format( 'invalid argument. Fifth argument must be greater than or equal to max(1,%d). Value: `%d`.', vala, LDX ) ); + } + if ( strideY === 0 ) { + throw new RangeError( format( 'invalid argument. Seventh argument must be non-zero. Value: `%d`.', strideY ) ); + } + if ( strideW === 0 ) { + throw new RangeError( format( 'invalid argument. Ninth argument must be non-zero. Value: `%d`.', strideW ) ); + } + if ( strideWS === 0 ) { + throw new RangeError( format( 'invalid argument. Eleventh argument must be non-zero. Value: `%d`.', strideY ) ); + } + // Check if we can early return... + if ( M === 0 || N === 0 ) { + return w; + } + oy = stride2offset( M, strideY ); + ow = stride2offset( N, strideW ); + ows = stride2offset( N, strideWS ); + if ( iscm ) { + sx1 = 1; + sx2 = LDX; + } else { // order === 'row-major' + sx1 = LDX; + sx2 = 1; + } + return base( M, N, X, sx1, sx2, 0, y, strideY, oy, w, strideW, ow, workspace, strideWS, ows, params ); // eslint-disable-line max-len +} + + +// EXPORTS // + +module.exports = dsgdTrainer; diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/dsgd_trainer.native.js b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/dsgd_trainer.native.js new file mode 100644 index 000000000000..6a22c83d8f64 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/dsgd_trainer.native.js @@ -0,0 +1,150 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2025 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var isLayout = require( '@stdlib/blas/base/assert/is-layout' ); +var isColumnMajor = require( '@stdlib/ndarray/base/assert/is-column-major-string' ); +var max = require( '@stdlib/math/base/special/fast/max' ); +var resolveOrder = require( '@stdlib/blas/base/layout-resolve-enum' ); +var resolvePenaltyStr = require( '@stdlib/ml/base/sgd/penalty-resolve-str' ); +var resolveLRStr = require( '@stdlib/ml/base/sgd/learning-rate-resolve-str' ); +var resolveLossFnStr = require( '@stdlib/ml/base/sgd/loss-function-resolve-str' ); +var format = require( '@stdlib/string/format' ); +var addon = require( './../src/addon.node' ); + + +// MAIN // + +/** +* Trains a linear model via stochastic gradient descent. +* +* @private +* @param {string} order - storage layout +* @param {NonNegativeInteger} M - number of samples +* @param {NonNegativeInteger} N - number of features +* @param {Float64Array} X - input matrix +* @param {NonNegativeInteger} LDX - stride of the first dimension of `X` (a.k.a., leading dimension of the matrix `X`) +* @param {Float64Array} y - target array +* @param {integer} strideY - stride length for `y` +* @param {Float64Array} w - weight array +* @param {integer} strideW - stride length for `w` +* @param {Float64Array} workspace - workspace array +* @param {integer} strideWS - stride length for `workspace` +* @param {Params} params - parameters object +* @throws {TypeError} first argument must be a valid order +* @throws {TypeError} thirteenth argument's `penalty` member must be a valid penalty +* @throws {TypeError} thirteenth argument's `learningRate` member must be a valid learning rate +* @throws {TypeError} thirteenth argument's `lossFunction` member must be a valid loss function +* @throws {RangeError} second argument must be a nonnegative integer +* @throws {RangeError} third argument must be a nonnegative integer +* @throws {RangeError} sixth argument must be non-zero +* @throws {RangeError} fifth argument must be a valid stride +* @throws {RangeError} eighth argument must be a valid stride +* @throws {RangeError} tenth argument must be a valid stride +* @throws {RangeError} twelfth argument must be a valid stride +* @returns {Float64Array} `w` +* +* @example +* var Float64Array = require( '@stdlib/array/float64' ); +* var Params = require( '@stdlib/ml/base/sgd/params/float64' ); +* var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); +* +* var M = 50; +* var N = 5; +* var lambda = 2.5; +* var eta0 = 0.01; +* +* var params = new Params({ +* 'penaltyParams': new Float64Array( [ lambda, 0.0 ] ), +* 'learningRateParams': new Float64Array( [ eta0, 0.0 ] ), +* 'intercept': 0.0, +* 'maxIter': 500, +* 'penalty': 'l2', +* 'learningRate': 'constant', +* 'lossFunction': 'hinge', +* 'fitIntercept': true +* }); +* +* var y = discreteUniform( M, 0.0, 30.0 ); +* var X = discreteUniform( M*N, -5.0, 5.0 ); +* var w = discreteUniform( N, -0.05, 0.05 ); +* var workspace = new Float64Array( N ); +* +* var out = dsgdTrainer( 'row-major', M, N, X, N, y, 1, w, 1, workspace, 1, params ); +* returns +*/ +function dsgdTrainer( order, M, N, X, LDX, y, strideY, w, strideW, workspace, strideWS, params ) { // eslint-disable-line max-params, max-len + var iscm; + var lsfn; + var vala; + var lr; + var p; + + if ( !isLayout( order ) ) { + throw new TypeError( format( 'invalid argument. First argument must be a valid order. Value: `%s`.', order ) ); + } + p = resolvePenaltyStr( params.penalty ); + lr = resolveLRStr( params.learningRate ); + lsfn = resolveLossFnStr( params.lossFunction ); + if ( p === null ) { + throw new TypeError( format( 'invalid argument. Thirteenth argument\'s `penalty` member must be a valid penalty. Value: `%s`.', params.penalty ) ); + } + if ( lr === null ) { + throw new TypeError( format( 'invalid argument. Thirteenth argument\'s `learningRate` member must be a valid learning rate. Value: `%s`.', params.learningRate ) ); + } + if ( lsfn === null ) { + throw new TypeError( format( 'invalid argument. Thirteenth argument\'s `lossFunction` member must be a valid loss function. Value: `%s`.', params.lossFunction ) ); + } + if ( M < 0 ) { + throw new RangeError( format( 'invalid argument. Second argument must be a nonnegative integer. Value: `%d`.', M ) ); + } + if ( N < 0 ) { + throw new RangeError( format( 'invalid argument. Third argument must be a nonnegative integer. Value: `%d`.', N ) ); + } + iscm = isColumnMajor( order ); + if ( iscm ) { + vala = M; + } else { + vala = N; + } + if ( LDX < max( 1, vala ) ) { + throw new RangeError( format( 'invalid argument. Sixth argument must be greater than or equal to max(1,%d). Value: `%d`.', vala, LDX ) ); + } + if ( strideY === 0 ) { + throw new RangeError( format( 'invalid argument. Eighth argument must be non-zero. Value: `%d`.', strideY ) ); + } + if ( strideW === 0 ) { + throw new RangeError( format( 'invalid argument. Tenth argument must be non-zero. Value: `%d`.', strideW ) ); + } + if ( strideWS === 0 ) { + throw new RangeError( format( 'invalid argument. Twelfth argument must be non-zero. Value: `%d`.', strideY ) ); + } + // Check if we can early return... + if ( M === 0 || N === 0 ) { + return w; + } + return addon( resolveOrder( order ), M, N, X, LDX, y, strideY, w, strideW, workspace, strideWS, params ); // eslint-disable-line max-len +} + + +// EXPORTS // + +module.exports = dsgdTrainer; diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/index.js b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/index.js new file mode 100644 index 000000000000..6cec28670458 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/index.js @@ -0,0 +1,110 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +/** +* Trains a linear model via stochastic gradient descent. +* +* @module @stdlib/ml/strided/dsgd-trainer +* +* @example +* var Float64Array = require( '@stdlib/array/float64' ); +* var Params = require( '@stdlib/ml/base/sgd/params/float64' ); +* var dsgdTrainer = require( '@stdlib/ml/strided/dsgd-trainer' ); +* var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); +* +* var M = 50; +* var N = 5; +* var lambda = 2.5; +* var eta0 = 0.01; +* +* var params = new Params({ +* 'penaltyParams': new Float64Array( [ lambda, 0.0 ] ), +* 'learningRateParams': new Float64Array( [ eta0, 0.0 ] ), +* 'intercept': 0.0, +* 'maxIter': 500, +* 'penalty': 'l2', +* 'learningRate': 'constant', +* 'lossFunction': 'hinge', +* 'fitIntercept': true +* }); +* +* var y = discreteUniform( M, 0.0, 30.0 ); +* var X = discreteUniform( M*N, -5.0, 5.0 ); +* var w = discreteUniform( N, -0.05, 0.05 ); +* var workspace = new Float64Array( N ); +* +* var out = dsgdTrainer( 'row-major', M, N, X, N, y, 1, w, 1, workspace, 1, params ); +* returns +* +* @example +* var Float64Array = require( '@stdlib/array/float64' ); +* var Params = require( '@stdlib/ml/base/sgd/params/float64' ); +* var dsgdTrainer = require( '@stdlib/ml/strided/dsgd-trainer' ); +* var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); +* +* var M = 50; +* var N = 5; +* var lambda = 2.5; +* var eta0 = 0.01; +* +* var params = new Params({ +* 'penaltyParams': new Float64Array( [ lambda, 0.0 ] ), +* 'learningRateParams': new Float64Array( [ eta0, 0.0 ] ), +* 'intercept': 0.0, +* 'maxIter': 500, +* 'penalty': 'l2', +* 'learningRate': 'constant', +* 'lossFunction': 'hinge', +* 'fitIntercept': true +* }); +* +* var y = discreteUniform( M, 0.0, 30.0 ); +* var X = discreteUniform( M*N, -5.0, 5.0 ); +* var w = discreteUniform( N, -0.05, 0.05 ); +* var workspace = new Float64Array( N ); +* +* var out = dsgdTrainer.ndarray( M, N, X, N, 1, 0, y, 1, 0, w, 1, 0, workspace, 1, 0, params ); +* returns +*/ + +// MODULES // + +var join = require( 'path' ).join; +var tryRequire = require( '@stdlib/utils/try-require' ); +var isError = require( '@stdlib/assert/is-error' ); +var main = require( './main.js' ); + + +// MAIN // + +var dsgdTrainer; +var tmp = tryRequire( join( __dirname, './native.js' ) ); +if ( isError( tmp ) ) { + dsgdTrainer = main; +} else { + dsgdTrainer = tmp; +} + + +// EXPORTS // + +module.exports = dsgdTrainer; + +// exports: { "ndarray": "dsgdTrainer.ndarray" } diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/learning_rate.js b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/learning_rate.js new file mode 100644 index 000000000000..90fc91f7f8d7 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/learning_rate.js @@ -0,0 +1,117 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var pow = require( '@stdlib/math/base/special/pow' ); + + +// VARIABLES // + +/** +* Learning rate schedulers. +* +* @private +* @name LEARNING_RATE_METHODS +* @constant +* @type {Object} +* +* @example +* var f = LEARNING_RATE_METHODS[ 'invscaling' ]; +* // returns +*/ +var LEARNING_RATE_METHODS = { + 'basic': basic, + 'constant': constant, + 'invscaling': invscaling, + 'pegasos': pegasos +}; + + +// FUNCTIONS // + +/** +* Computes learning rate by applying the `constant` learning rate scheduler. +* +* Note: +* +* - Here **params**: `[ eta0 ]` +* +* @private +* @param {NonNegativeInteger} t - current iteration. +* @param {Float64Array} params - strided array containing scheduler specific parameters. +* @returns {number} learning rate +*/ +function constant( t, params ) { // eslint-disable-line no-unused-vars + return params[ 0 ]; // return eta0 +} + +/** +* Computes learning rate by applying the `basic` learning rate scheduler. +* +* Note: +* +* - Here **params**: `[ ]` (empty) +* +* @private +* @param {NonNegativeInteger} t - current iteration. +* @param {Float64Array} params - strided array containing scheduler specific parameters. +* @returns {number} learning rate +*/ +function basic( t, params ) { // eslint-disable-line no-unused-vars + return 10.0 / ( 10.0*t ); +} + +/** +* Computes learning rate by applying the `invscaling` learning rate scheduler. +* +* Note: +* +* - Here **params**: `[ eta0, powerT ]` +* +* @private +* @param {NonNegativeInteger} t - current iteration. +* @param {Float64Array} params - strided array containing scheduler specific parameters. +* @returns {number} learning rate +*/ +function invscaling( t, params ) { + return params[ 0 ] / pow( t, params[ 1 ] ); +} + +/** +* Computes learning rate by applying the `Pegasos` learning rate scheduler. +* +* Note: +* +* - Here **params**: `[ lambda ]` +* +* @private +* @param {NonNegativeInteger} t - current iteration. +* @param {Float64Array} params - strided array containing scheduler specific parameters. +* @returns {number} learning rate +*/ +function pegasos( t, params ) { + return 1.0 / ( params[ 0 ]*t ); +} + + +// EXPORTS // + +module.exports = LEARNING_RATE_METHODS; diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/loss.js b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/loss.js new file mode 100644 index 000000000000..aeeb7edc6a3f --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/loss.js @@ -0,0 +1,218 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var logGradient = require( '@stdlib/ml/base/loss/float64/log-gradient' ); +var huberGradient = require( '@stdlib/ml/base/loss/float64/huber-gradient' ); +var hingeGradient = require( '@stdlib/ml/base/loss/float64/hinge-gradient' ); +var squaredHingeGradient = require( '@stdlib/ml/base/loss/float64/squared-hinge-gradient' ); +var squaredErrorGradient = require( '@stdlib/ml/base/loss/float64/squared-error-gradient' ); +var modifiedHuberGradient = require( '@stdlib/ml/base/loss/float64/modified-huber-gradient' ); +var epsilonInsensitiveGradient = require( '@stdlib/ml/base/loss/float64/epsilon-insensitive-gradient' ); +var squaredEpsilonInsensitiveGradient = require( '@stdlib/ml/base/loss/float64/squared-epsilon-insensitive-gradient' ); + + +// VARIABLES // + +/** +* Loss function gradients. +* +* @private +* @name LOSS_FUNCTIONS +* @constant +* @type {Object} +* +* @example +* var f = LOSS_FUNCTIONS[ 'hinge' ]; +* // returns +*/ +var LOSS_FUNCTIONS = { + 'epsilon-insensitive': epsilonInsensitive, + 'hinge': hinge, + 'huber': huber, + 'log': log, + 'modified-huber': modifiedHuber, + 'perceptron': perceptron, + 'squared-epsilon-insensitive': squaredEpsilonInsensitive, + 'squared-error': squaredError, + 'squared-hinge': squaredHinge +}; + + +// FUNCTIONS // + +/** +* Computes the gradient of the epsilon-insensitive loss. +* +* Note: +* +* - Here **params**: `[ epsilon ]` +* +* @private +* @param {number} y - target value +* @param {number} p - predicted value +* @param {Float64Array} params - strided array containing loss function specific parameters. +* @returns {number} loss gradient +*/ +function epsilonInsensitive( y, p, params ) { + return epsilonInsensitiveGradient( 1.0, params[ 0 ], y, p ); // Magic number `1.0` as we need per-sample loss. +} + +/** +* Computes the gradient of the hinge loss. +* +* Note: +* +* - Here **params**: `[ ]` (empty) +* +* @private +* @param {number} y - target value +* @param {number} p - predicted value +* @param {Float64Array} params - strided array containing loss function specific parameters. +* @returns {number} loss gradient +*/ +function hinge( y, p, params ) { // eslint-disable-line no-unused-vars + return hingeGradient( 1.0, 1.0, y, p ); // Magic numbers `1.0` and `1.0` as we need per-sample loss used by SVM. +} + +/** +* Computes the gradient of the Huber loss. +* +* Note: +* +* - Here **params**: `[ threshold ]` (empty) +* +* @private +* @param {number} y - target value +* @param {number} p - predicted value +* @param {Float64Array} params - strided array containing loss function specific parameters. +* @returns {number} loss gradient +*/ +function huber( y, p, params ) { // eslint-disable-line no-unused-vars + return huberGradient( 1.0, params[ 0 ], y, p ); // Magic number `1.0` as we need per-sample loss. +} + +/** +* Computes the gradient of the log loss. +* +* Note: +* +* - Here **params**: `[ ]` (empty) +* +* @private +* @param {number} y - target value +* @param {number} p - predicted value +* @param {Float64Array} params - strided array containing loss function specific parameters. +* @returns {number} loss gradient +*/ +function log( y, p, params ) { // eslint-disable-line no-unused-vars + return logGradient( 1.0, y, p ); // Magic number `1.0` as we need per-sample loss. +} + +/** +* Computes the gradient of the modified Huber loss. +* +* Note: +* +* - Here **params**: `[ ]` (empty) +* +* @private +* @param {number} y - target value +* @param {number} p - predicted value +* @param {Float64Array} params - strided array containing loss function specific parameters. +* @returns {number} loss gradient +*/ +function modifiedHuber( y, p, params ) { // eslint-disable-line no-unused-vars + return modifiedHuberGradient( 1.0, y, p ); // Magic number `1.0` as we need per-sample loss. +} + +/** +* Computes the gradient of the perceptron loss. +* +* Note: +* +* - Here **params**: `[ ]` (empty) +* +* @private +* @param {number} y - target value +* @param {number} p - predicted value +* @param {Float64Array} params - strided array containing loss function specific parameters. +* @returns {number} loss gradient +*/ +function perceptron( y, p, params ) { // eslint-disable-line no-unused-vars + return hingeGradient( 1.0, 0.0, y, p ); // Magic numbers `1.0` and `0.0` as we need per-sample loss used by Perceptron. +} + +/** +* Computes the gradient of the squared epsilon-insensitive loss. +* +* Note: +* +* - Here **params**: `[ epsilon ]` +* +* @private +* @param {number} y - target value +* @param {number} p - predicted value +* @param {Float64Array} params - strided array containing loss function specific parameters. +* @returns {number} loss gradient +*/ +function squaredEpsilonInsensitive( y, p, params ) { + return squaredEpsilonInsensitiveGradient( 1.0, params[ 0 ], y, p ); // Magic number `1.0` as we need per-sample loss. +} + +/** +* Computes the gradient of the squared error loss. +* +* Note: +* +* - Here **params**: `[ ]` (empty) +* +* @private +* @param {number} y - target value +* @param {number} p - predicted value +* @param {Float64Array} params - strided array containing loss function specific parameters. +* @returns {number} loss gradient +*/ +function squaredError( y, p, params ) { // eslint-disable-line no-unused-vars + return squaredErrorGradient( 1.0, y, p ); // Magic number `1.0` as we need per-sample loss. +} + +/** +* Computes the gradient of the squared hinge loss. +* +* Note: +* +* - Here **params**: `[ ]` (empty) +* +* @private +* @param {number} y - target value +* @param {number} p - predicted value +* @param {Float64Array} params - strided array containing loss function specific parameters. +* @returns {number} loss gradient +*/ +function squaredHinge( y, p, params ) { // eslint-disable-line no-unused-vars + return squaredHingeGradient( 1.0, 1.0, y, p ); // Magic numbers `1.0` and `1.0` as we need per-sample loss used by SVM. +} + + +// EXPORTS // + +module.exports = LOSS_FUNCTIONS; diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/main.js b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/main.js new file mode 100644 index 000000000000..bff16b5d4900 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/main.js @@ -0,0 +1,35 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var setReadOnly = require( '@stdlib/utils/define-nonenumerable-read-only-property' ); +var dsgdTrainer = require( './dsgd_trainer.js' ); +var ndarray = require( './ndarray.js' ); + + +// MAIN // + +setReadOnly( dsgdTrainer, 'ndarray', ndarray ); + + +// EXPORTS // + +module.exports = dsgdTrainer; diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/native.js b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/native.js new file mode 100644 index 000000000000..faa45719b594 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/native.js @@ -0,0 +1,35 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2025 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var setReadOnly = require( '@stdlib/utils/define-nonenumerable-read-only-property' ); +var dsgdTrainer = require( './dsgd_trainer.native.js' ); +var ndarray = require( './ndarray.native.js' ); + + +// MAIN // + +setReadOnly( dsgdTrainer, 'ndarray', ndarray ); + + +// EXPORTS // + +module.exports = dsgdTrainer; diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/ndarray.js b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/ndarray.js new file mode 100644 index 000000000000..41c6f6c49a34 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/ndarray.js @@ -0,0 +1,129 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2024 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var resolvePenaltyStr = require( '@stdlib/ml/base/sgd/penalty-resolve-str' ); +var resolveLRStr = require( '@stdlib/ml/base/sgd/learning-rate-resolve-str' ); +var resolveLossFnStr = require( '@stdlib/ml/base/sgd/loss-function-resolve-str' ); +var format = require( '@stdlib/string/format' ); +var base = require( './base.js' ); + + +// MAIN // + +/** +* Trains a linear model via stochastic gradient descent using alternative indexing semantics. +* +* @param {NonNegativeInteger} M - number of samples +* @param {NonNegativeInteger} N - number of features +* @param {Float64Array} X - input matrix +* @param {integer} strideX1 - stride of the first dimension of `X` +* @param {integer} strideX2 - stride of the second dimension of `X` +* @param {NonNegativeInteger} offsetX - starting index for `X` +* @param {Float64Array} y - target array +* @param {integer} strideY - stride length for `y` +* @param {NonNegativeInteger} offsetY - starting index for `y` +* @param {Float64Array} w - weight array +* @param {integer} strideW - stride length for `w` +* @param {NonNegativeInteger} offsetW - starting index for `w` +* @param {Float64Array} workspace - workspace array +* @param {integer} strideWS - stride length for `workspace` +* @param {NonNegativeInteger} offsetWS - starting index for `workspace` +* @param {Params} params - parameters object +* @throws {TypeError} first argument must be a valid transpose operation +* @throws {RangeError} second argument must be a nonnegative integer +* @throws {RangeError} third argument must be a nonnegative integer +* @throws {RangeError} tenth argument must be non-zero +* @throws {RangeError} fourteenth argument must be non-zero +* @returns {Float64Array} `w` +* +* @example +* var Float64Array = require( '@stdlib/array/float64' ); +* var Params = require( '@stdlib/ml/base/sgd/params/float64' ); +* var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); +* +* var M = 50; +* var N = 5; +* var lambda = 2.5; +* var eta0 = 0.01; +* +* var params = new Params({ +* 'penaltyParams': new Float64Array( [ lambda, 0.0 ] ), +* 'learningRateParams': new Float64Array( [ eta0, 0.0 ] ), +* 'intercept': 0.0, +* 'maxIter': 500, +* 'penalty': 'l2', +* 'learningRate': 'constant', +* 'lossFunction': 'hinge', +* 'fitIntercept': true +* }); +* +* var y = discreteUniform( M, 0.0, 30.0 ); +* var X = discreteUniform( M*N, -5.0, 5.0 ); +* var w = discreteUniform( N, -0.05, 0.05 ); +* var workspace = new Float64Array( N ); +* +* var out = dsgdTrainer( M, N, X, N, 1, 0, y, 1, 0, w, 1, 0, workspace, 1, 0, params ); +* returns +*/ +function dsgdTrainer( M, N, X, strideX1, strideX2, offsetX, y, strideY, offsetY, w, strideW, offsetW, workspace, strideWS, offsetWS, params ) { // eslint-disable-line max-params, max-len + var lsfn; + var lr; + var p; + + p = resolvePenaltyStr( params.penalty ); + lr = resolveLRStr( params.learningRate ); + lsfn = resolveLossFnStr( params.lossFunction ); + if ( p === null ) { + throw new TypeError( format( 'invalid argument. Thirteenth argument\'s `penalty` member must be a valid penalty. Value: `%s`.', params.penalty ) ); + } + if ( lr === null ) { + throw new TypeError( format( 'invalid argument. Thirteenth argument\'s `learningRate` member must be a valid learning rate. Value: `%s`.', params.learningRate ) ); + } + if ( lsfn === null ) { + throw new TypeError( format( 'invalid argument. Thirteenth argument\'s `lossFunction` member must be a valid loss function. Value: `%s`.', params.lossFunction ) ); + } + if ( M < 0 ) { + throw new RangeError( format( 'invalid argument. Second argument must be a nonnegative integer. Value: `%d`.', M ) ); + } + if ( N < 0 ) { + throw new RangeError( format( 'invalid argument. Third argument must be a nonnegative integer. Value: `%d`.', N ) ); + } + if ( strideY === 0 ) { + throw new RangeError( format( 'invalid argument. Eighth argument must be non-zero. Value: `%d`.', strideY ) ); + } + if ( strideW === 0 ) { + throw new RangeError( format( 'invalid argument. Tenth argument must be non-zero. Value: `%d`.', strideW ) ); + } + if ( strideWS === 0 ) { + throw new RangeError( format( 'invalid argument. Twelfth argument must be non-zero. Value: `%d`.', strideY ) ); + } + // Check if we can early return... + if ( M === 0 || N === 0 ) { + return w; + } + return base( M, N, X, strideX1, strideX2, offsetX, y, strideY, offsetY, w, strideW, offsetW, workspace, strideWS, offsetWS, params ); // eslint-disable-line max-len +} + + +// EXPORTS // + +module.exports = dsgdTrainer; diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/ndarray.native.js b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/ndarray.native.js new file mode 100644 index 000000000000..eed0506567f4 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/ndarray.native.js @@ -0,0 +1,130 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2025 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var resolvePenaltyStr = require( '@stdlib/ml/base/sgd/penalty-resolve-str' ); +var resolveLRStr = require( '@stdlib/ml/base/sgd/learning-rate-resolve-str' ); +var resolveLossFnStr = require( '@stdlib/ml/base/sgd/loss-function-resolve-str' ); +var format = require( '@stdlib/string/format' ); +var addon = require( './../src/addon.node' ); + + +// MAIN // + +/** +* Trains a linear model via stochastic gradient descent using alternative indexing semantics. +* +* @param {NonNegativeInteger} M - number of samples +* @param {NonNegativeInteger} N - number of features +* @param {Float64Array} X - input matrix +* @param {integer} strideX1 - stride of the first dimension of `X` +* @param {integer} strideX2 - stride of the second dimension of `X` +* @param {NonNegativeInteger} offsetX - starting index for `X` +* @param {Float64Array} y - target array +* @param {integer} strideY - stride length for `y` +* @param {NonNegativeInteger} offsetY - starting index for `y` +* @param {Float64Array} w - weight array +* @param {integer} strideW - stride length for `w` +* @param {NonNegativeInteger} offsetW - starting index for `w` +* @param {Float64Array} workspace - workspace array +* @param {integer} strideWS - stride length for `workspace` +* @param {NonNegativeInteger} offsetWS - starting index for `workspace` +* @param {Params} params - parameters object +* @throws {TypeError} first argument must be a valid transpose operation +* @throws {RangeError} second argument must be a nonnegative integer +* @throws {RangeError} third argument must be a nonnegative integer +* @throws {RangeError} tenth argument must be non-zero +* @throws {RangeError} fourteenth argument must be non-zero +* @returns {Float64Array} `w` +* +* @example +* var Float64Array = require( '@stdlib/array/float64' ); +* var Params = require( '@stdlib/ml/base/sgd/params/float64' ); +* var discreteUniform = require( '@stdlib/random/array/discrete-uniform' ); +* +* var M = 50; +* var N = 5; +* var lambda = 2.5; +* var eta0 = 0.01; +* +* var params = new Params({ +* 'penaltyParams': new Float64Array( [ lambda, 0.0 ] ), +* 'learningRateParams': new Float64Array( [ eta0, 0.0 ] ), +* 'intercept': 0.0, +* 'maxIter': 500, +* 'penalty': 'l2', +* 'learningRate': 'constant', +* 'lossFunction': 'hinge', +* 'fitIntercept': true +* }); +* +* var y = discreteUniform( M, 0.0, 30.0 ); +* var X = discreteUniform( M*N, -5.0, 5.0 ); +* var w = discreteUniform( N, -0.05, 0.05 ); +* var workspace = new Float64Array( N ); +* +* var out = dsgdTrainer( M, N, X, N, 1, 0, y, 1, 0, w, 1, 0, workspace, 1, 0, params ); +* returns +*/ +function dsgdTrainer( M, N, X, strideX1, strideX2, offsetX, y, strideY, offsetY, w, strideW, offsetW, workspace, strideWS, offsetWS, params ) { // eslint-disable-line max-params, max-len + var lsfn; + var lr; + var p; + + p = resolvePenaltyStr( params.penalty ); + lr = resolveLRStr( params.learningRate ); + lsfn = resolveLossFnStr( params.lossFunction ); + if ( p === null ) { + throw new TypeError( format( 'invalid argument. Thirteenth argument\'s `penalty` member must be a valid penalty. Value: `%s`.', params.penalty ) ); + } + if ( lr === null ) { + throw new TypeError( format( 'invalid argument. Thirteenth argument\'s `learningRate` member must be a valid learning rate. Value: `%s`.', params.learningRate ) ); + } + if ( lsfn === null ) { + throw new TypeError( format( 'invalid argument. Thirteenth argument\'s `lossFunction` member must be a valid loss function. Value: `%s`.', params.lossFunction ) ); + } + if ( M < 0 ) { + throw new RangeError( format( 'invalid argument. Second argument must be a nonnegative integer. Value: `%d`.', M ) ); + } + if ( N < 0 ) { + throw new RangeError( format( 'invalid argument. Third argument must be a nonnegative integer. Value: `%d`.', N ) ); + } + if ( strideY === 0 ) { + throw new RangeError( format( 'invalid argument. Eighth argument must be non-zero. Value: `%d`.', strideY ) ); + } + if ( strideW === 0 ) { + throw new RangeError( format( 'invalid argument. Tenth argument must be non-zero. Value: `%d`.', strideW ) ); + } + if ( strideWS === 0 ) { + throw new RangeError( format( 'invalid argument. Twelfth argument must be non-zero. Value: `%d`.', strideY ) ); + } + // Check if we can early return... + if ( M === 0 || N === 0 ) { + return y; + } + addon.ndarray( M, N, X, strideX1, strideX2, offsetX, y, strideY, offsetY, w, strideW, offsetW, workspace, strideWS, offsetWS, params ); // eslint-disable-line max-len + return y; +} + + +// EXPORTS // + +module.exports = dsgdTrainer; diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/truncation.js b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/truncation.js new file mode 100644 index 000000000000..2a15a033d79d --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/truncation.js @@ -0,0 +1,121 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var max = require( '@stdlib/math/base/special/max' ); +var min = require( '@stdlib/math/base/special/min' ); + + +// VARIABLES // + +/** +* Truncation functions. +* +* @private +* @name TRUNCATIONS +* @constant +* @type {Object} +* +* @example +* var f = TRUNCATIONS[ 'l2' ]; +* // returns +*/ +var TRUNCATIONS = { + 'elasticnet': l1Truncation, + 'l1': l1Truncation, + 'l2': noTruncation, + 'none': noTruncation +}; + + +// FUNCTIONS // + +/** +* Applies no truncation. +* +* Note: +* +* - Here **params**: `[ ]` (empty) +* +* @private +* @param {NonNegativeInteger} N - number of features (length of `w`) +* @param {number} u - cumulative L1 penalty accumulated so far +* @param {NonNegativeInteger} eta - learning rate. +* @param {number} scaleFactor - current scaling factor applied to the stored weights +* @param {Float64Array} w - weight array +* @param {integer} strideW - stride length for `w` +* @param {NonNegativeInteger} offsetW - starting index for `w` +* @param {Float64Array} workspace - workspace array tracking the total L1 shrinkage applied to each feature +* @param {integer} strideWS - stride length for `workspace` +* @param {NonNegativeInteger} offsetWS - starting index for `workspace` +* @param {Float64Array} params - strided array containing regularizer specific parameters. +* @returns {number} u +*/ +function noTruncation( N, u, eta, scaleFactor, w, strideW, offsetW, workspace, strideWS, offsetWS, params ) { // eslint-disable-line no-unused-vars, max-len, max-params + return u; +} + +/** +* Applies the cumulative L1 penalty to the weight vector. +* +* Note: +* +* - Here **params**: `[ lambda, l1Ratio ]` +* +* @private +* @param {NonNegativeInteger} N - number of features +* @param {number} u - cumulative L1 penalty accumulated so far +* @param {NonNegativeInteger} eta - learning rate. +* @param {number} scaleFactor - current scaling factor applied to the stored weights +* @param {Float64Array} w - weight array +* @param {integer} strideW - stride length for `w` +* @param {NonNegativeInteger} offsetW - starting index for `w` +* @param {Float64Array} workspace - workspace array tracking the total L1 shrinkage applied to each feature +* @param {integer} strideWS - stride length for `workspace` +* @param {NonNegativeInteger} offsetWS - starting index for `workspace` +* @param {Float64Array} params - strided array containing regularizer specific parameters. +* @returns {number} u +*/ +function l1Truncation( N, u, eta, scaleFactor, w, strideW, offsetW, workspace, strideWS, offsetWS, params ) { // eslint-disable-line max-len, max-params + var wsIdx; + var idx; + var z; + var j; + + u += ( eta * params[ 0 ] * params[ 1 ] ); + for ( j = 0; j < N; j++ ) { + idx = offsetW + ( j * strideW ); + wsIdx = offsetWS + ( j * strideWS ); + z = w[ idx ]; + if ( scaleFactor * z > 0.0 ) { + w[ idx ] = max( 0.0, z - ( ( u + workspace[ wsIdx ] ) / scaleFactor ) ); // eslint-disable-line max-len + } else if ( scaleFactor * z < 0.0 ) { + w[ idx ] = min( 0.0, z + ( ( u - workspace[ wsIdx ] ) / scaleFactor ) ); // eslint-disable-line max-len + } + workspace[ wsIdx ] += scaleFactor * ( w[ idx ] - z ); + } + return u; +} + + +// EXPORTS // + +module.exports = TRUNCATIONS; diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/manifest.json b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/manifest.json new file mode 100644 index 000000000000..ea2d92986968 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/manifest.json @@ -0,0 +1,168 @@ +{ + "options": { + "task": "build", + "wasm": false + }, + "fields": [ + { + "field": "src", + "resolve": true, + "relative": true + }, + { + "field": "include", + "resolve": true, + "relative": true + }, + { + "field": "libraries", + "resolve": false, + "relative": false + }, + { + "field": "libpath", + "resolve": true, + "relative": false + } + ], + "confs": [ + { + "task": "build", + "wasm": false, + "src": [ + "./src/main.c" + ], + "include": [ + "./include" + ], + "libraries": [], + "libpath": [], + "dependencies": [ + "@stdlib/blas/base/shared", + "@stdlib/strided/base/stride2offset", + "@stdlib/blas/base/ddot", + "@stdlib/blas/base/daxpy", + "@stdlib/blas/base/dscal", + "@stdlib/ml/base/sgd/learning-rates", + "@stdlib/ml/base/sgd/loss-functions", + "@stdlib/ml/base/sgd/penalties", + "@stdlib/ml/base/loss/float64/epsilon-insensitive-gradient", + "@stdlib/ml/base/loss/float64/hinge-gradient", + "@stdlib/ml/base/loss/float64/huber-gradient", + "@stdlib/ml/base/loss/float64/log-gradient", + "@stdlib/ml/base/loss/float64/modified-huber-gradient", + "@stdlib/ml/base/loss/float64/squared-epsilon-insensitive-gradient", + "@stdlib/ml/base/loss/float64/squared-error-gradient", + "@stdlib/ml/base/loss/float64/squared-hinge-gradient", + "@stdlib/math/base/special/max", + "@stdlib/math/base/special/min", + "@stdlib/math/base/special/pow", + "@stdlib/napi/export", + "@stdlib/napi/argv", + "@stdlib/napi/argv-int64", + "@stdlib/napi/argv-int32", + "@stdlib/napi/argv-strided-float64array", + "@stdlib/napi/create-double" + ] + }, + { + "task": "benchmark", + "wasm": false, + "src": [ + "./src/main.c" + ], + "include": [ + "./include" + ], + "libraries": [], + "libpath": [], + "dependencies": [ + "@stdlib/blas/base/shared", + "@stdlib/strided/base/stride2offset", + "@stdlib/blas/base/ddot", + "@stdlib/blas/base/daxpy", + "@stdlib/blas/base/dscal", + "@stdlib/ml/base/sgd/learning-rates", + "@stdlib/ml/base/sgd/loss-functions", + "@stdlib/ml/base/sgd/penalties", + "@stdlib/ml/base/loss/float64/epsilon-insensitive-gradient", + "@stdlib/ml/base/loss/float64/hinge-gradient", + "@stdlib/ml/base/loss/float64/huber-gradient", + "@stdlib/ml/base/loss/float64/log-gradient", + "@stdlib/ml/base/loss/float64/modified-huber-gradient", + "@stdlib/ml/base/loss/float64/squared-epsilon-insensitive-gradient", + "@stdlib/ml/base/loss/float64/squared-error-gradient", + "@stdlib/ml/base/loss/float64/squared-hinge-gradient", + "@stdlib/math/base/special/max", + "@stdlib/math/base/special/min", + "@stdlib/math/base/special/pow" + ] + }, + { + "task": "examples", + "wasm": false, + "src": [ + "./src/main.c" + ], + "include": [ + "./include" + ], + "libraries": [], + "libpath": [], + "dependencies": [ + "@stdlib/blas/base/shared", + "@stdlib/strided/base/stride2offset", + "@stdlib/blas/base/ddot", + "@stdlib/blas/base/daxpy", + "@stdlib/blas/base/dscal", + "@stdlib/ml/base/sgd/learning-rates", + "@stdlib/ml/base/sgd/loss-functions", + "@stdlib/ml/base/sgd/penalties", + "@stdlib/ml/base/loss/float64/epsilon-insensitive-gradient", + "@stdlib/ml/base/loss/float64/hinge-gradient", + "@stdlib/ml/base/loss/float64/huber-gradient", + "@stdlib/ml/base/loss/float64/log-gradient", + "@stdlib/ml/base/loss/float64/modified-huber-gradient", + "@stdlib/ml/base/loss/float64/squared-epsilon-insensitive-gradient", + "@stdlib/ml/base/loss/float64/squared-error-gradient", + "@stdlib/ml/base/loss/float64/squared-hinge-gradient", + "@stdlib/math/base/special/max", + "@stdlib/math/base/special/min", + "@stdlib/math/base/special/pow" + ] + }, + { + "task": "", + "wasm": true, + "src": [ + "./src/main.c" + ], + "include": [ + "./include" + ], + "libraries": [], + "libpath": [], + "dependencies": [ + "@stdlib/blas/base/shared", + "@stdlib/strided/base/stride2offset", + "@stdlib/blas/base/ddot", + "@stdlib/blas/base/daxpy", + "@stdlib/blas/base/dscal", + "@stdlib/ml/base/sgd/learning-rates", + "@stdlib/ml/base/sgd/loss-functions", + "@stdlib/ml/base/sgd/penalties", + "@stdlib/ml/base/loss/float64/epsilon-insensitive-gradient", + "@stdlib/ml/base/loss/float64/hinge-gradient", + "@stdlib/ml/base/loss/float64/huber-gradient", + "@stdlib/ml/base/loss/float64/log-gradient", + "@stdlib/ml/base/loss/float64/modified-huber-gradient", + "@stdlib/ml/base/loss/float64/squared-epsilon-insensitive-gradient", + "@stdlib/ml/base/loss/float64/squared-error-gradient", + "@stdlib/ml/base/loss/float64/squared-hinge-gradient", + "@stdlib/math/base/special/max", + "@stdlib/math/base/special/min", + "@stdlib/math/base/special/pow" + ] + } + ] +} diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/package.json b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/package.json new file mode 100644 index 000000000000..456c7aae6d17 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/package.json @@ -0,0 +1,61 @@ +{ + "name": "@stdlib/ml", + "version": "0.0.0", + "description": "Machine learning algorithms.", + "license": "Apache-2.0", + "author": { + "name": "The Stdlib Authors", + "url": "https://github.com/stdlib-js/stdlib/graphs/contributors" + }, + "contributors": [ + { + "name": "The Stdlib Authors", + "url": "https://github.com/stdlib-js/stdlib/graphs/contributors" + } + ], + "main": "./lib", + "directories": { + "doc": "./docs", + "example": "./examples", + "lib": "./lib", + "test": "./test" + }, + "types": "./docs/types", + "scripts": {}, + "homepage": "https://github.com/stdlib-js/stdlib", + "repository": { + "type": "git", + "url": "git://github.com/stdlib-js/stdlib.git" + }, + "bugs": { + "url": "https://github.com/stdlib-js/stdlib/issues" + }, + "dependencies": {}, + "devDependencies": {}, + "engines": { + "node": ">=0.10.0", + "npm": ">2.7.0" + }, + "os": [ + "aix", + "darwin", + "freebsd", + "linux", + "macos", + "openbsd", + "sunos", + "win32", + "windows" + ], + "keywords": [ + "stdlib", + "stdml", + "sgd", + "strided", + "schocastic gradient descent", + "sgd trainer", + "machine", + "learning", + "ml" + ] +} diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/src/Makefile b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/src/Makefile new file mode 100644 index 000000000000..7733b6180cb4 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/src/Makefile @@ -0,0 +1,70 @@ +#/ +# @license Apache-2.0 +# +# Copyright (c) 2025 The Stdlib Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#/ + +# VARIABLES # + +ifndef VERBOSE + QUIET := @ +else + QUIET := +endif + +# Determine the OS ([1][1], [2][2]). +# +# [1]: https://en.wikipedia.org/wiki/Uname#Examples +# [2]: http://stackoverflow.com/a/27776822/2225624 +OS ?= $(shell uname) +ifneq (, $(findstring MINGW,$(OS))) + OS := WINNT +else +ifneq (, $(findstring MSYS,$(OS))) + OS := WINNT +else +ifneq (, $(findstring CYGWIN,$(OS))) + OS := WINNT +else +ifneq (, $(findstring Windows_NT,$(OS))) + OS := WINNT +endif +endif +endif +endif + + +# RULES # + +#/ +# Removes generated files for building an add-on. +# +# @example +# make clean-addon +#/ +clean-addon: + $(QUIET) -rm -f *.o *.node + +.PHONY: clean-addon + +#/ +# Removes generated files. +# +# @example +# make clean +#/ +clean: clean-addon + +.PHONY: clean diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/src/addon.c b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/src/addon.c new file mode 100644 index 000000000000..f8d62df125ee --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/src/addon.c @@ -0,0 +1,100 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2025 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "stdlib/ml/strided/dsgd_trainer.h" +#include "stdlib/ml/base/sgd/params/float64.h" +#include "stdlib/blas/base/shared.h" +#include "stdlib/napi/export.h" +#include "stdlib/napi/argv.h" +#include "stdlib/napi/argv_int64.h" +#include "stdlib/napi/argv_int32.h" +#include "stdlib/napi/argv_strided_float64array.h" +#include "stdlib/napi/argv_strided_float64array2d.h" +#include "stdlib/napi/argv_dataview_cast.h" +#include + +/** +* Receives JavaScript callback invocation data. +* +* @param env environment under which the function is invoked +* @param info callback data +* @return Node-API value +*/ +static napi_value addon( napi_env env, napi_callback_info info ) { + CBLAS_INT sx1; + CBLAS_INT sx2; + + STDLIB_NAPI_ARGV( env, info, argv, argc, 12 ); + + STDLIB_NAPI_ARGV_INT32( env, layout, argv, 0 ); + + STDLIB_NAPI_ARGV_INT64( env, M, argv, 1 ); + STDLIB_NAPI_ARGV_INT64( env, N, argv, 2 ); + STDLIB_NAPI_ARGV_INT64( env, strideY, argv, 6 ); + STDLIB_NAPI_ARGV_INT64( env, strideW, argv, 8 ); + STDLIB_NAPI_ARGV_INT64( env, strideWS, argv, 10 ); + STDLIB_NAPI_ARGV_INT64( env, LDX, argv, 4 ); + + if ( layout == CblasColMajor ) { + sx1 = 1; + sx2 = LDX; + } else { // layout == CblasRowMajor + sx1 = LDA; + sx2 = 1; + } + STDLIB_NAPI_ARGV_STRIDED_FLOAT64ARRAY( env, y, M, strideY, argv, 5 ); + STDLIB_NAPI_ARGV_STRIDED_FLOAT64ARRAY( env, w, N, strideY, argv, 7 ); + STDLIB_NAPI_ARGV_STRIDED_FLOAT64ARRAY( env, ws, N, strideY, argv, 9 ); + STDLIB_NAPI_ARGV_STRIDED_FLOAT64ARRAY2D( env, X, M, N, sx1, sx2, argv, 3 ); + STDLIB_NAPI_ARGV_DATAVIEW_CAST( env, params, struct stdlib_ml_sgd_float64_params, argv, 7 ); + API_SUFFIX(stdlib_strided_dsgd_trainer)( order, M, N, X, LDX, y, strideY, w, strideW, workspace, strideWS, params ); + return NULL; +} + +/** +* Receives JavaScript callback invocation data. +* +* @param env environment under which the function is invoked +* @param info callback data +* @return Node-API value +*/ +static napi_value addon_method( napi_env env, napi_callback_info info ) { + STDLIB_NAPI_ARGV( env, info, argv, argc, 12 ); + + STDLIB_NAPI_ARGV_INT64( env, M, argv, 0 ); + STDLIB_NAPI_ARGV_INT64( env, N, argv, 1 ); + STDLIB_NAPI_ARGV_INT64( env, strideY, argv, 7 ); + STDLIB_NAPI_ARGV_INT64( env, offsetY, argv, 8 ); + STDLIB_NAPI_ARGV_INT64( env, strideW, argv, 10 ); + STDLIB_NAPI_ARGV_INT64( env, offsetW, argv, 11 ); + STDLIB_NAPI_ARGV_INT64( env, strideWS, argv, 12 ); + STDLIB_NAPI_ARGV_INT64( env, offsetWS, argv, 13 ); + STDLIB_NAPI_ARGV_INT64( env, strideX1, argv, 3 ); + STDLIB_NAPI_ARGV_INT64( env, strideX2, argv, 4 ); + STDLIB_NAPI_ARGV_INT64( env, offsetX, argv, 5 ); + + STDLIB_NAPI_ARGV_STRIDED_FLOAT64ARRAY( env, y, M, strideY, argv, 6 ); + STDLIB_NAPI_ARGV_STRIDED_FLOAT64ARRAY( env, w, N, strideY, argv, 9 ); + STDLIB_NAPI_ARGV_STRIDED_FLOAT64ARRAY( env, ws, N, strideY, argv, 12 ); + STDLIB_NAPI_ARGV_STRIDED_FLOAT64ARRAY2D( env, X, M, N, sx1, sx2, argv, 2 ); + STDLIB_NAPI_ARGV_DATAVIEW_CAST( env, params, struct stdlib_ml_sgd_float64_params, argv, 14 ); + API_SUFFIX(stdlib_strided_dsgd_trainer_ndarray)( M, N, X, strideX1, strideX2, offsetX, y, strideY, offsetY, w, strideW, offsetW, workspace, strideWS, offsetWS, params ); + return NULL; +} + +STDLIB_NAPI_MODULE_EXPORT_FCN_WITH_METHOD( addon, "ndarray", addon_method ) diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/src/main.c b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/src/main.c new file mode 100644 index 000000000000..88a4000f00d2 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/src/main.c @@ -0,0 +1,549 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2025 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "stdlib/ml/strided/dsgd_trainer.h" +#include "stdlib/ml/base/sgd/learning_rates.h" +#include "stdlib/ml/base/sgd/loss_functions.h" +#include "stdlib/ml/base/sgd/penalties.h" +#include "stdlib/ml/base/loss/float64/epsilon_insensitive_gradient.h" +#include "stdlib/ml/base/loss/float64/hinge_gradient.h" +#include "stdlib/ml/base/loss/float64/huber_gradient.h" +#include "stdlib/ml/base/loss/float64/log_gradient.h" +#include "stdlib/ml/base/loss/float64/modified_huber_gradient.h" +#include "stdlib/ml/base/loss/float64/squared_epsilon_insensitive_gradient.h" +#include "stdlib/ml/base/loss/float64/squared_error_gradient.h" +#include "stdlib/ml/base/loss/float64/squared_hinge_gradient.h" +#include "stdlib/math/base/special/max.h" +#include "stdlib/math/base/special/min.h" +#include "stdlib/math/base/special/pow.h" +#include "stdlib/blas/base/shared.h" +#include "stdlib/blas/base/ddot.h" +#include "stdlib/blas/base/daxpy.h" +#include "stdlib/blas/base/dscal.h" +#include "stdlib/strided/base/stride2offset.h" +#include +#include + +static const double MIN_SCALE = 1.0e-9; +static const double MAX_DLOSS = 1.0e12; + +/** +* Computes a learning rate according to the `basic` learning rate scheduler. +* +* @param t current iteration +* @return learning rate +*/ +static double basic( const CBLAS_INT t ) { + return 10.0 / ( 10.0*(double)t ); +} + +/** +* Computes a learning rate according to the `constant` learning rate scheduler. +* +* ## Notes +* +* - The parameter list is `[ eta0 ]`. +* +* @param params scheduler parameters +* @return learning rate +*/ +static double constant( const double *params ) { + return params[ 0 ]; // eta0 +} + +/** +* Computes a learning rate according to the `invscaling` learning rate scheduler. +* +* ## Notes +* +* - The parameter list is `[ eta0, powerT ]`. +* +* @param t current iteration +* @param params scheduler parameters +* @return learning rate +*/ +static double invscaling( const CBLAS_INT t, const double *params ) { + return params[ 0 ] / stdlib_base_pow( (double)t, params[ 1 ] ); +} + +/** +* Computes a learning rate according to the `pegasos` learning rate scheduler. +* +* ## Notes +* +* - The parameter list is `[ lambda ]`. +* +* @param t current iteration +* @param params scheduler parameters +* @return learning rate +*/ +static double pegasos( const CBLAS_INT t, const double *params ) { + return 1.0 / ( params[ 0 ]*(double)t ); +} + +/** +* Computes the gradient of the epsilon-insensitive loss. +* +* ## Notes +* +* - The parameter list is `[ epsilon ]`. +* +* @param y target value +* @param p predicted value +* @param params loss function parameters +* @return loss gradient +*/ +static double epsilon_insensitive( const double y, const double p, const double *params ) { + return stdlib_base_float64_epsilon_insensitive_gradient( 1.0, params[ 0 ], y, p ); +} + +/** +* Computes the gradient of the hinge loss. +* +* @param y target value +* @param p predicted value +* @return loss gradient +*/ +static double hinge( const double y, const double p ) { + return stdlib_base_float64_hinge_gradient( 1.0, 1.0, y, p ); +} + +/** +* Computes the gradient of the Huber loss. +* +* ## Notes +* +* - The parameter list is `[ threshold ]`. +* +* @param y target value +* @param p predicted value +* @param params loss function parameters +* @return loss gradient +*/ +static double huber( const double y, const double p, const double *params ) { + return stdlib_base_float64_huber_gradient( 1.0, params[ 0 ], y, p ); +} + +/** +* Computes the gradient of the log loss. +* +* @param y target value +* @param p predicted value +* @return loss gradient +*/ +static double log_loss( const double y, const double p ) { + return stdlib_base_float64_log_gradient( 1.0, y, p ); +} + +/** +* Computes the gradient of the modified Huber loss. +* +* @param y target value +* @param p predicted value +* @return loss gradient +*/ +static double modified_huber( const double y, const double p ) { + return stdlib_base_float64_modified_huber_gradient( 1.0, y, p ); +} + +/** +* Computes the gradient of the perceptron loss. +* +* ## Notes +* +* - The perceptron loss is the hinge loss without a margin, and thus we resolve the gradient using the hinge loss gradient with a zero margin threshold. +* +* @param y target value +* @param p predicted value +* @return loss gradient +*/ +static double perceptron( const double y, const double p ) { + return stdlib_base_float64_hinge_gradient( 1.0, 0.0, y, p ); +} + +/** +* Computes the gradient of the squared epsilon-insensitive loss. +* +* ## Notes +* +* - The parameter list is `[ epsilon ]`. +* +* @param y target value +* @param p predicted value +* @param params loss function parameters +* @return loss gradient +*/ +static double squared_epsilon_insensitive( const double y, const double p, const double *params ) { + return stdlib_base_float64_squared_epsilon_insensitive_gradient( 1.0, params[ 0 ], y, p ); +} + +/** +* Computes the gradient of the squared error loss. +* +* @param y target value +* @param p predicted value +* @return loss gradient +*/ +static double squared_error( const double y, const double p ) { + return stdlib_base_float64_squared_error_gradient( 1.0, y, p ); +} + +/** +* Computes the gradient of the squared hinge loss. +* +* @param y target value +* @param p predicted value +* @return loss gradient +*/ +static double squared_hinge( const double y, const double p ) { + return stdlib_base_float64_squared_hinge_gradient( 1.0, 1.0, y, p ); +} + +/** +* Computes a scale factor by applying the identity decay. +* +* @param scaleFactor current scale factor +* @return scale factor +*/ +static double identity_decay( const double scaleFactor ) { + return scaleFactor; +} + +/** +* Computes a scale factor by applying the L2 decay. +* +* ## Notes +* +* - The parameter list is `[ lambda, l1Ratio ]`. +* +* @param scaleFactor current scale factor +* @param eta learning rate +* @param params penalty parameters +* @return scale factor +*/ +static double l2_decay( const double scaleFactor, const double eta, const double *params ) { + return scaleFactor * stdlib_base_max( 0.0, 1.0-( ( 1.0-params[ 1 ] )*eta*params[ 0 ] ) ); +} + +/** +* Applies the cumulative L1 penalty to a weight vector. +* +* ## Notes +* +* - The parameter list is `[ lambda, l1Ratio ]`. +* +* @param N number of features +* @param u cumulative L1 penalty accumulated thus far +* @param eta learning rate +* @param scaleFactor current scale factor applied to the stored weights +* @param w weight array +* @param strideW stride length for `w` +* @param offsetW starting index for `w` +* @param ws workspace array tracking the total L1 shrinkage applied to each feature +* @param strideWS stride length for `ws` +* @param offsetWS starting index for `ws` +* @param params penalty parameters +* @return cumulative L1 penalty +*/ +static double l1_truncation( const CBLAS_INT N, double u, const double eta, const double scaleFactor, double *w, const CBLAS_INT strideW, const CBLAS_INT offsetW, double *ws, const CBLAS_INT strideWS, const CBLAS_INT offsetWS, const double *params ) { + CBLAS_INT wsIdx; + CBLAS_INT idx; + CBLAS_INT j; + double z; + + u += ( eta*params[ 0 ]*params[ 1 ] ); + for ( j = 0; j < N; j++ ) { + idx = offsetW + ( j*strideW ); + wsIdx = offsetWS + ( j*strideWS ); + z = w[ idx ]; + if ( scaleFactor*z > 0.0 ) { + w[ idx ] = stdlib_base_max( 0.0, z-( ( u+ws[ wsIdx ] )/scaleFactor ) ); + } else if ( scaleFactor*z < 0.0 ) { + w[ idx ] = stdlib_base_min( 0.0, z+( ( u-ws[ wsIdx ] )/scaleFactor ) ); + } + ws[ wsIdx ] += scaleFactor*( w[ idx ]-z ); + } + return u; +} + +/** +* Computes a learning rate according to a specified learning rate scheduler. +* +* @param learningRate learning rate scheduler +* @param t current iteration +* @param params scheduler parameters +* @return learning rate +*/ +static double stdlib_ml_sgd_learning_rate( const enum STDLIB_ML_SGD_LEARNING_RATE learningRate, const CBLAS_INT t, const double *params ) { + switch ( learningRate ) { + case STDLIB_ML_SGD_CONSTANT: + return constant( params ); + case STDLIB_ML_SGD_INVSCALING: + return invscaling( t, params ); + case STDLIB_ML_SGD_PEGASOS: + return pegasos( t, params ); + case STDLIB_ML_SGD_BASIC: + default: + return basic( t ); + } +} + +/** +* Computes the gradient of a specified loss function. +* +* @param lossFunction loss function +* @param y target value +* @param p predicted value +* @param params loss function parameters +* @return loss gradient +*/ +static double stdlib_ml_sgd_loss_gradient( const enum STDLIB_ML_SGD_LOSS_FUNCTION lossFunction, const double y, const double p, const double *params ) { + switch ( lossFunction ) { + case STDLIB_ML_SGD_EPSILON_INSENSITIVE: + return epsilon_insensitive( y, p, params ); + case STDLIB_ML_SGD_HUBER: + return huber( y, p, params ); + case STDLIB_ML_SGD_LOG: + return log_loss( y, p ); + case STDLIB_ML_SGD_MODIFIED_HUBER: + return modified_huber( y, p ); + case STDLIB_ML_SGD_PERCEPTRON: + return perceptron( y, p ); + case STDLIB_ML_SGD_SQUARED_EPSILON_INSENSITIVE: + return squared_epsilon_insensitive( y, p, params ); + case STDLIB_ML_SGD_SQUARED_ERROR: + return squared_error( y, p ); + case STDLIB_ML_SGD_SQUARED_HINGE: + return squared_hinge( y, p ); + case STDLIB_ML_SGD_HINGE: + default: + return hinge( y, p ); + } +} + +/** +* Computes a scale factor according to the weight decay associated with a specified penalty. +* +* @param penalty regularization function +* @param scaleFactor current scale factor +* @param eta learning rate +* @param params penalty parameters +* @return scale factor +*/ +static double stdlib_ml_sgd_decay( const enum STDLIB_ML_SGD_PENALTY penalty, const double scaleFactor, const double eta, const double *params ) { + switch ( penalty ) { + case STDLIB_ML_SGD_ELASTICNET: + case STDLIB_ML_SGD_L2: + return l2_decay( scaleFactor, eta, params ); + case STDLIB_ML_SGD_L1: + case STDLIB_ML_SGD_NONE: + default: + return identity_decay( scaleFactor ); + } +} + +/** +* Applies the truncation associated with a specified penalty to a weight vector. +* +* @param penalty regularization function +* @param N number of features +* @param u cumulative L1 penalty accumulated thus far +* @param eta learning rate +* @param scaleFactor current scale factor applied to the stored weights +* @param w weight array +* @param strideW stride length for `w` +* @param offsetW starting index for `w` +* @param ws workspace array tracking the total L1 shrinkage applied to each feature +* @param strideWS stride length for `ws` +* @param offsetWS starting index for `ws` +* @param params penalty parameters +* @return cumulative L1 penalty +*/ +static double stdlib_ml_sgd_truncation( const enum STDLIB_ML_SGD_PENALTY penalty, const CBLAS_INT N, const double u, const double eta, const double scaleFactor, double *w, const CBLAS_INT strideW, const CBLAS_INT offsetW, double *ws, const CBLAS_INT strideWS, const CBLAS_INT offsetWS, const double *params ) { + switch ( penalty ) { + case STDLIB_ML_SGD_ELASTICNET: + case STDLIB_ML_SGD_L1: + return l1_truncation( N, u, eta, scaleFactor, w, strideW, offsetW, ws, strideWS, offsetWS, params ); + case STDLIB_ML_SGD_L2: + case STDLIB_ML_SGD_NONE: + default: + return u; + } +} + +/** +* Trains a linear model via stochastic gradient descent. +* +* ## Notes +* +* - When `params->fitIntercept` is `true`, `w` must have `N+1` indexed elements, as the fitted intercept is written to the element following the `N` feature weights. +* - Upon return, `w` contains effective (i.e., fully rescaled) weights. +* +* @param layout storage layout of `X` +* @param M number of samples +* @param N number of features +* @param X input matrix +* @param LDX stride of the major dimension of `X` +* @param y target array +* @param strideY stride length for `y` +* @param w weight array +* @param strideW stride length for `w` +* @param ws workspace array +* @param strideWS stride length for `ws` +* @param params parameters +* @return `w` +*/ +double* API_SUFFIX(stdlib_strided_dsgd_trainer)( const CBLAS_LAYOUT layout, const CBLAS_INT M, const CBLAS_INT N, const double *X, const CBLAS_INT LDX, const double *y, const CBLAS_INT strideY, double *w, const CBLAS_INT strideW, double *ws, const CBLAS_INT strideWS, const struct stdlib_ml_sgd_float64_params *params ) { + CBLAS_INT sx1; + CBLAS_INT sx2; + CBLAS_INT ows; + CBLAS_INT oy; + CBLAS_INT ow; + + // Check if we can early return... + if ( M <= 0 || N <= 0 ) { + return w; + } + if ( layout == CblasColMajor ) { + sx1 = 1; + sx2 = LDX; + } else { // layout == CblasRowMajor + sx1 = LDX; + sx2 = 1; + } + oy = (CBLAS_INT)stdlib_strided_stride2offset( (int64_t)M, (int64_t)strideY ); + ow = (CBLAS_INT)stdlib_strided_stride2offset( (int64_t)N, (int64_t)strideW ); + ows = (CBLAS_INT)stdlib_strided_stride2offset( (int64_t)N, (int64_t)strideWS ); + return API_SUFFIX(stdlib_strided_dsgd_trainer_ndarray)( M, N, X, sx1, sx2, 0, y, strideY, oy, w, strideW, ow, ws, strideWS, ows, params ); +} + +/** +* Trains a linear model via stochastic gradient descent using alternative indexing semantics. +* +* ## Notes +* +* - When `params->fitIntercept` is `true`, `w` must have `N+1` indexed elements, as the fitted intercept is written to the element following the `N` feature weights. +* - Upon return, `w` contains effective (i.e., fully rescaled) weights. +* +* @param M number of samples +* @param N number of features +* @param X input matrix +* @param strideX1 stride of the first dimension of `X` +* @param strideX2 stride of the second dimension of `X` +* @param offsetX starting index for `X` +* @param y target array +* @param strideY stride length for `y` +* @param offsetY starting index for `y` +* @param w weight array +* @param strideW stride length for `w` +* @param offsetW starting index for `w` +* @param ws workspace array +* @param strideWS stride length for `ws` +* @param offsetWS starting index for `ws` +* @param params parameters +* @return `w` +*/ +double* API_SUFFIX(stdlib_strided_dsgd_trainer_ndarray)( const CBLAS_INT M, const CBLAS_INT N, const double *X, const CBLAS_INT strideX1, const CBLAS_INT strideX2, const CBLAS_INT offsetX, const double *y, const CBLAS_INT strideY, const CBLAS_INT offsetY, double *w, const CBLAS_INT strideW, const CBLAS_INT offsetW, double *ws, const CBLAS_INT strideWS, const CBLAS_INT offsetWS, const struct stdlib_ml_sgd_float64_params *params ) { + enum STDLIB_ML_SGD_LOSS_FUNCTION lossFunction; + enum STDLIB_ML_SGD_LEARNING_RATE learningRate; + enum STDLIB_ML_SGD_PENALTY penalty; + double penaltyParams[ 2 ]; + double scaleFactor; + double intercept; + bool fitIntercept; + int32_t maxIter; + int32_t epoch; + double update; + double dloss; + double eta; + double p; + double u; + CBLAS_INT ox; + CBLAS_INT oy; + CBLAS_INT i; + CBLAS_INT t; + + // Check if we can early return... + if ( M <= 0 || N <= 0 ) { + return w; + } + penalty = (enum STDLIB_ML_SGD_PENALTY)params->penalty; + learningRate = (enum STDLIB_ML_SGD_LEARNING_RATE)params->learningRate; + lossFunction = (enum STDLIB_ML_SGD_LOSS_FUNCTION)params->lossFunction; + fitIntercept = params->fitIntercept; + intercept = params->intercept; + maxIter = params->maxIter; + + // Resolve the effective L1 ratio, as `l1` and `l2` are the limiting cases of `elasticnet`... + penaltyParams[ 0 ] = params->penaltyParams[ 0 ]; + if ( penalty == STDLIB_ML_SGD_L2 ) { + penaltyParams[ 1 ] = 0.0; + } else if ( penalty == STDLIB_ML_SGD_L1 ) { + penaltyParams[ 1 ] = 1.0; + } else { + penaltyParams[ 1 ] = params->penaltyParams[ 1 ]; + } + + t = 1; + u = 0.0; + scaleFactor = 1.0; + for ( epoch = 1; epoch <= maxIter; epoch++ ) { + ox = offsetX; + oy = offsetY; + for ( i = 0; i < M; i++ ) { + p = ( scaleFactor*API_SUFFIX(c_ddot_ndarray)( N, w, strideW, offsetW, X, strideX2, ox ) ) + intercept; + + eta = stdlib_ml_sgd_learning_rate( learningRate, t, params->learningRateParams ); + + dloss = stdlib_ml_sgd_loss_gradient( lossFunction, y[ oy ], p, params->lossFunctionParams ); + if ( dloss < -MAX_DLOSS ) { + dloss = -MAX_DLOSS; + } else if ( dloss > MAX_DLOSS ) { + dloss = MAX_DLOSS; + } + update = -eta * dloss; + + scaleFactor = stdlib_ml_sgd_decay( penalty, scaleFactor, eta, penaltyParams ); + if ( scaleFactor < MIN_SCALE ) { + API_SUFFIX(c_dscal_ndarray)( N, scaleFactor, w, strideW, offsetW ); + scaleFactor = 1.0; + } + + // Gradient step: as `w_eff = w_stored*scaleFactor`, adding `update*X` to the effective weights amounts to adding `(update/scaleFactor)*X` to the stored weights... + API_SUFFIX(c_daxpy_ndarray)( N, update/scaleFactor, X, strideX2, ox, w, strideW, offsetW ); + + if ( fitIntercept ) { + intercept += update; + } + u = stdlib_ml_sgd_truncation( penalty, N, u, eta, scaleFactor, w, strideW, offsetW, ws, strideWS, offsetWS, penaltyParams ); + + t += 1; + ox += strideX1; + oy += strideY; + } + } + // Fold any residual scaling back into the weights so that `w` holds effective values... + if ( scaleFactor != 1.0 ) { + API_SUFFIX(c_dscal_ndarray)( N, scaleFactor, w, strideW, offsetW ); + scaleFactor = 1.0; + } + if ( fitIntercept ) { + w[ offsetW+( strideW*N ) ] = intercept; + } + return w; +} diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/large_epsilon.json b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/large_epsilon.json new file mode 100644 index 000000000000..030fd4e7d036 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/large_epsilon.json @@ -0,0 +1 @@ +{"M": 20, "N": 3, "X": [0.22274577223842806, -1.7083359837461911, -0.48593264543917547, -0.15564389511208487, 1.1141617836213031, 0.8817708703819678, -1.0751820757976778, -1.9018224309183172, -0.9705402363426794, -0.3595358804727704, -1.9664040174266906, -0.11380421328565449, 1.4096884434229118, 1.692718793395077, -1.074255464344596, 1.963819657909315, -1.9760084142172412, -0.6589979105217347, 1.1570324517718937, -1.6059539391777093, -0.23067100288255116, -0.47732270207524996, 1.3303938764231757, 0.8478487413814544, -0.5799966971260195, -1.9014546620136668, 0.7740500757099307, -0.3151522344890023, 1.4333977727941112, 0.3002942151591923, 1.7602494486433122, 1.5210220777363088, -0.1674198777895275, -1.9307597298715238, -1.7253814344758114, -0.7175679354479967, 0.36920754750184814, 0.4402097532709326, 0.049710013450857016, -1.420974166639183, 1.507234772811977, 0.9529407166922663, -0.5067184644285883, 1.314745532503959, -1.6390418500336121, -1.7439871251107384, 0.02358779386919485, 1.7337068457391682, -1.4041523281733261, 0.2779151728262823, -1.7610053800982062, -1.322888292544099, -1.7238865596835367, 1.624089804355524, -0.21035975899734938, -1.5744591977166338, 0.6610391141070506, 0.009093508577587617, -0.3631744717872851, 0.9119738813524925], "y": [0.38306968005788355, -0.620994106789678, 1.8263412664555343, 0.7123611387680657, -0.7181045811759956, -0.6272033559573793, -0.3587832253693836, -0.5709853881985854, 0.3770180369958464, -0.21520863563092085, -1.2251363899114658, 1.9043270163760648, -0.3880543279394097, 0.2020596199466619, 0.8406010485785755, 0.4403855623726914, 1.7761122422000957, 0.598255825196784, 0.19698300613574402, -0.3542045970507915], "w": [0.03347067705588824, 0.043413585502444216, -0.0029206644103943052, 0.0], "workspace": [0.0, 0.0, 0.0], "order": "row-major", "LDX": 3, "penalty": "l2", "loss_function": "squared-epsilon-insensitive", "learning_rate": "constant", "lambda": 0.0001, "l1_ratio": 0.15, "eta0": 0.01, "power_t": 0.25, "epsilon": 2.0, "max_iter": 5, "fit_intercept": true, "intercept": 0.0, "expected_w": [0.02765563296514642, 0.0382157475828472, -0.005080292874998354], "expected_intercept": 0.003010223004876238} diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/large_nfeatures.json b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/large_nfeatures.json new file mode 100644 index 000000000000..26acb1ef1120 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/large_nfeatures.json @@ -0,0 +1 @@ +{"M": 30, "N": 8, "X": [0.24406608575136213, -0.21529754011451363, 1.4211961042580485, 1.6447224913912182, 1.5062591721455232, 1.8617169026554499, -1.1907547762138444, 0.9055034356246567, 0.2852720648873577, 0.33570931844287566, -0.17311241073736605, 0.32844337406901714, -1.7290068633063957, -1.9761979095091267, -0.12795300229978723, 1.160910970386296, 1.6917954318649921, 0.8809656102276806, -1.496275864599847, 0.8763698658885728, 0.4619770776691654, -0.4531263900606386, 1.140856158180573, -1.5946237573897495, -0.3882485148806487, 0.9695867073331166, -1.9502647547520078, 1.7127306800490083, -0.1591060560912143, -1.7214999261614716, 1.639723484584045, -1.5067820003174996, 1.5791266523221297, -1.179759909246731, 1.7279551435546812, -0.008283444613439439, -0.5599837735715942, -1.3565152284267339, 0.9055038844851993, 1.3410994124192426, -0.7244844169012525, 1.9122614588090574, -0.10915525637133516, 0.2645775847486429, -1.4132623630958068, -1.6010223273189461, -1.6539314942656325, 1.012055308806242, 0.9691572003745508, -0.7379745652112271, 1.3812577088027989, 0.13818663028407396, 1.330281417925553, -1.255864060709623, -0.8961878968130037, 0.3322303790036387, 1.7198058487629528, -1.7708184101128936, 1.2731893908106016, -1.3415081022098154, -0.24117557726353267, -0.5339169573863884, -1.7368848491798525, 1.151235517791974, 0.04955223884629545, -1.2926459833764357, 0.0833248701609306, 0.3640362570761879, -0.22184554198509643, -1.4279679795347633, 1.8450564396854516, -0.3289208945959716, -1.479899451144708, 0.6039723036107008, 0.3516216049745906, -0.9810499778518729, -0.850268678833523, 1.2211154388653886, -1.7751694918602339, -0.8872332473934885, 1.7468455701656112, 1.3536262799059595, 1.6326954173592054, 0.3784683498825774, 1.0940052779091949, -0.6236000226751939, -0.464530912125384, -0.4882350677353071, -0.2961433877982409, 0.8275913887469071, -1.0233174829072604, -1.288874549481812, -1.1341381442180412, 0.8975615932442276, 0.5259480771625031, 1.178139615022297, 1.1428740553232348, -1.4200907047539815, -1.6500473826288466, -0.972972302414425, 0.5604696494530224, 0.27883156042496626, 1.5573727815097147, 1.672567463152547, -1.3904602432392985, -1.5240599794547416, -1.9597854210606145, -0.8806420462158604, -1.5083115557885938, -0.1694557012696154, -1.246394058523419, -1.9695541020803402, 1.5853519075513391, 1.9902117953223968, 1.2240162147882647, -1.8793282667131406, 0.14129393779619992, 0.44000200404302836, 1.4056945604678268, -0.3601679845686365, -0.954411898628146, 1.0191639279519582, 1.1643039302757066, 1.149158811795059, 1.7911274743006813, -0.39916705635423044, 0.632865529253098, 0.1025448309565955, -0.3274283776413882, -0.36985873720757967, -0.5149243745409353, 1.3755774971558457, -0.11669568909863148, 0.3395983716545259, -1.3371279134948146, 0.18863297422354064, 0.15527408601039694, 0.030519816092046348, 0.5723235331990484, 0.17873494711133686, -0.7269822167820785, 1.152765358473276, 0.2577013425915573, -0.33768455901425165, -0.688277350887585, 1.6825925546885387, 1.7218770592016175, -1.9572329931260577, -1.6276708837002318, -1.4636422255835022, 0.5093480973462148, -0.9532330984460269, 0.3301718297638234, 0.21960795160166757, -1.1986373428402497, 0.6283204800673983, 1.7728275481211115, -0.5729292637365933, -1.8727949356744116, -0.10589184343790103, -1.7301696308699226, -0.002984552135096852, 0.7685749240111588, -1.6955295357762799, -1.080679881604011, -0.010528531182274925, 0.17116290693535374, -0.02026042182480392, 1.2051318683212102, -0.7681629484841594, -1.543920884992136, 0.24112415512267749, 1.076435400887481, -0.25925229768534086, -1.6658644560151439, 0.3943754747457504, 1.3842565496753068, -1.2669773145341812, 1.6518767655452633, -1.5853909423966077, -1.4927047534677924, -1.983702332562233, 1.138356116290356, 0.24191379767133814, 0.6420805467039874, 0.0872890383804541, 1.1829845893028037, -0.993423393494183, 0.12192601976122841, -1.7747829968363762, -1.450669978352841, -1.4320477493831785, 0.6550718310518415, -1.5686419131829887, 1.0281382568127064, -0.18298026448742366, 0.7765106299217273, -0.5730905019255661, 1.072070869494694, 1.2693109634320097, 1.7069747706981535, -1.171849742990417, 0.4038260920604313, -1.8620887801661237, -0.8280409253527203, -1.830796051704672, -1.6109290088900714, -0.7963339740967319, -1.758933515478339, 0.33271167991059825, 0.897666281747084, 1.0030732022603628, 0.7923976057376598, -1.546564906061482, -0.8521623240892628, -0.9514215862422861, -1.7350877422644877, -1.9912922002916824, -0.9226224013458637, -1.8647568246715989, -0.12855010100772946, 0.16330621420984448, -1.1808980921524945, -1.084806403591437, 0.6133243491281353, -1.9215123600125263, -0.8023356042755481, -0.0783376868449066, 0.08509485307968845, 1.835992203881502, 1.0427188603165107, 1.2137056322879394, -0.681757498211685, -0.49288357133344984, 0.7482264148143973, 0.8290114720901394, -1.5566140346193902, -0.9289890156310094, -1.8778689057312667, -0.39602412251224806], "y": [-2.502127256606556, 1.7537411596711132, -1.7930695858009567, 1.2637896127321737, 1.0933480032453258, 1.9992147413021908, -0.5114017715814466, -1.6281183204216754, 1.8243463750772828, 0.5227249731158873, -0.35632893722236203, 0.2773133179148996, -2.233257676621232, -0.3365155191131898, 0.8895364387022068, 1.7097391994952302, -1.5452770481677645, -0.14981738941872053, 5.239403630903784, -2.832433263918785, 3.2443215404886003, -3.8885431673771294, 2.775101177626189, 1.0412324208482602, 0.2981329913450337, -0.5502558560717105, 2.8751156938322877, 0.05680479483507081, -2.5737729985321924, 1.219111547830647], "w": [-0.018579911916050018, -0.005414648940187491, -0.01989101886640754, -0.006271615693174956, -0.04492253692737036, 0.04535113879074375, -0.03211065826801199, -0.042324728177420334, 0.0], "workspace": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], "order": "row-major", "LDX": 8, "penalty": "elasticnet", "loss_function": "squared-epsilon-insensitive", "learning_rate": "constant", "lambda": 0.0001, "l1_ratio": 0.15, "eta0": 0.01, "power_t": 0.25, "epsilon": 0.1, "max_iter": 5, "fit_intercept": true, "intercept": 0.0, "expected_w": [-0.8614567540526539, 0.3851158853786177, 0.7281998866671436, -0.19026098822582405, -0.5220922878531832, -0.6787293371669859, 0.5479545198714791, -0.12632121990953651], "expected_intercept": 0.049427768231497436} diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/learning-rates/lr_constant.json b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/learning-rates/lr_constant.json new file mode 100644 index 000000000000..804266349efb --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/learning-rates/lr_constant.json @@ -0,0 +1 @@ +{"M": 20, "N": 3, "X": [0.49184863344576346, -0.34814932094747153, -0.5477551911780645, -1.8535882346206338, 1.4725948382875114, 0.6913128505125967, -1.6501135437531054, 1.5478637569455782, 1.1298582030615867, -0.7307823549481589, 1.2732551495480275, 0.03022027296939278, -1.9152267929277276, -0.2659129670848732, -0.21474777618482355, -1.0447200423710807, 1.3209829273643017, 0.9790567064522131, 0.34591600384066234, -0.02852858973807182, -0.050576480635228016, -0.9330372163083185, 0.4200444057588677, 1.014174883961148, -0.9176630881870653, 0.08921313252990393, -1.6066858791457554, 0.8545466998779148, 1.5361623760639285, 0.26821768491201325, 1.977926315588928, -1.2850409189497674, -1.9511996548222879, -0.17200607777167676, 1.7270077674198032, 1.3840987528714987, -0.10668049571895954, 1.6102201041909407, -1.096017894650541, -0.7833850453736546, 0.8599755260540927, 0.896365933751508, -1.9252942423052741, -0.856747586564476, 0.32194534098155536, 1.723146528360679, -0.6440123932849473, -1.5196675077213357, 0.06509083410300853, 0.7968282532453359, -0.8054372916941004, 1.4464384611167111, 1.6232287815714956, 1.0743330073911381, -0.9550734370499776, 1.7538223847865955, 1.7545698220533867, 0.9801781884315104, 1.6429401713892413, -1.051101164821859], "y": [-0.6469006684260985, 1.583828050630603, 1.9861639666704718, 0.8310566289128972, -0.3827396059433048, 1.8269791598268526, -0.06999305823322631, 1.2651470592501224, -1.4047265180421287, 1.1783565828730356, -2.485433620743457, 2.4083030996503925, -0.10546375041122602, 1.3333946999632889, -0.15024329223572774, -1.8716730434183178, -0.24057029797606505, 1.9002328519757954, 2.737890375764529, -0.059906457215955576], "w": [0.005124203335664046, 0.006708121448377999, 0.031834497533628284, 0.0], "workspace": [0.0, 0.0, 0.0], "order": "row-major", "LDX": 3, "penalty": "l2", "loss_function": "squared-epsilon-insensitive", "learning_rate": "constant", "lambda": 0.0001, "l1_ratio": 0.15, "eta0": 0.01, "power_t": 0.25, "epsilon": 0.1, "max_iter": 5, "fit_intercept": true, "intercept": 0.0, "expected_w": [-0.13067946689918927, 0.5935643883857246, 0.7574510089030908], "expected_intercept": 0.054914894415283196} diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/learning-rates/lr_invscaling.json b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/learning-rates/lr_invscaling.json new file mode 100644 index 000000000000..03673f682b3f --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/learning-rates/lr_invscaling.json @@ -0,0 +1 @@ +{"M": 20, "N": 3, "X": [1.7559492152259675, -1.2283943405426072, 0.8513760971698203, 0.7917592169622498, -0.9668140051849408, 1.6632312782692131, 0.1294327463086602, 0.2318576776333856, -0.7089109273394034, -0.6454768696057704, -0.6707937838335223, 1.915272822370985, -1.187139217832068, 0.6681042402589932, 0.299137067767584, -1.7920988537218014, 0.1713524103606554, -1.168203540245476, -1.6356392811404619, 1.479422848035726, -1.8905208820125439, 1.874498088784982, -0.6900004417008394, -0.35888816852966476, -1.457823139815551, -1.491734757020184, -0.344064080943451, 0.46977494415677423, -1.470932388530589, 1.8979248714331152, -1.2774595902218833, 1.041399644785264, -0.0713527391823292, 1.0723154725770456, -0.7884062286809317, -1.9392996368981859, -0.14652525413218287, 0.8133483156760675, -1.7358364283613796, 0.676045670627218, -0.6180837766313587, 0.36688589041237885, -1.08378616877157, -0.9297265500889882, 1.731307942558797, 1.3045802351354268, -1.4182272286659376, 0.5880144395733393, 1.5358046613326701, 0.9654421366229844, 0.06284319737806987, -1.458992625740691, -1.8404653895786032, 1.3372873283558744, -1.5317685228209004, 0.09240925444021597, 1.4922338730262021, -1.8451686521346904, 0.7695631471748134, -0.09492608746937448], "y": [-0.9296034784994464, -1.264883906569768, 0.3244200436650097, -0.7893327336884475, -0.12901061013234016, 2.17915029486798, 1.6436479943506277, -0.2828325139708316, 2.7051784317623735, -0.908484642155605, 0.13598085671561017, 1.9355793676600648, 1.064028085967183, -0.16755055976969824, -0.18857226421413747, -0.2225346690018546, -1.9852899904518522, 1.4482374356704992, -0.4400502699718625, 0.5947804445814224], "w": [0.038104992838066884, -0.04587361188094161, -0.032513223853402065, 0.0], "workspace": [0.0, 0.0, 0.0], "order": "row-major", "LDX": 3, "penalty": "l2", "loss_function": "squared-epsilon-insensitive", "learning_rate": "invscaling", "lambda": 0.0001, "l1_ratio": 0.15, "eta0": 0.01, "power_t": 0.25, "epsilon": 0.1, "max_iter": 5, "fit_intercept": true, "intercept": 0.0, "expected_w": [-0.3851190531127391, -0.1889321969722861, -0.44084917286955105], "expected_intercept": 0.11604942770144361} diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/loss-functions/loss_epsilon_insensitive.json b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/loss-functions/loss_epsilon_insensitive.json new file mode 100644 index 000000000000..2cce7d396269 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/loss-functions/loss_epsilon_insensitive.json @@ -0,0 +1 @@ +{"M": 20, "N": 3, "X": [1.199909651827984, -1.37894397292626, -0.5560321670962898, 1.820301026598949, -1.4793665285813047, -0.9086136593918828, 1.236471472296433, 1.8626282266948766, -1.6303113438782004, 0.3336970808345061, -0.3782947746783134, 1.5972967513969478, -1.6608662177221927, -1.2754548935757462, -1.7973381052573099, -0.7948850474797209, -1.2392645827818267, -1.965578451148518, -1.4899083973821718, -1.5485411047316169, 0.9216578058769334, -1.0424998686530396, 1.606280484150901, -0.685944280986412, 1.496170891144882, -1.0538819400500206, -0.4861815773232916, 0.16916223196505564, 0.06317592910230418, -0.3447915272825641, -0.4445335483876569, -0.638676728304683, -0.39106147917359113, -0.1715604131375006, -0.46803576920007606, 0.6323589094170394, 1.5248568248273653, 0.29069033732332317, -0.2878381718541849, -0.9664882671760537, 1.4610777326900428, -1.3068867970508427, 0.31981129929701657, 0.9095099935114406, 0.9216093414030437, -0.38401673597577624, -1.5282901160587254, 1.0543785236740266, 0.1346443689265837, 0.8881394350392484, 0.390529340078833, -0.308536120048176, 1.7339443506405878, 1.975883972040969, 0.19135535576813867, 0.9209382873709981, -1.8880628597409617, 1.454365106571017, 1.9945230374404606, -0.6987607042286847], "y": [0.749610938002661, 1.0669806352092963, 1.449041914787048, -0.2027322136145916, -1.1907523090788612, -0.5130441498423087, -1.5934292222010968, -0.32632468610617604, 0.9387997549010489, 0.15777404719222937, -0.4906827914004536, -0.12125129754383662, 1.1123844943639383, -0.1891612383971119, 0.4250675111968416, -0.7882957359061169, 0.2830589883577977, 0.02018392020843425, 0.6522398987859501, 1.5662783745917837], "w": [-0.005141598124558555, -0.025561642062394443, 0.031417296433045125, 0.0], "workspace": [0.0, 0.0, 0.0], "order": "row-major", "LDX": 3, "penalty": "l2", "loss_function": "epsilon-insensitive", "learning_rate": "constant", "lambda": 0.0001, "l1_ratio": 0.15, "eta0": 0.01, "power_t": 0.25, "epsilon": 0.1, "max_iter": 5, "fit_intercept": true, "intercept": 0.0, "expected_w": [0.6791123374993187, 0.23541521043475572, -0.11354917748875704], "expected_intercept": 0.03} diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/loss-functions/loss_hinge.json b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/loss-functions/loss_hinge.json new file mode 100644 index 000000000000..e9131c8a90aa --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/loss-functions/loss_hinge.json @@ -0,0 +1 @@ +{"M": 20, "N": 3, "X": [0.7251850780964713, -0.4348667349991602, -1.895182104448605, 0.002398785384576385, 0.11250843628454676, -1.106927833044534, -1.9932412721725878, -0.21374607166859638, 0.021303253327713545, -1.6759246144269841, 1.2909596238500813, -1.0392202778771287, -1.9612388933381713, -1.0998330617252243, -1.6632394519138733, 1.3038583957043048, 0.8590688744396169, -1.0217754488187571, -1.7446526427804505, -0.29298321923164705, 0.8241660788828442, 0.8726143140303257, -1.2262626600145023, 0.9601924041297143, 1.8269194952820405, 0.06357590853621886, -0.2798190845214794, -1.907852979706964, 0.029303859915172925, 0.7462489109559316, -1.447298952162853, 0.6896914489890218, -1.842754154896078, -0.2728939149110836, 1.4171033864036957, -0.3106860657102244, -1.6462035573322895, -1.7732199694559712, -1.5869086177674427, -0.05873721045373825, 0.698628687555658, -1.636420625263392, 1.3288492257950342, 1.1058541236603894, -1.7553465666793362, -0.7103285530906707, -0.49144120618837306, 0.009060023641976223, -0.2001385792629069, -0.13055197818459385, -1.5811423592043021, -0.5049967592801399, -1.9129109521669587, -0.20664892739540974, 1.5724592327526437, 1.1035248954238317, -1.0033547384148207, 0.926178045586854, -0.7394480989901071, -0.015478157111626256], "y": [1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0], "w": [-0.021385740696746414, 0.015864623261680866, -0.040969959085125254, 0.0], "workspace": [0.0, 0.0, 0.0], "order": "row-major", "LDX": 3, "penalty": "l2", "loss_function": "hinge", "learning_rate": "constant", "lambda": 0.0001, "l1_ratio": 0.15, "eta0": 0.01, "power_t": 0.25, "epsilon": 0.1, "max_iter": 5, "fit_intercept": true, "intercept": 0.0, "expected_w": [-0.7488988843283844, -0.0947550639466107, -0.27256202958380776], "expected_intercept": 0.21000000000000005} diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/loss-functions/loss_huber.json b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/loss-functions/loss_huber.json new file mode 100644 index 000000000000..6a1c07b58976 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/loss-functions/loss_huber.json @@ -0,0 +1 @@ +{"M": 20, "N": 3, "X": [0.0734202218991471, -0.09101849860015321, -0.39557436535790025, 1.7224576066603379, 0.06134458779906771, 1.23832807438819, 1.5270889111004813, 1.0506711282824686, -0.14231719127253983, -1.704321581358256, -0.11800256850404356, -1.1977120901693028, -1.165731105617183, -1.2898415992035326, -0.5830342055148456, 1.6470125048668818, -0.2659163238268789, -1.286816454721456, 1.0086489963024912, -1.7981177371888992, -1.6693259304581405, -1.8456662338204755, -0.13888557406837032, 0.4153146539831454, -1.272842627178175, 0.07611880248459935, 0.8406131034542623, 1.6999215137633952, 0.11652657955290557, 1.884742929298596, 0.18901104406482938, -1.9742480859324458, -0.10889300588421014, 0.08835231888271755, -1.3911643676466707, 0.6169363364555531, -1.4782254793323797, -0.18340450516112483, 1.9294458036999829, -1.469943623389541, -1.758884315013728, -1.7285108868714456, -1.6154158118282202, 1.277369499650077, 0.5184260168906558, 1.134524020564299, -1.6914583889513564, 1.8879932430627262, 1.5313342519665665, 0.5217325165983322, 1.716292354177872, -0.4074935773989483, -0.044295613462754346, 0.743212937162042, -0.8736136805761103, 0.7067998169792609, -0.01707314843453034, 1.164567926691066, -1.1246658761953383, 0.9847833462853184], "y": [-0.2456169866406885, 0.6557488459762985, -0.4688200088457306, -0.7951290132629524, -0.1283061153288265, -1.2987719247749145, -1.366740936664777, 0.5237426800942675, 0.8408477963061164, 1.465629026815471, 0.13945727964900279, 0.9306862590635027, 1.5947694168212077, -1.0116978600591013, 0.26938706171898136, 1.742408766115053, 1.2830204432128367, 0.5988547776643325, -0.06369541747909852, 1.000236126591862], "w": [0.016208359650160292, -0.015259577556246996, -0.037143000425776285, 0.0], "workspace": [0.0, 0.0, 0.0], "order": "row-major", "LDX": 3, "penalty": "l2", "loss_function": "huber", "learning_rate": "constant", "lambda": 0.0001, "l1_ratio": 0.15, "eta0": 0.01, "power_t": 0.25, "epsilon": 0.1, "max_iter": 5, "fit_intercept": true, "intercept": 0.0, "expected_w": [0.023310624892916108, -0.01801363663622113, 0.061255512132994794], "expected_intercept": 0.022499551983441297} diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/loss-functions/loss_log.json b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/loss-functions/loss_log.json new file mode 100644 index 000000000000..0a8ab74fe086 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/loss-functions/loss_log.json @@ -0,0 +1 @@ +{"M": 20, "N": 3, "X": [-0.400007232993302, 1.3083908985621004, -0.17843452001748794, 0.7743328238868705, 0.5519213824976434, 0.6235321089670851, 1.0772759788879154, -1.9990910003433129, -0.7972182206399427, 0.7818859536092106, -1.6364503129076313, 0.09459009607780056, 1.5783801145226444, 1.8548234690021017, 0.8227527498689517, 1.0561155480006605, -0.6215685660926722, -0.9925986870784791, 0.668836461511471, -1.4027799396608347, -0.11633323088909098, -1.910832249696596, 1.038844223719198, -1.1773716803736303, 1.3623618965039435, 1.0994173294229173, -1.2953438193329623, 1.538235162376512, -1.62774418388077, -1.1912462182799723, -0.5103780685701529, -1.3053771569074661, -0.6190650291008013, 0.2709539246558572, -1.372192024289176, 1.1029617227006017, -1.865353689072597, -1.95616594389071, -0.7195065779869312, 0.10690841860846145, 0.5189062606613981, 1.526055332471107, 0.5645566914852158, -1.9092483379402063, 0.7807611913926054, 1.538607192275101, 1.0055764198203478, -0.9476881155643966, 0.030297960785700795, -1.5032359448584196, 0.43490277027863833, 0.7732512148821278, -1.688855073010691, -0.38367401024863046, 0.05394755763525261, 1.22826516163967, -1.604134757912683, -0.10987308742979485, 0.26936739220369343, 0.14393477340970495], "y": [1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0], "w": [-0.014970438241412413, -0.029761000195529266, -0.03400558876544127, 0.0], "workspace": [0.0, 0.0, 0.0], "order": "row-major", "LDX": 3, "penalty": "l2", "loss_function": "log", "learning_rate": "constant", "lambda": 0.0001, "l1_ratio": 0.15, "eta0": 0.01, "power_t": 0.25, "epsilon": 0.1, "max_iter": 5, "fit_intercept": true, "intercept": 0.0, "expected_w": [-0.33842846802522253, 0.12477761204380569, 0.10113162761249513], "expected_intercept": -0.1553302347328218} diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/loss-functions/loss_modified_huber.json b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/loss-functions/loss_modified_huber.json new file mode 100644 index 000000000000..7bb196167d08 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/loss-functions/loss_modified_huber.json @@ -0,0 +1 @@ +{"M": 20, "N": 3, "X": [0.20930924077637947, -1.257079594390068, 1.9239054834027463, -1.6162534614599773, 0.9930515227605645, 0.0984736408745297, -0.8369611003066266, -1.0872369898312617, 0.01495664365612015, 1.7278286475329412, 0.29651911099460415, 1.958978463316039, -0.2809078999199688, -0.3345770071510765, 0.5115092130991759, 1.6294733148232408, -1.7223090447214808, 0.2016125720855153, -1.3815766594614751, 0.8742432013513493, -1.4583723920931626, -1.981814134395067, 0.15006470423607565, -1.9336287924628874, 0.14205814201157496, -1.8172317268992395, -1.384082536769911, 1.37204178889714, 1.1917176561924983, -0.7856227444671515, -0.40602645100897394, -0.20282358282124813, -1.9178592922749411, 0.47021646024926644, 1.218219485579037, -1.0633119542160094, -0.399486440456708, -0.026743355999237384, 0.007524475633007732, -0.6660471632735132, -1.5783188834831399, 0.5632313505422779, -1.3941235044004632, -1.2192933643882031, -1.770434181045085, 1.6589781890819872, 1.3226803648555299, -1.3639828888422172, 1.4708267019604437, 0.029192816571451452, -1.4867945171606545, -1.5475289984016771, -1.1088088779729253, 1.5593479001085129, -0.19631165400359363, 0.7888386844478363, -0.4388393853537722, 0.4904950415155427, 0.8657924447231933, 0.003436956893867915], "y": [-1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0], "w": [0.006564677152321861, -0.04714419644559209, -0.03596693813754142, 0.0], "workspace": [0.0, 0.0, 0.0], "order": "row-major", "LDX": 3, "penalty": "l2", "loss_function": "modified-huber", "learning_rate": "constant", "lambda": 0.0001, "l1_ratio": 0.15, "eta0": 0.01, "power_t": 0.25, "epsilon": 0.1, "max_iter": 5, "fit_intercept": true, "intercept": 0.0, "expected_w": [0.5341745141973712, 0.5289308896759088, -0.031437735174862244], "expected_intercept": -0.2076235275562692} diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/loss-functions/loss_perceptron.json b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/loss-functions/loss_perceptron.json new file mode 100644 index 000000000000..dda88b592cf0 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/loss-functions/loss_perceptron.json @@ -0,0 +1 @@ +{"M": 20, "N": 3, "X": [-1.784791096896201, 1.4511846722306188, 0.5198815330359134, 1.4786126068731789, -1.6492365005665053, 1.4636152974938628, -1.8350097582036944, 1.8326963629380892, -1.0905276911912605, 1.0074484076643166, -0.39700532191235993, -0.387698302333086, -0.98685212504695, 0.027353798831553977, -1.2617593669900171, -1.7716638948450192, -0.5322938483788713, 0.879349146603877, -0.10330185743288745, 1.6904811103857962, -1.3195107952735943, 0.8735271184967148, 1.4791905561759098, -1.4827609768911016, 1.8962232443372167, 1.0168624855657478, 1.6667098230500477, -1.8491886354076508, -0.8566046118000132, 1.2916315118613868, -0.9010654803355109, -1.7931042078105528, -1.408226874971675, -1.9340293231952503, 1.1296664913335857, -0.6035135700480567, 0.9987113824062832, -1.7801864311312388, -1.6872071053594553, 1.157362099722167, -0.5182477887163492, -1.5561502306701667, -0.3791359049765495, 0.6051951239567321, -0.25484717116859423, -1.1900993964161102, -0.2244416780889149, 0.9685923893992938, -1.6159396310056056, 0.04688505550936739, 1.3744657788806487, 1.9741746082820901, 0.523024709711871, -0.8581066808354083, -1.49591296870725, -1.94657718741546, -0.6473775157892385, -0.06271930725701225, 0.18476418683828877, -1.8558425036712785], "y": [1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0], "w": [0.011054014681613177, 0.015169836591697461, 0.015446516189045967, 0.0], "workspace": [0.0, 0.0, 0.0], "order": "row-major", "LDX": 3, "penalty": "l2", "loss_function": "perceptron", "learning_rate": "constant", "lambda": 0.0001, "l1_ratio": 0.15, "eta0": 0.01, "power_t": 0.25, "epsilon": 0.1, "max_iter": 5, "fit_intercept": true, "intercept": 0.0, "expected_w": [-0.021390167869277752, -0.026227768616922737, 0.01787985269715197], "expected_intercept": 0.01} diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/loss-functions/loss_squared_epsilon_insensitive.json b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/loss-functions/loss_squared_epsilon_insensitive.json new file mode 100644 index 000000000000..d925f12929d7 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/loss-functions/loss_squared_epsilon_insensitive.json @@ -0,0 +1 @@ +{"M": 20, "N": 3, "X": [0.7885975943362533, -0.3615818358251217, -1.9771396629393712, 1.8160574562373295, 0.42101039848294075, -1.9738148936047835, -0.0060260518041546085, 0.3945413840750982, -0.7994101994938219, -0.15297001532503884, -0.01760087537506516, 0.34263656371901163, 1.4785519538274818, 1.3289078367201133, 1.1142521920184327, 0.19373243074836655, 0.4538078260237386, 0.937078908525252, 0.2654763656160064, 1.1008405388652047, 1.9461824230541218, 1.156822758782904, 0.4573439266653798, -1.3668618429874955, -0.09445474314594016, -0.8654033062157427, -1.384750938991433, 1.1088816781166857, -1.7641474664238799, -1.6835099939397296, 0.6569538507576698, 0.09337992420402585, -0.21437244168100866, -0.7726782118590951, -1.9308595144678509, -0.8258926343987372, -0.9288241375511399, -1.8530975933291365, 1.2161583117000623, -0.6648017708216187, 1.5675467382701198, 1.2414129340843973, 1.856404830172147, 1.8308562553420074, 0.364662019712072, -0.7539867225196994, 1.673682921120243, -1.797398175074595, -1.0970292744265278, -0.5491756053038204, -0.35097719375805214, 1.778485182753363, 0.317530180105178, -1.2529593954944747, 0.38593866117567943, 1.735025188745014, -1.4634474385606921, 1.9291341211467254, -1.3844973871078725, 1.2829125498336316], "y": [1.522269486599184, 1.7233769639898104, 0.12944281855697026, -0.16426826424544821, -0.394629820347218, -0.4165319997403697, -1.3445012638485825, 0.9950858460498876, 1.0125504584165494, 1.6969627976008863, 0.2710842922062427, 0.7334737527592142, -0.7003701789362734, -1.186033047467742, 0.05944648338752013, 0.4332262262706267, -0.23715621680951188, 1.1475772865521685, 0.5070616439204056, 0.08936350490635409], "w": [0.049583823804866894, 0.04442045718534875, -0.036885588816160365, 0.0], "workspace": [0.0, 0.0, 0.0], "order": "row-major", "LDX": 3, "penalty": "l2", "loss_function": "squared-epsilon-insensitive", "learning_rate": "constant", "lambda": 0.0001, "l1_ratio": 0.15, "eta0": 0.01, "power_t": 0.25, "epsilon": 0.1, "max_iter": 5, "fit_intercept": true, "intercept": 0.0, "expected_w": [0.2684456154143304, -0.15937811137646452, -0.53171747782223], "expected_intercept": 0.052400477801831546} diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/loss-functions/loss_squared_error.json b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/loss-functions/loss_squared_error.json new file mode 100644 index 000000000000..9fe3cc426b67 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/loss-functions/loss_squared_error.json @@ -0,0 +1 @@ +{"M": 20, "N": 3, "X": [0.04943806854490873, -0.9480780929029344, -1.6044131634325711, 1.1953854271372943, -1.4287888945368952, 1.22266314233821, 1.5181044037318134, -0.0003193432162835741, 1.6570077634717233, -0.3503496341715211, -1.1985056508583987, 0.5742844343053419, -0.40053942757859007, 0.4569259917573443, -0.25075441319587277, 0.3724149866232609, 1.8057600430421696, 0.21248173566721773, 1.6149414288965946, -1.44598098051274, -1.9938249497152953, 0.4513373820941484, 0.16681172093328156, 1.1225226341046848, 1.2859758148085443, 1.8136390245531593, 0.43982436662883817, -0.8396727124941301, 1.3072319756711805, -0.49593242509683844, 0.4367081037151781, 0.4800862268836714, -0.23400012035842632, -1.2049124187491267, -0.2241855163458264, 1.6990693772877248, 0.6857374388257544, -1.6739076134844826, 0.2906369958356443, -1.517351208975994, 1.9998548951145576, 1.9895213890012435, 0.7788485709476332, -0.5044806236072912, -0.3939568441333825, 1.7003976520193884, -1.0205646329090605, 0.6560507215752711, -0.07511699571343478, -1.860225764331251, -1.556418152258496, 1.5814775198684674, 1.568404552068999, 1.6606439512364863, -0.77039331617451, -0.8063214806518224, 0.6370074357056192, 0.38055089043176116, 1.937476046607788, 0.4624890276933491], "y": [-1.8073164017936103, -0.8831961745108083, -0.15891176301131443, -0.16288414114277416, 0.40524208501269776, 1.0924306337468972, -3.200611561493632, 0.44182884926954635, 0.683601519256572, 0.9804389842315738, -0.08895573211328513, 1.7245112749098028, -1.4391218594014126, 3.8454849261944704, -1.0587447863101933, -1.486179319730095, -2.171177263442877, 0.9864679694605235, 0.4515652289689359, 1.171685156995844], "w": [0.012846754097598667, 0.048265104161919356, -0.03236833342087293, 0.0], "workspace": [0.0, 0.0, 0.0], "order": "row-major", "LDX": 3, "penalty": "l2", "loss_function": "squared-error", "learning_rate": "constant", "lambda": 0.0001, "l1_ratio": 0.15, "eta0": 0.01, "power_t": 0.25, "epsilon": 0.1, "max_iter": 5, "fit_intercept": true, "intercept": 0.0, "expected_w": [-0.4664114330704009, 0.6501098229392946, 0.5112476080067482], "expected_intercept": -0.0314852075691766} diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/loss-functions/loss_squared_hinge.json b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/loss-functions/loss_squared_hinge.json new file mode 100644 index 000000000000..ec09eb65b22a --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/loss-functions/loss_squared_hinge.json @@ -0,0 +1 @@ +{"M": 20, "N": 3, "X": [1.2890612359005744, 1.2296571530798683, -1.2335333031297497, 0.5532120811967887, 1.3298584626721843, -0.12069163101502589, 0.32141213306276617, -1.5643888803735293, -0.15032204015024941, 1.5454073315601726, -0.14006890024093277, -0.13513891408304568, -1.2159813252458105, -1.8450844454221014, 0.4634470245535294, 0.2585251219165383, -1.567076725647452, 0.9665327517570397, -1.266071895458436, -0.6054356321755407, -0.07807402694599119, 0.5757057289250311, 0.48650570181263353, 1.0447720366865036, -0.8533957784416222, -0.030459178858456948, 0.7276407540499337, 0.03987942496074659, 1.0728454576595046, -0.8002542339693064, 0.18752301005263794, -1.021420274099691, -0.22561083473236465, 1.2400578514347544, -1.5845323502487605, -1.080061044519124, 1.167057630957205, 0.26616531409951216, 1.3096689513775894, 1.8997349790269968, 1.8608978997673593, 1.7988104009301242, 0.20714766563406606, -1.6298554863871626, 0.9617230918343997, 0.2571897792687108, 0.9834309140861408, -0.178440454615433, -1.3773480003977916, 1.9838113639526322, 1.558764345100104, 1.2149975966954583, -0.3540971556950865, -1.1473725765252176, -0.8222408630641689, 0.4917253586410606, -1.007131794968056, 1.1987237679949243, -1.9244710728552215, -0.07859443917075026], "y": [1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0], "w": [0.006733437857021726, 0.019964846361620908, -0.040806452559370626, 0.0], "workspace": [0.0, 0.0, 0.0], "order": "row-major", "LDX": 3, "penalty": "l2", "loss_function": "squared-hinge", "learning_rate": "constant", "lambda": 0.0001, "l1_ratio": 0.15, "eta0": 0.01, "power_t": 0.25, "epsilon": 0.1, "max_iter": 5, "fit_intercept": true, "intercept": 0.0, "expected_w": [0.6244501112949565, 0.456344158484591, -0.21897463983698223], "expected_intercept": -0.0810075858246219} diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/many_epochs.json b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/many_epochs.json new file mode 100644 index 000000000000..53d7d553effc --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/many_epochs.json @@ -0,0 +1 @@ +{"M": 20, "N": 3, "X": [1.7471477514041598, 1.3432994416386257, 1.8904744355118996, 1.2926993494848453, -1.8675855244701869, 0.27798827434883266, -1.4099397281011936, 0.32021613444193653, -1.666037581120027, -0.17885089773270213, 1.4366284347836626, -0.6976942720838455, -0.649620013238752, 1.6725460783968917, 0.5488126402431388, -1.3361973743238766, 1.4964926620003598, -1.6177325322420155, 0.36049068799231376, 0.530998784426624, -0.6693805946438705, -1.3552794891872613, 0.5630982528849886, -0.44904017672454577, 1.2661114254180412, -1.0827211504865497, 0.6604656774781046, 0.30242979222728517, -1.682511990911419, -1.830711938007695, 1.4280899882519735, -1.4098314519292345, -1.58768917598058, -1.574604990792789, -0.8269937724626386, 0.12046730035457909, 1.465302083238932, 1.4751349859050733, -0.5739867920407296, -1.6074901549600566, -0.24709085522231922, 1.7899449319618408, -1.8336199208794333, -0.7566097717276365, 1.1291476482534741, 1.1820405927148836, 1.4103225964388408, 0.17556424816204563, 0.36251428150489806, -1.4435681099064088, -1.861105350405555, -1.3789616957236013, 1.0276778945318332, -0.5248702946152539, -0.8687505464855283, -1.032805117134529, 1.4854076710795248, -0.5316224590257081, 1.55214311666063, 1.7995146370467552], "y": [1.674620526437552, 1.47477632201614, -1.6805671477377357, -0.5291076705569755, -0.5884674785166051, -2.006737079222284, -0.1436525317329071, -1.2541325708486128, 1.3193776259279213, 0.2600101567816365, 0.7862257391287131, -0.9999022309104999, 0.4994500380323206, -0.6059963981912443, -0.7515442104453247, 0.4440020355982339, 0.03894998580099387, -1.2476889296283846, 0.0848625353565381, -0.2317657945488346], "w": [0.04301825245105392, 0.020205782452563686, 0.030062544866097385, 0.0], "workspace": [0.0, 0.0, 0.0], "order": "row-major", "LDX": 3, "penalty": "l2", "loss_function": "squared-epsilon-insensitive", "learning_rate": "constant", "lambda": 0.0001, "l1_ratio": 0.15, "eta0": 0.01, "power_t": 0.25, "epsilon": 0.1, "max_iter": 25, "fit_intercept": true, "intercept": 0.0, "expected_w": [0.7259824725227432, -0.25340054286191704, 0.31558534637128116], "expected_intercept": 0.024531501392368423} diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/no_intercept.json b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/no_intercept.json new file mode 100644 index 000000000000..67c093867606 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/no_intercept.json @@ -0,0 +1 @@ +{"M": 20, "N": 3, "X": [-0.31961034040006675, -0.6375736827892631, -1.8209243367794872, -1.5484347443656499, -0.24783404854009028, -1.7157071839055513, 1.2649804306288175, 1.4644625931707385, -1.4755206323244727, -1.2271222013046983, -0.2500289929046797, 1.883701298707937, 1.0954438571104976, -1.47289411316231, 1.7087194547252902, 1.3111863189828354, -0.4290733385586947, -1.437717415499935, -1.7914546432285912, -1.6290827383247786, 1.5479585214540585, -1.327396131135877, -0.5484535523384086, 0.6171275430236318, 0.4192878134399822, -0.42002851101573135, -0.50157839923338, -1.7477089129261119, -0.28416388867135245, 0.8788837042156672, 1.0721235601519137, -1.2225274172742036, 1.4514830763683966, -1.8851121868811447, 1.6085152666498863, -1.0538164803065468, -1.4856629570761468, -0.7284968478825591, 1.3950451820655236, 1.3291909868449068, -0.6173945900722413, -1.040115246650318, 0.14315021395012106, -0.9141322567155492, -1.8622765022224934, 0.508342360885933, 0.08080184378274557, -1.832176743598307, 1.029891075836595, 1.9533990518220077, 1.205603832891259, -0.6240756546960364, -0.6584350299655597, -0.5937143880089435, -0.2282172826083011, 0.3179737408783514, 0.4702436369433798, -1.2915300481313587, 1.4599271541886814, 0.8410827624573156], "y": [1.4034410088861806, 2.534765439407114, 1.543029737409197, -1.3022842744138745, -3.410157847683268, 0.19860022194453505, -1.4013145234425388, -0.0028830412259241156, 0.13005974324725922, 0.26174232065233555, -2.9655720323953028, 3.383977157189304, -0.7609987936816015, -0.2846864872229031, 0.9819028418136555, 1.5897259016367142, -0.41209628150879163, 0.502212687920912, -0.14389759452009862, 1.0143193919895745], "w": [-0.03460848348445229, -0.0004104154820087427, -0.023870521982604722, 0.0], "workspace": [0.0, 0.0, 0.0], "order": "row-major", "LDX": 3, "penalty": "l2", "loss_function": "squared-epsilon-insensitive", "learning_rate": "constant", "lambda": 0.0001, "l1_ratio": 0.15, "eta0": 0.01, "power_t": 0.25, "epsilon": 0.1, "max_iter": 5, "fit_intercept": false, "intercept": 0.0, "expected_w": [-0.581049115325038, 0.5912489778564128, -0.9128343325696479], "expected_intercept": 0.0} diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/penalties/penalty_elasticnet.json b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/penalties/penalty_elasticnet.json new file mode 100644 index 000000000000..491ecb84de8d --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/penalties/penalty_elasticnet.json @@ -0,0 +1 @@ +{"M": 20, "N": 3, "X": [0.7693805736638444, -0.11059624371926091, 1.4274917677380032, 0.6589733356172243, -0.6664962308987019, 0.07258199702525303, -0.4212456662193098, -1.8758016065745546, 0.30735937253248347, 1.4010069813954433, 1.8059554606945802, 0.7319151884330668, -1.6953498072158881, -0.35968822891005203, -1.8323944049130532, -1.3032526736984833, 0.26160802225901403, -1.3084353176005585, 0.26644191177778476, 0.05601701179816443, 1.4754835501895074, 0.8813889917238114, 1.6143653882811932, -0.8895400070665116, -0.9733382712740482, 0.7988130514456429, 1.6062755849957577, 1.9347693004863125, 0.5636512497049799, -0.6799704458591562, 0.4267008649166404, 1.2886391543035622, 0.5118602618446464, -1.5283077699971588, -0.8564893065953747, 1.9469888613514583, -0.2727966060363025, 0.29693765513225756, 0.1089683066969207, -1.2093878024899491, -1.3371662711006636, 0.00396183168879416, 1.960816240341257, -0.4240327547654128, 0.8560530611064596, -1.296382922306909, -1.7172152849982294, -1.3695076547500107, -1.3537679459646563, -0.865451670951443, 0.14682410225626397, 1.092954859572842, 1.5213743251088174, 0.549565787829621, 1.4104089610182124, 0.39182148609770806, 0.7753866412082515, 0.1487066925864049, 1.556920333862784, -1.7909342949727214], "y": [-0.9987636598914722, 0.8247285413003103, 0.9148901941212165, -1.158247775236564, 0.9705507963532617, 0.23993786140586557, -1.1650553801867936, 0.17462069460080237, -2.6817941136706773, 1.3057441076912282, -1.1562184567568425, -1.9331796054681905, -0.41899625150687864, 0.19244106072326808, 0.6592782782754152, 1.7381927383862659, -0.32118218057278275, -0.9501921252564359, -0.12103577822443397, 0.569740440386762], "w": [0.046759148096693515, 0.04300288092605399, -0.03153201680039677, 0.0], "workspace": [0.0, 0.0, 0.0], "order": "row-major", "LDX": 3, "penalty": "elasticnet", "loss_function": "squared-epsilon-insensitive", "learning_rate": "constant", "lambda": 0.0001, "l1_ratio": 0.15, "eta0": 0.01, "power_t": 0.25, "epsilon": 0.1, "max_iter": 5, "fit_intercept": true, "intercept": 0.0, "expected_w": [0.32064875418567257, -0.453539405839973, -0.7282131843967453], "expected_intercept": -0.046634523953870397} diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/penalties/penalty_l1.json b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/penalties/penalty_l1.json new file mode 100644 index 000000000000..80a6e8de13d4 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/penalties/penalty_l1.json @@ -0,0 +1 @@ +{"M": 20, "N": 3, "X": [-0.5375236505312575, 1.2064103940110407, 1.130942367901505, 0.805421516946772, 0.4911063464127965, -0.02526941701155172, 1.3621508004739549, 0.8483879477745893, -0.22436407433659022, -1.8758605554666694, -0.5470409592256078, 0.9228871656413551, -0.09773370766543366, -0.6223321195119893, 0.5635217399840751, -1.4951787135424022, -1.314138956143001, 0.9483459747540866, -1.4918824257103904, -0.5214005002134927, 0.4173360193441056, -1.587582244577411, 1.2094967293488406, 1.7822129433684974, 1.9161552797357504, 1.5249289853557082, 0.5107276858559007, 1.7219461336106625, 0.8991598124097555, 0.8667115442214248, -1.8356857336632646, -0.24207289933010046, -0.8717208675356489, -0.6600161244322909, -1.6658919718223988, 1.0433965882295442, 0.03708978865581569, 0.6441896704338088, 0.5212577699371259, -0.5162926901698901, -0.21303939307662123, -0.33956711920962723, -0.0784459679463736, 1.9332942935538648, -0.5063051705858506, -1.9503720730891456, 1.6876133511961156, 1.4931042813768318, -0.593012713649645, 0.5205324500091684, -0.5686921626332357, -1.1487202494061401, -1.1067231075093913, -0.3217448330766892, -1.708359115687733, 0.6033562805467692, 1.0227445958037498, 1.728404905247249, -0.49438336167200836, -0.8122908662018662], "y": [1.7469232414307059, 0.11747490038914946, -0.003499087408005048, 0.848226470371408, 0.0454227465762615, 0.28854353012124667, 0.33107565852793136, 2.7099971198001356, 0.9561250354151642, 0.9977942235129255, -0.38047547565331, -0.13786965518954192, 0.7259933380729536, -0.22720568497289156, 0.9285355387637286, 2.9293498211949336, 0.24355091400635798, -0.6004085386845477, 1.544720047112647, -1.3403597888470205], "w": [0.028792778797953972, 0.03564774570595708, 0.022779601559861917, 0.0], "workspace": [0.0, 0.0, 0.0], "order": "row-major", "LDX": 3, "penalty": "l1", "loss_function": "squared-epsilon-insensitive", "learning_rate": "constant", "lambda": 0.0001, "l1_ratio": 0.15, "eta0": 0.01, "power_t": 0.25, "epsilon": 0.1, "max_iter": 5, "fit_intercept": true, "intercept": 0.0, "expected_w": [-0.2682075068709644, 0.5856149345677873, 0.5750193229246117], "expected_intercept": 0.2083091117628858} diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/penalties/penalty_l2.json b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/penalties/penalty_l2.json new file mode 100644 index 000000000000..8d12b744c877 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/penalties/penalty_l2.json @@ -0,0 +1 @@ +{"M": 20, "N": 3, "X": [0.5349030758039164, -0.2467604755102899, -1.3897089013019785, 0.27363846098876055, 0.11289711034024208, 1.8057150550143728, -0.07856328595993567, 0.010238253530201469, 0.147512771697639, 1.2768082682566333, -1.7715374476445604, 0.6776869722981953, 1.06846651351789, 0.8324614479104153, 1.1874687349007864, 0.2310433137097978, 1.8633461279685104, -1.4113724004280113, -1.8814119978583377, 0.37557397049908703, -1.54373720502935, 1.8032394003364889, -0.6971703422986111, -1.2255252393848912, -0.16875340441028985, 1.681610284372351, 1.516276646058703, -0.9895369798138791, -0.6079648285226154, -1.269645073678765, 1.6071842054839682, 0.8261126526871898, 0.906633846248563, 1.6003513472388309, 1.116655203077297, 0.39661912241716957, -0.8354990204085007, -1.3944189423702715, -0.6593013634022604, 0.6302071086312777, -1.7066298254695274, -1.7799744183751054, -0.7072207443152942, 0.3619272178519446, 1.4155942685025669, -0.851750299999964, -1.3077310927408314, -1.4639151760046287, 1.978615314577178, -1.2820085221224327, -0.7298127078912167, 0.27316561863642885, -1.962605701999022, 1.6025944846203677, 1.9089657236903483, 0.22757871654733952, -1.6609046264232394, -0.6679901370835997, 0.913714705478688, -1.4302585066327311], "y": [-1.2698992337837034, 1.7729399736158222, 0.1508151869665399, 1.5475902109796758, 0.9306260705692969, -2.085915354564297, -1.8081955641820648, -0.5927850979957924, 0.5213056119647277, -1.0014412695753345, 0.6745782822521256, 0.04645905805426469, -0.061794280735183935, -0.7169653181183582, 1.2019716284178328, -0.8971425840035512, 0.10429970555242818, 2.4775136093226, -1.6228917857078133, -1.950957483398009], "w": [0.0371423255048698, -0.028436593701160418, 0.023494518856219643, 0.0], "workspace": [0.0, 0.0, 0.0], "order": "row-major", "LDX": 3, "penalty": "l2", "loss_function": "squared-epsilon-insensitive", "learning_rate": "constant", "lambda": 0.0001, "l1_ratio": 0.15, "eta0": 0.01, "power_t": 0.25, "epsilon": 0.1, "max_iter": 5, "fit_intercept": true, "intercept": 0.0, "expected_w": [0.11180063226335232, -0.38460679411498216, 0.8754338312152041], "expected_intercept": -0.026089120589532073} diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/penalties/penalty_none.json b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/penalties/penalty_none.json new file mode 100644 index 000000000000..46d41e4e73b7 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/penalties/penalty_none.json @@ -0,0 +1 @@ +{"M": 20, "N": 3, "X": [-1.2339221984844309, 0.48843508415932746, -0.24908904397154208, 1.1414343348550768, 1.119903232475214, -0.9096295788694335, -0.8941429794276132, 1.207488710140077, 1.8325574147348207, 1.5037305389683788, -0.5687309201685333, 0.003980502093834826, 0.7338517406885452, 0.8508081079316008, -0.5189969808384203, 0.24478474426249974, 0.012332661231238884, -1.944926201637271, 1.0913064864494961, 1.5305647625444663, -0.5404560643945109, 0.4615847137339748, -1.6984750334280938, -0.524703975992102, 1.7325604079300865, 0.6055125729063096, -0.4111896890953832, 1.154920571762982, -0.732655511324515, 0.2723946105042767, 1.4765095582449033, -0.2553063044172825, 1.2085905683206364, -1.4249327019417417, 0.8170438844733416, 0.8183252327582902, -1.1248315773036457, 1.6994705144622602, -0.23143697838329347, 1.63726383588989, -1.7607631088805924, -1.2628516647447454, -1.8105788847939395, 0.6995237743293208, 0.3784991197377954, 0.13324065199500223, -1.826703749220786, 0.24573232025359149, -0.6813262175163399, 0.011867332450473445, -1.5524227297023847, 0.42877482487393825, 0.2637785722021255, -1.9729437520399888, 0.4697668352171882, 1.6484915457326172, 1.1620965322281336, 1.9683258647534458, 1.835207048611466, 1.1678565411665591], "y": [0.615780567117064, -0.2924716955927201, 0.5983274388324427, -0.8535063034045541, -0.04511975114820965, -0.07232418417258707, -0.14311511736782573, -0.35195515736204436, -0.5993867245972314, -0.7266283889200774, -0.7692109548203575, 0.8682866188541212, 0.8980239768652085, -1.0863320077453502, 0.8794665202411985, -0.7023934913747021, 0.43689498777280067, -0.012164474803745749, 0.30486684746051645, -0.3491324359939706], "w": [-0.03501662840100728, 0.02460634091367167, 0.03310069924335378, 0.0], "workspace": [0.0, 0.0, 0.0], "order": "row-major", "LDX": 3, "penalty": "none", "loss_function": "squared-epsilon-insensitive", "learning_rate": "constant", "lambda": 0.0, "l1_ratio": 0.15, "eta0": 0.01, "power_t": 0.25, "epsilon": 0.1, "max_iter": 5, "fit_intercept": true, "intercept": 0.0, "expected_w": [-0.3540673727315888, 0.22097333229954338, 0.011242062522575555], "expected_intercept": -0.018616232538650853} diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/runner.py b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/runner.py new file mode 100644 index 000000000000..970f7b74dc36 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/fixtures/python/runner.py @@ -0,0 +1,241 @@ +#!/usr/bin/env python +# +# @license Apache-2.0 +# +# Copyright (c) 2026 The Stdlib Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Generate fixtures.""" + +import os +import json +import numpy as np +from sklearn.linear_model import SGDRegressor, SGDClassifier + +# For reproducibility: +np.random.seed(1234) + +# Get the file path: +FILE = os.path.realpath(__file__) + +# Extract the directory in which this file resides: +DIR = os.path.dirname(FILE) + +# Map scikit-learn penalty arguments to stdlib penalty names: +penalties = { + None: "none", + "l2": "l2", + "l1": "l1", + "elasticnet": "elasticnet" +} + +# Map scikit-learn loss arguments to stdlib loss function names: +losses = { + "squared_error": "squared-error", + "huber": "huber", + "epsilon_insensitive": "epsilon-insensitive", + "squared_epsilon_insensitive": "squared-epsilon-insensitive", + "hinge": "hinge", + "squared_hinge": "squared-hinge", + "perceptron": "perceptron", + "modified_huber": "modified-huber", + "log_loss": "log" +} + +# Loss functions which require binary targets: +classification = ( + "hinge", + "squared_hinge", + "perceptron", + "modified_huber", + "log_loss" +) + + +def gen( + name: str, + loss: str = "squared_epsilon_insensitive", + penalty: str | None = "l2", + alpha: float = 1.0e-4, + l1_ratio: float = 0.15, + learning_rate: str = "constant", + eta0: float = 0.01, + power_t: float = 0.25, + epsilon: float = 0.1, + max_iter: int = 5, + fit_intercept: bool = True, + n_samples: int = 20, + n_features: int = 3 + ): + """Generate fixture data and write to file. + + # Arguments + + * `name::str`: output filename + * `loss::str`: loss function name + * `penalty`: penalty name + * `alpha`: regularization parameter + * `l1_ratio`: elastic net mixing parameter + * `learning_rate::str`: learning rate schedule + * `eta0`: initial learning rate + * `power_t`: exponent for the inverse scaling learning rate + * `epsilon`: insensitivity parameter + * `max_iter`: number of epochs + * `fit_intercept`: boolean indicating whether to fit an intercept + * `n_samples`: number of samples + * `n_features`: number of features + + # Examples + + ``` python + python> gen('./data.json', penalty='l2') + ``` + """ + x = np.asarray( + np.random.uniform( -2.0, 2.0, ( n_samples, n_features ) ), + dtype=np.float64 + ) + beta = np.random.uniform( -1.0, 1.0, n_features ) + y = x @ beta + np.random.normal( 0.0, 0.1, n_samples ) + + if loss in classification: + y = np.where( y > 0.0, 1.0, -1.0 ) # binary classification + + y = np.asarray( y, dtype=np.float64 ) + + # Generate initial coefficients: + w = np.asarray( + np.random.uniform( -0.05, 0.05, n_features ), + dtype=np.float64 + ) + + opts = { + "loss": loss, + "penalty": penalty, + "alpha": alpha, + "l1_ratio": l1_ratio, + "fit_intercept": fit_intercept, + "max_iter": max_iter, + "learning_rate": learning_rate, + "eta0": eta0, + "power_t": power_t, + "epsilon": epsilon, + + # Equivalence settings to make it perform similar to stdlib implementation: + "tol": None, + "shuffle": False, + "early_stopping": False, + "warm_start": False, + "average": False, + "random_state": 0 + } + + if loss in classification: + model = SGDClassifier(class_weight=None, **opts) + model.fit(x, y, coef_init=w.reshape(1, n_features).copy(), + intercept_init=np.zeros(1, dtype=np.float64)) + expected = model.coef_.ravel() # returns 2D array as it supports multi-class classification + else: + model = SGDRegressor(**opts) + model.fit(x, y, coef_init=w.copy(), + intercept_init=np.zeros(1, dtype=np.float64)) + expected = model.coef_ + + # Ensure that the convergence check did not terminate training early: + assert model.n_iter_ == max_iter, (name, model.n_iter_, max_iter) + + intercept = 0.0 + expected_intercept = float(model.intercept_[0]) + + data = { + "M": n_samples, + "N": n_features, + "X": x.ravel().tolist(), + "y": y.tolist(), + "w": w.tolist() + [intercept], + "workspace": [0.0] * n_features, + "order": "row-major", + "LDX": n_features, + "penalty": penalties[penalty], + "loss_function": losses[loss], + "learning_rate": learning_rate, + "lambda": alpha, + "l1_ratio": l1_ratio, + "eta0": eta0, + "power_t": power_t, + "epsilon": epsilon, + "max_iter": max_iter, + "fit_intercept": fit_intercept, + "intercept": intercept, + "expected_w": expected.tolist(), + "expected_intercept": expected_intercept + } + + # Based on the script directory, create an output filepath: + filepath = os.path.join(DIR, name) + + # Create the output directory if it doesn't exist: + os.makedirs(os.path.dirname(filepath), exist_ok=True) + + # Write the data to the output filepath as JSON: + with open(filepath, "w", encoding="utf-8") as outfile: + json.dump(data, outfile) + + # Include trailing newline: + with open(filepath, "a", encoding="utf-8") as outfile: + outfile.write("\n") + + +def main(): + """Generate fixture data.""" + # Penalties: + gen("penalties/penalty_none.json", penalty=None, alpha=0.0) + gen("penalties/penalty_l2.json", penalty="l2") + gen("penalties/penalty_l1.json", penalty="l1") + gen("penalties/penalty_elasticnet.json", penalty="elasticnet") + + # Learning rate schedules: + gen("learning-rates/lr_constant.json", learning_rate="constant") + gen("learning-rates/lr_invscaling.json", learning_rate="invscaling") + + # Intercept handling: + gen("no_intercept.json", fit_intercept=False) + + # Large insensitivity parameter (i.e., many zero gradients): + gen("large_epsilon.json", loss="squared_epsilon_insensitive", epsilon=2.0) + + # Loss functions (regression): + gen("loss-functions/loss_squared_error.json", loss="squared_error") + gen("loss-functions/loss_epsilon_insensitive.json", loss="epsilon_insensitive") + gen("loss-functions/loss_squared_epsilon_insensitive.json", + loss="squared_epsilon_insensitive") + gen("loss-functions/loss_huber.json", loss="huber") + + # Loss functions (binary classification, `y` in `{-1,+1}`): + gen("loss-functions/loss_hinge.json", loss="hinge") + gen("loss-functions/loss_squared_hinge.json", loss="squared_hinge") + gen("loss-functions/loss_perceptron.json", loss="perceptron") + gen("loss-functions/loss_modified_huber.json", loss="modified_huber") + gen("loss-functions/loss_log.json", loss="log_loss") + + # Larger number of features: + gen("large_nfeatures.json", penalty="elasticnet", n_samples=30, + n_features=8) + + # Multiple epochs: + gen("many_epochs.json", max_iter=25) + + +if __name__ == "__main__": + main() diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/test.dsgd_trainer.js b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/test.dsgd_trainer.js new file mode 100644 index 000000000000..ca1d3ee66580 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/test.dsgd_trainer.js @@ -0,0 +1,1027 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* eslint-disable max-len */ + +'use strict'; + +// MODULES // + +var tape = require( 'tape' ); +var isAlmostSameValue = require( '@stdlib/number/float64/base/assert/is-almost-same-value' ); +var penalty2enum = require( '@stdlib/ml/base/sgd/penalty-str2enum' ); +var lr2enum = require( '@stdlib/ml/base/sgd/learning-rate-str2enum' ); +var loss2enum = require( '@stdlib/ml/base/sgd/loss-function-str2enum' ); +var Float64Params = require( '@stdlib/ml/base/sgd/params/float64' ); +var Float64Array = require( '@stdlib/array/float64' ); +var dsgdTrainer = require( './../lib/dsgd_trainer.js' ); + + +// FIXTURES // + +var penaltyNone = require( './fixtures/python/penalties/penalty_none.json' ); +var penaltyL2 = require( './fixtures/python/penalties/penalty_l2.json' ); +var penaltyL1 = require( './fixtures/python/penalties/penalty_l1.json' ); +var penaltyElasticnet = require( './fixtures/python/penalties/penalty_elasticnet.json' ); +var lrConstant = require( './fixtures/python/learning-rates/lr_constant.json' ); +var lrInvscaling = require( './fixtures/python/learning-rates/lr_invscaling.json' ); +var noIntercept = require( './fixtures/python/no_intercept.json' ); +var largeEpsilon = require( './fixtures/python/large_epsilon.json' ); +var lossSquaredError = require( './fixtures/python/loss-functions/loss_squared_error.json' ); +var lossEpsilonInsensitive = require( './fixtures/python/loss-functions/loss_epsilon_insensitive.json' ); +var lossSqEpsInsensitive = require( './fixtures/python/loss-functions/loss_squared_epsilon_insensitive.json' ); +var lossHuber = require( './fixtures/python/loss-functions/loss_huber.json' ); +var lossHinge = require( './fixtures/python/loss-functions/loss_hinge.json' ); +var lossSquaredHinge = require( './fixtures/python/loss-functions/loss_squared_hinge.json' ); +var lossPerceptron = require( './fixtures/python/loss-functions/loss_perceptron.json' ); +var lossModifiedHuber = require( './fixtures/python/loss-functions/loss_modified_huber.json' ); +var lossLog = require( './fixtures/python/loss-functions/loss_log.json' ); +var largeNfeatures = require( './fixtures/python/large_nfeatures.json' ); +var manyEpochs = require( './fixtures/python/many_epochs.json' ); + + +// TESTS // + +tape( 'main export is a function', function test( t ) { + t.ok( true, __filename ); + t.strictEqual( typeof dsgdTrainer, 'function', 'main export is a function' ); + t.end(); +}); + +tape( 'the function has an arity of 12', function test( t ) { + t.strictEqual( dsgdTrainer.length, 12, 'returns expected value' ); + t.end(); +}); + +tape( 'the function throws an error if provided an invalid first argument', function test( t ) { + var values; + var params; + var data; + var i; + + data = penaltyL2; + + values = [ + 'foo', + 'bar', + 'beep', + 'boop' + ]; + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + params = new Float64Params(params ); + + for ( i = 0; i < values.length; i++ ) { + t.throws( badValue( values[ i ] ), TypeError, 'throws an error when provided ' + values[ i ] ); + } + t.end(); + + function badValue( value ) { + return function badValue() { + dsgdTrainer( value, data.M, data.N, new Float64Array( data.X ), data.LDX, new Float64Array( data.y ), 1, new Float64Array( data.N+1 ), 1, new Float64Array( data.N ), 1, params ); + }; + } +}); + +tape( 'the function throws an error if provided an invalid second argument', function test( t ) { + var values; + var params; + var data; + var i; + + data = penaltyL2; + + values = [ + -1, + -2, + -3 + ]; + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + params = new Float64Params(params ); + + for ( i = 0; i < values.length; i++ ) { + t.throws( badValue( values[ i ] ), RangeError, 'throws an error when provided ' + values[ i ] ); + } + t.end(); + + function badValue( value ) { + return function badValue() { + dsgdTrainer( data.order, value, data.N, new Float64Array( data.X ), data.LDX, new Float64Array( data.y ), 1, new Float64Array( data.N+1 ), 1, new Float64Array( data.N ), 1, params ); + }; + } +}); + +tape( 'the function throws an error if provided an invalid third argument', function test( t ) { + var values; + var params; + var data; + var i; + + data = penaltyL2; + + values = [ + -1, + -2, + -3 + ]; + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + params = new Float64Params(params ); + + for ( i = 0; i < values.length; i++ ) { + t.throws( badValue( values[ i ] ), RangeError, 'throws an error when provided ' + values[ i ] ); + } + t.end(); + + function badValue( value ) { + return function badValue() { + dsgdTrainer( data.order, data.M, value, new Float64Array( data.X ), data.LDX, new Float64Array( data.y ), 1, new Float64Array( data.N+1 ), 1, new Float64Array( data.N ), 1, params ); + }; + } +}); + +tape( 'the function throws an error if provided an invalid fifth argument', function test( t ) { + var values; + var params; + var data; + var i; + + data = penaltyL2; + + values = [ + 0, + -1 + ]; + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + params = new Float64Params(params ); + + for ( i = 0; i < values.length; i++ ) { + t.throws( badValue( values[ i ] ), RangeError, 'throws an error when provided ' + values[ i ] ); + } + t.end(); + + function badValue( value ) { + return function badValue() { + dsgdTrainer( data.order, data.M, data.N, new Float64Array( data.X ), value, new Float64Array( data.y ), 1, new Float64Array( data.N+1 ), 1, new Float64Array( data.N ), 1, params ); + }; + } +}); + +tape( 'the function throws an error if provided an invalid seventh argument', function test( t ) { + var values; + var params; + var data; + var i; + + data = penaltyL2; + + values = [ + 0 + ]; + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + params = new Float64Params(params ); + + for ( i = 0; i < values.length; i++ ) { + t.throws( badValue( values[ i ] ), RangeError, 'throws an error when provided ' + values[ i ] ); + } + t.end(); + + function badValue( value ) { + return function badValue() { + dsgdTrainer( data.order, data.M, data.N, new Float64Array( data.X ), data.LDX, new Float64Array( data.y ), value, new Float64Array( data.N+1 ), 1, new Float64Array( data.N ), 1, params ); + }; + } +}); + +tape( 'the function throws an error if provided an invalid ninth argument', function test( t ) { + var values; + var params; + var data; + var i; + + data = penaltyL2; + + values = [ + 0 + ]; + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + params = new Float64Params(params ); + + for ( i = 0; i < values.length; i++ ) { + t.throws( badValue( values[ i ] ), RangeError, 'throws an error when provided ' + values[ i ] ); + } + t.end(); + + function badValue( value ) { + return function badValue() { + dsgdTrainer( data.order, data.M, data.N, new Float64Array( data.X ), data.LDX, new Float64Array( data.y ), 1, new Float64Array( data.N+1 ), value, new Float64Array( data.N ), 1, params ); + }; + } +}); + +tape( 'the function throws an error if provided an invalid eleventh argument', function test( t ) { + var values; + var params; + var data; + var i; + + data = penaltyL2; + + values = [ + 0 + ]; + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + params = new Float64Params(params ); + + for ( i = 0; i < values.length; i++ ) { + t.throws( badValue( values[ i ] ), RangeError, 'throws an error when provided ' + values[ i ] ); + } + t.end(); + + function badValue( value ) { + return function badValue() { + dsgdTrainer( data.order, data.M, data.N, new Float64Array( data.X ), data.LDX, new Float64Array( data.y ), 1, new Float64Array( data.N+1 ), 1, new Float64Array( data.N ), value, params ); + }; + } +}); + +tape( 'the function trains a linear model via stochastic gradient descent (no penalty)', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = penaltyNone; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.order, data.M, data.N, new Float64Array( data.X ), data.LDX, new Float64Array( data.y ), 1, w, 1, workspace, 1, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], 0 ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, 0 ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function trains a linear model via stochastic gradient descent (L2 penalty)', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = penaltyL2; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.order, data.M, data.N, new Float64Array( data.X ), data.LDX, new Float64Array( data.y ), 1, w, 1, workspace, 1, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], 0 ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, 0 ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function trains a linear model via stochastic gradient descent (L1 penalty)', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = penaltyL1; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.order, data.M, data.N, new Float64Array( data.X ), data.LDX, new Float64Array( data.y ), 1, w, 1, workspace, 1, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], 0 ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, 0 ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function trains a linear model via stochastic gradient descent (elastic net penalty)', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = penaltyElasticnet; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.order, data.M, data.N, new Float64Array( data.X ), data.LDX, new Float64Array( data.y ), 1, w, 1, workspace, 1, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], 0 ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, 0 ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function supports a constant learning rate scheduler', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = lrConstant; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.order, data.M, data.N, new Float64Array( data.X ), data.LDX, new Float64Array( data.y ), 1, w, 1, workspace, 1, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], 0 ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, 0 ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function supports an inverse scaling learning rate scheduler', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = lrInvscaling; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.order, data.M, data.N, new Float64Array( data.X ), data.LDX, new Float64Array( data.y ), 1, w, 1, workspace, 1, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], 0 ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, 0 ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function supports a large insensitivity parameter', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = largeEpsilon; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.order, data.M, data.N, new Float64Array( data.X ), data.LDX, new Float64Array( data.y ), 1, w, 1, workspace, 1, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], 0 ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, 0 ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function supports the squared error loss function', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = lossSquaredError; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.order, data.M, data.N, new Float64Array( data.X ), data.LDX, new Float64Array( data.y ), 1, w, 1, workspace, 1, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], 0 ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, 0 ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function supports the epsilon-insensitive loss function', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = lossEpsilonInsensitive; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.order, data.M, data.N, new Float64Array( data.X ), data.LDX, new Float64Array( data.y ), 1, w, 1, workspace, 1, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], 0 ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, 0 ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function supports the squared epsilon-insensitive loss function', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = lossSqEpsInsensitive; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.order, data.M, data.N, new Float64Array( data.X ), data.LDX, new Float64Array( data.y ), 1, w, 1, workspace, 1, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], 0 ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, 0 ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function supports the Huber loss function', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = lossHuber; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.order, data.M, data.N, new Float64Array( data.X ), data.LDX, new Float64Array( data.y ), 1, w, 1, workspace, 1, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], 0 ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, 0 ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function supports the hinge loss function', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = lossHinge; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.order, data.M, data.N, new Float64Array( data.X ), data.LDX, new Float64Array( data.y ), 1, w, 1, workspace, 1, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], 0 ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, 0 ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function supports the squared hinge loss function', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = lossSquaredHinge; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.order, data.M, data.N, new Float64Array( data.X ), data.LDX, new Float64Array( data.y ), 1, w, 1, workspace, 1, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], 0 ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, 0 ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function supports the perceptron loss function', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = lossPerceptron; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.order, data.M, data.N, new Float64Array( data.X ), data.LDX, new Float64Array( data.y ), 1, w, 1, workspace, 1, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], 0 ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, 0 ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function supports the modified Huber loss function', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = lossModifiedHuber; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.order, data.M, data.N, new Float64Array( data.X ), data.LDX, new Float64Array( data.y ), 1, w, 1, workspace, 1, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], 0 ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, 0 ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function supports the log loss function', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = lossLog; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.order, data.M, data.N, new Float64Array( data.X ), data.LDX, new Float64Array( data.y ), 1, w, 1, workspace, 1, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], 0 ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, 0 ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function supports a large number of features', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = largeNfeatures; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.order, data.M, data.N, new Float64Array( data.X ), data.LDX, new Float64Array( data.y ), 1, w, 1, workspace, 1, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], 0 ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, 0 ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function supports multiple epochs', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = manyEpochs; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.order, data.M, data.N, new Float64Array( data.X ), data.LDX, new Float64Array( data.y ), 1, w, 1, workspace, 1, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], 0 ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, 0 ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function does not fit an intercept when `fitIntercept` is `false`', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = noIntercept; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.order, data.M, data.N, new Float64Array( data.X ), data.LDX, new Float64Array( data.y ), 1, w, 1, workspace, 1, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], 0 ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( out[ data.N ], 0.0, 'does not store an intercept' ); + + t.end(); +}); diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/test.js b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/test.js new file mode 100644 index 000000000000..a086d4b31ae6 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/test.js @@ -0,0 +1,38 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +'use strict'; + +// MODULES // + +var tape = require( 'tape' ); +var dsgdTrainer = require( './../lib' ); + + +// TESTS // + +tape( 'main export is a function', function test( t ) { + t.ok( true, __filename ); + t.strictEqual( typeof dsgdTrainer, 'function', 'main export is a function' ); + t.end(); +}); + +tape( 'attached to the main export is a method providing an ndarray interface', function test( t ) { + t.strictEqual( typeof dsgdTrainer.ndarray, 'function', 'method is a function' ); + t.end(); +}); diff --git a/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/test.ndarray.js b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/test.ndarray.js new file mode 100644 index 000000000000..7f35dc290c24 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/test/test.ndarray.js @@ -0,0 +1,1001 @@ +/** +* @license Apache-2.0 +* +* Copyright (c) 2026 The Stdlib Authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* eslint-disable max-len */ + +'use strict'; + +// MODULES // + +var tape = require( 'tape' ); +var isAlmostSameValue = require( '@stdlib/number/float64/base/assert/is-almost-same-value' ); +var penalty2enum = require( '@stdlib/ml/base/sgd/penalty-str2enum' ); +var lr2enum = require( '@stdlib/ml/base/sgd/learning-rate-str2enum' ); +var loss2enum = require( '@stdlib/ml/base/sgd/loss-function-str2enum' ); +var Float64Params = require( '@stdlib/ml/base/sgd/params/float64' ); +var Float64Array = require( '@stdlib/array/float64' ); +var dsgdTrainer = require( './../lib/ndarray.js' ); + + +// FIXTURES // + +var penaltyNone = require( './fixtures/python/penalties/penalty_none.json' ); +var penaltyL2 = require( './fixtures/python/penalties/penalty_l2.json' ); +var penaltyL1 = require( './fixtures/python/penalties/penalty_l1.json' ); +var penaltyElasticnet = require( './fixtures/python/penalties/penalty_elasticnet.json' ); +var lrConstant = require( './fixtures/python/learning-rates/lr_constant.json' ); +var lrInvscaling = require( './fixtures/python/learning-rates/lr_invscaling.json' ); +var noIntercept = require( './fixtures/python/no_intercept.json' ); +var largeEpsilon = require( './fixtures/python/large_epsilon.json' ); +var lossSquaredError = require( './fixtures/python/loss-functions/loss_squared_error.json' ); +var lossEpsilonInsensitive = require( './fixtures/python/loss-functions/loss_epsilon_insensitive.json' ); +var lossSqEpsInsensitive = require( './fixtures/python/loss-functions/loss_squared_epsilon_insensitive.json' ); +var lossHuber = require( './fixtures/python/loss-functions/loss_huber.json' ); +var lossHinge = require( './fixtures/python/loss-functions/loss_hinge.json' ); +var lossSquaredHinge = require( './fixtures/python/loss-functions/loss_squared_hinge.json' ); +var lossPerceptron = require( './fixtures/python/loss-functions/loss_perceptron.json' ); +var lossModifiedHuber = require( './fixtures/python/loss-functions/loss_modified_huber.json' ); +var lossLog = require( './fixtures/python/loss-functions/loss_log.json' ); +var largeNfeatures = require( './fixtures/python/large_nfeatures.json' ); +var manyEpochs = require( './fixtures/python/many_epochs.json' ); + + +// VARIABLES // + +// Maximum allowed ULP difference when comparing against scikit-learn: +var ULP = 16; + + +// TESTS // + +tape( 'main export is a function', function test( t ) { + t.ok( true, __filename ); + t.strictEqual( typeof dsgdTrainer, 'function', 'main export is a function' ); + t.end(); +}); + +tape( 'the function has an arity of 16', function test( t ) { + t.strictEqual( dsgdTrainer.length, 16, 'returns expected value' ); + t.end(); +}); + +tape( 'the function throws an error if provided an invalid first argument', function test( t ) { + var values; + var params; + var data; + var i; + + data = penaltyL2; + + values = [ + -1, + -2, + -3 + ]; + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + for ( i = 0; i < values.length; i++ ) { + t.throws( badValue( values[ i ] ), RangeError, 'throws an error when provided ' + values[ i ] ); + } + t.end(); + + function badValue( value ) { + return function badValue() { + dsgdTrainer( value, data.N, new Float64Array( data.X ), data.LDX, 1, 0, new Float64Array( data.y ), 1, 0, new Float64Array( data.N+1 ), 1, 0, new Float64Array( data.N ), 1, 0, params ); + }; + } +}); + +tape( 'the function throws an error if provided an invalid second argument', function test( t ) { + var values; + var params; + var data; + var i; + + data = penaltyL2; + + values = [ + -1, + -2, + -3 + ]; + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + for ( i = 0; i < values.length; i++ ) { + t.throws( badValue( values[ i ] ), RangeError, 'throws an error when provided ' + values[ i ] ); + } + t.end(); + + function badValue( value ) { + return function badValue() { + dsgdTrainer( data.M, value, new Float64Array( data.X ), data.LDX, 1, 0, new Float64Array( data.y ), 1, 0, new Float64Array( data.N+1 ), 1, 0, new Float64Array( data.N ), 1, 0, params ); + }; + } +}); + +tape( 'the function throws an error if provided an invalid eighth argument', function test( t ) { + var values; + var params; + var data; + var i; + + data = penaltyL2; + + values = [ + 0 + ]; + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + for ( i = 0; i < values.length; i++ ) { + t.throws( badValue( values[ i ] ), RangeError, 'throws an error when provided ' + values[ i ] ); + } + t.end(); + + function badValue( value ) { + return function badValue() { + dsgdTrainer( data.M, data.N, new Float64Array( data.X ), data.LDX, 1, 0, new Float64Array( data.y ), value, 0, new Float64Array( data.N+1 ), 1, 0, new Float64Array( data.N ), 1, 0, params ); + }; + } +}); + +tape( 'the function throws an error if provided an invalid eleventh argument', function test( t ) { + var values; + var params; + var data; + var i; + + data = penaltyL2; + + values = [ + 0 + ]; + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + for ( i = 0; i < values.length; i++ ) { + t.throws( badValue( values[ i ] ), RangeError, 'throws an error when provided ' + values[ i ] ); + } + t.end(); + + function badValue( value ) { + return function badValue() { + dsgdTrainer( data.M, data.N, new Float64Array( data.X ), data.LDX, 1, 0, new Float64Array( data.y ), 1, 0, new Float64Array( data.N+1 ), value, 0, new Float64Array( data.N ), 1, 0, params ); + }; + } +}); + +tape( 'the function throws an error if provided an invalid fourteenth argument', function test( t ) { + var values; + var params; + var data; + var i; + + data = penaltyL2; + + values = [ + 0 + ]; + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + for ( i = 0; i < values.length; i++ ) { + t.throws( badValue( values[ i ] ), RangeError, 'throws an error when provided ' + values[ i ] ); + } + t.end(); + + function badValue( value ) { + return function badValue() { + dsgdTrainer( data.M, data.N, new Float64Array( data.X ), data.LDX, 1, 0, new Float64Array( data.y ), 1, 0, new Float64Array( data.N+1 ), 1, 0, new Float64Array( data.N ), value, 0, params ); + }; + } +}); + +tape( 'the function trains a linear model via stochastic gradient descent (no penalty)', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = penaltyNone; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.M, data.N, new Float64Array( data.X ), data.LDX, 1, 0, new Float64Array( data.y ), 1, 0, w, 1, 0, workspace, 1, 0, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], ULP ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, ULP ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function trains a linear model via stochastic gradient descent (L2 penalty)', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = penaltyL2; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.M, data.N, new Float64Array( data.X ), data.LDX, 1, 0, new Float64Array( data.y ), 1, 0, w, 1, 0, workspace, 1, 0, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], ULP ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, ULP ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function trains a linear model via stochastic gradient descent (L1 penalty)', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = penaltyL1; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.M, data.N, new Float64Array( data.X ), data.LDX, 1, 0, new Float64Array( data.y ), 1, 0, w, 1, 0, workspace, 1, 0, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], ULP ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, ULP ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function trains a linear model via stochastic gradient descent (elastic net penalty)', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = penaltyElasticnet; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.M, data.N, new Float64Array( data.X ), data.LDX, 1, 0, new Float64Array( data.y ), 1, 0, w, 1, 0, workspace, 1, 0, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], ULP ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, ULP ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function supports a constant learning rate scheduler', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = lrConstant; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.M, data.N, new Float64Array( data.X ), data.LDX, 1, 0, new Float64Array( data.y ), 1, 0, w, 1, 0, workspace, 1, 0, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], ULP ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, ULP ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function supports an inverse scaling learning rate scheduler', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = lrInvscaling; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.M, data.N, new Float64Array( data.X ), data.LDX, 1, 0, new Float64Array( data.y ), 1, 0, w, 1, 0, workspace, 1, 0, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], ULP ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, ULP ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function supports a large insensitivity parameter', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = largeEpsilon; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.M, data.N, new Float64Array( data.X ), data.LDX, 1, 0, new Float64Array( data.y ), 1, 0, w, 1, 0, workspace, 1, 0, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], ULP ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, ULP ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function supports the squared error loss function', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = lossSquaredError; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.M, data.N, new Float64Array( data.X ), data.LDX, 1, 0, new Float64Array( data.y ), 1, 0, w, 1, 0, workspace, 1, 0, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], ULP ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, ULP ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function supports the epsilon-insensitive loss function', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = lossEpsilonInsensitive; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.M, data.N, new Float64Array( data.X ), data.LDX, 1, 0, new Float64Array( data.y ), 1, 0, w, 1, 0, workspace, 1, 0, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], ULP ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, ULP ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function supports the squared epsilon-insensitive loss function', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = lossSqEpsInsensitive; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.M, data.N, new Float64Array( data.X ), data.LDX, 1, 0, new Float64Array( data.y ), 1, 0, w, 1, 0, workspace, 1, 0, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], ULP ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, ULP ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function supports the Huber loss function', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = lossHuber; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.M, data.N, new Float64Array( data.X ), data.LDX, 1, 0, new Float64Array( data.y ), 1, 0, w, 1, 0, workspace, 1, 0, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], ULP ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, ULP ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function supports the hinge loss function', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = lossHinge; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.M, data.N, new Float64Array( data.X ), data.LDX, 1, 0, new Float64Array( data.y ), 1, 0, w, 1, 0, workspace, 1, 0, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], ULP ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, ULP ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function supports the squared hinge loss function', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = lossSquaredHinge; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.M, data.N, new Float64Array( data.X ), data.LDX, 1, 0, new Float64Array( data.y ), 1, 0, w, 1, 0, workspace, 1, 0, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], ULP ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, ULP ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function supports the perceptron loss function', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = lossPerceptron; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.M, data.N, new Float64Array( data.X ), data.LDX, 1, 0, new Float64Array( data.y ), 1, 0, w, 1, 0, workspace, 1, 0, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], ULP ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, ULP ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function supports the modified Huber loss function', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = lossModifiedHuber; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.M, data.N, new Float64Array( data.X ), data.LDX, 1, 0, new Float64Array( data.y ), 1, 0, w, 1, 0, workspace, 1, 0, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], ULP ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, ULP ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function supports the log loss function', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = lossLog; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.M, data.N, new Float64Array( data.X ), data.LDX, 1, 0, new Float64Array( data.y ), 1, 0, w, 1, 0, workspace, 1, 0, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], ULP ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, ULP ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function supports a large number of features', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = largeNfeatures; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.M, data.N, new Float64Array( data.X ), data.LDX, 1, 0, new Float64Array( data.y ), 1, 0, w, 1, 0, workspace, 1, 0, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], ULP ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, ULP ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function supports multiple epochs', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = manyEpochs; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.M, data.N, new Float64Array( data.X ), data.LDX, 1, 0, new Float64Array( data.y ), 1, 0, w, 1, 0, workspace, 1, 0, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], ULP ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( isAlmostSameValue( out[ data.N ], data.expected_intercept, ULP ), true, 'returns expected intercept' ); + + t.end(); +}); + +tape( 'the function does not fit an intercept when `fitIntercept` is `false`', function test( t ) { + var workspace; + var params; + var data; + var out; + var w; + var i; + + data = noIntercept; + + w = new Float64Array( data.w ); + workspace = new Float64Array( data.workspace ); + + params = new Float64Params({ + 'penalty': penalty2enum( data.penalty ), + 'learningRate': lr2enum( data.learning_rate ), + 'lossFunction': loss2enum( data.loss_function ), + 'penaltyParams': new Float64Array( [ data.lambda, data.l1_ratio ] ), + 'learningRateParams': new Float64Array( [ data.eta0, data.power_t ] ), + 'lossFunctionParams': new Float64Array( [ data.epsilon ] ), + 'intercept': data.intercept, + 'maxIter': data.max_iter, + 'fitIntercept': data.fit_intercept + }); + + out = dsgdTrainer( data.M, data.N, new Float64Array( data.X ), data.LDX, 1, 0, new Float64Array( data.y ), 1, 0, w, 1, 0, workspace, 1, 0, params ); + + t.strictEqual( out, w, 'returns expected value' ); + for ( i = 0; i < data.N; i++ ) { + t.strictEqual( isAlmostSameValue( out[ i ], data.expected_w[ i ], ULP ), true, 'returns expected coefficient '+i ); + } + t.strictEqual( out[ 0 + ( data.N*1 ) ], 0.0, 'does not store an intercept' ); + + t.end(); +}); + +tape( 'if `M` is `0`, the function returns the weight vector unchanged', function test( t ) { + var expected; + var params; + var out; + var w; + + w = new Float64Array( [ 1.0, 2.0, 3.0 ] ); + expected = new Float64Array( [ 1.0, 2.0, 3.0 ] ); + + params = new Float64Params({ + 'penalty': penalty2enum( 'l2' ), + 'learningRate': lr2enum( 'constant' ), + 'lossFunction': loss2enum( 'hinge' ), + 'penaltyParams': new Float64Array( [ 1.0e-4, 0.0 ] ), + 'learningRateParams': new Float64Array( [ 0.01, 0.0 ] ), + 'lossFunctionParams': new Float64Array( [ 0.1 ] ), + 'intercept': 0.0, + 'maxIter': 5, + 'fitIntercept': true + }); + + out = dsgdTrainer( 0, 2, new Float64Array( 4 ), 2, 1, 0, new Float64Array( 2 ), 1, 0, w, 1, 0, new Float64Array( 2 ), 1, 0, params ); + + t.strictEqual( out, w, 'returns expected value' ); + t.deepEqual( out, expected, 'returns expected value' ); + + t.end(); +}); + +tape( 'if `N` is `0`, the function returns the weight vector unchanged', function test( t ) { + var expected; + var params; + var out; + var w; + + w = new Float64Array( [ 1.0, 2.0, 3.0 ] ); + expected = new Float64Array( [ 1.0, 2.0, 3.0 ] ); + + params = new Float64Params({ + 'penalty': penalty2enum( 'l2' ), + 'learningRate': lr2enum( 'constant' ), + 'lossFunction': loss2enum( 'hinge' ), + 'penaltyParams': new Float64Array( [ 1.0e-4, 0.0 ] ), + 'learningRateParams': new Float64Array( [ 0.01, 0.0 ] ), + 'lossFunctionParams': new Float64Array( [ 0.1 ] ), + 'intercept': 0.0, + 'maxIter': 5, + 'fitIntercept': true + }); + + out = dsgdTrainer( 2, 0, new Float64Array( 4 ), 2, 1, 0, new Float64Array( 2 ), 1, 0, w, 1, 0, new Float64Array( 2 ), 1, 0, params ); + + t.strictEqual( out, w, 'returns expected value' ); + t.deepEqual( out, expected, 'returns expected value' ); + + t.end(); +});