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..e470c16d3df6 --- /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 uniform = require( '@stdlib/random/array/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 = uniform( N*N, -10.0, 10.0, options ); + var y = uniform( N, -10.0, 10.0, options ); + var w = uniform( 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..b6c8d9406d86 --- /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 uniform = require( '@stdlib/random/array/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 = uniform( N*N, -10.0, 10.0, options ); + var y = uniform( N, -10.0, 10.0, options ); + var w = uniform( 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..b313f6ec99b8 --- /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 uniform = require( '@stdlib/random/array/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 = uniform( N*N, -10.0, 10.0, options ); + var y = uniform( N, -10.0, 10.0, options ); + var w = uniform( 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..17b0c6f752d0 --- /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 uniform = require( '@stdlib/random/array/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 = uniform( N*N, -10.0, 10.0, options ); + var y = uniform( N, -10.0, 10.0, options ); + var w = uniform( 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..ee77ddd0997a --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/benchmark/c/benchmark.length.c @@ -0,0 +1,253 @@ +/** +* @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 +#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_params_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( CblasRowMajor, N, N, x, N, y, 1, w, 1, ws, 1, params ); + 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_params_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, params ); + 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/types/index.d.ts b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/docs/types/index.d.ts new file mode 100644 index 000000000000..841babe49551 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/docs/types/index.d.ts @@ -0,0 +1,134 @@ +/* +* @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'; +import { Params } from '@stdlib/ml/base/sgd/params/float64'; + +/** +* Interface describing `dsgdTrainer`. +*/ +interface Routine { + /** + * Performs one of the matrix-vector operations `y = α*A*x + β*y` or `y = α*A^T*x + β*y`, where `α` and `β` are scalars, `x` and `y` are vectors, and `A` is an `M` by `N` matrix. + * + * @param order - storage layout + * @param trans - specifies whether `A` should be transposed, conjugate-transposed, or not transposed + * @param M - number of rows in the matrix `A` + * @param N - number of columns in the matrix `A` + * @param alpha - scalar constant + * @param A - input matrix + * @param LDA - stride of the first dimension of `A` (a.k.a., leading dimension of the matrix `A`) + * @param x - first input vector + * @param strideX - `x` stride length + * @param beta - scalar constant + * @param y - second input vector + * @param strideY - `y` stride length + * @returns `y` + * + * @example + * var Float64Array = require( '@stdlib/array/float64' ); + * + * var A = new Float64Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 ] ); + * var x = new Float64Array( [ 1.0, 1.0, 1.0 ] ); + * var y = new Float64Array( [ 1.0, 1.0 ] ); + * + * dsgdTrainer( 'row-major', 'no-transpose', 2, 3, 1.0, A, 3, x, 1, 1.0, y, 1 ); + * // y => [ 7.0, 16.0 ] + */ + ( 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; + + /** + * Performs one of the matrix-vector operations `y = α*A*x + β*y` or `y = α*A^T*x + β*y`, using alternative indexing semantics and where `α` and `β` are scalars, `x` and `y` are vectors, and `A` is an `M` by `N` matrix. + * + * @param trans - specifies whether `A` should be transposed, conjugate-transposed, or not transposed + * @param M - number of rows in the matrix `A` + * @param N - number of columns in the matrix `A` + * @param alpha - scalar constant + * @param A - input matrix + * @param strideA1 - stride of the first dimension of `A` + * @param strideA2 - stride of the second dimension of `A` + * @param offsetA - starting index for `A` + * @param x - first input vector + * @param strideX - `x` stride length + * @param offsetX - starting index for `x` + * @param beta - scalar constant + * @param y - second input vector + * @param strideY - `y` stride length + * @param offsetY - starting index for `y` + * @returns `y` + * + * @example + * var Float64Array = require( '@stdlib/array/float64' ); + * + * var A = new Float64Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 ] ); + * var x = new Float64Array( [ 1.0, 1.0, 1.0 ] ); + * var y = new Float64Array( [ 1.0, 1.0 ] ); + * + * dsgdTrainer.ndarray( 'no-transpose', 2, 3, 1.0, A, 3, 1, 0, x, 1, 0, 1.0, y, 1, 0 ); + * // y => [ 7.0, 16.0 ] + */ + 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; +} + +/** +* Performs one of the matrix-vector operations `y = α*A*x + β*y` or `y = α*A^T*x + β*y`, where `α` and `β` are scalars, `x` and `y` are vectors, and `A` is an `M` by `N` matrix. +* +* @param order - storage layout +* @param trans - specifies whether `A` should be transposed, conjugate-transposed, or not transposed +* @param M - number of rows in the matrix `A` +* @param N - number of columns in the matrix `A` +* @param alpha - scalar constant +* @param A - input matrix +* @param LDA - stride of the first dimension of `A` (a.k.a., leading dimension of the matrix `A`) +* @param x - first input vector +* @param strideX - `x` stride length +* @param beta - scalar constant +* @param y - second input vector +* @param strideY - `y` stride length +* @returns `y` +* +* @example +* var Float64Array = require( '@stdlib/array/float64' ); +* +* var A = new Float64Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0 ] ); +* var x = new Float64Array( [ 1.0, 1.0, 1.0 ] ); +* var y = new Float64Array( [ 1.0, 1.0, 1.0 ] ); +* +* dsgdTrainer( 'row-major', 'no-transpose', 3, 3, 1.0, A, 3, x, -1, 1.0, y, -1 ); +* // y => [ 25.0, 16.0, 7.0 ] +* +* @example +* var Float64Array = require( '@stdlib/array/float64' ); +* +* var A = new Float64Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0 ] ); +* var x = new Float64Array( [ 1.0, 1.0, 1.0 ] ); +* var y = new Float64Array( [ 1.0, 1.0, 1.0 ] ); +* +* dsgdTrainer.ndarray( 'no-transpose', 3, 3, 1.0, A, 3, 1, 0, x, -1, 2, 1.0, y, -1, 2 ); +* // y => [ 25.0, 16.0, 7.0 ] +*/ +declare var dsgdTrainer: Routine; + + +// EXPORTS // + +export = dsgdTrainer; 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..f54fab593292 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/include/stdlib/ml/strided/dsgd_trainer.h @@ -0,0 +1,81 @@ +/** +* @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 the C interface to the BLAS Level 2 routine `dgemv`. +*/ +#ifndef STDLIB_ML_STRIDED_DSGD_TRAINER_H +#define STDLIB_ML_STRIDED_DSGD_TRAINER_H + +#include "stdlib/blas/base/shared.h" +#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_params_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; +}; + +/** +* Performs one of the matrix-vector operations `Y = α*A*X + β*Y` or `Y = α*A^T*X + β*Y`, where `α` and `β` are scalars, `X` and `Y` are vectors, and `A` is an `M` by `N` matrix. +*/ +void 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_params_float64_params params ); + +/** +* Performs one of the matrix-vector operations `Y = α*A*X + β*Y` or `Y = α*A^T*X + β*Y`, where `α` and `β` are scalars, `X` and `Y` are vectors, and `A` is an `M` by `N` matrix using alternative indexing semantics. +*/ +void 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_params_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..d8584484b201 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/base.js @@ -0,0 +1,220 @@ +/** +* @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 logger = require( 'debug' ); +var params2str = require( '@stdlib/ml/base/sgd/params/to-string' ); +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 with the squared epsilon-insensitive loss via stochastic gradient descent. +* +* @private +* @param {NonNegativeInteger} M - number of samples +* @param {NonNegativeInteger} N - number of features +* @param {Float64Array} x - `M` by `N` 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 vector +* @param {integer} strideY - stride length for `y` +* @param {NonNegativeInteger} offsetY - starting index for `y` +* @param {Float64Array} w - weight vector +* @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' ); +* +* // Two features, four samples; target y = 2*x0 - 1*x1: +* var x = new Float64Array( [ 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 2.0, 1.0 ] ); +* var y = new Float64Array( [ 2.0, -1.0, 1.0, 3.0 ] ); +* var w = new Float64Array( 2 ); +* var workspace = new Float64Array( 2 ); +* +* var out = dsgdTrainer( +* 'l2', 'invscaling', true, // penalty, learningRate, fitIntercept +* 4, 2, 0.0, // M, N, l1Ratio +* 1000, // maxIter +* 0.02, 0.5, // eta0, powerT +* 0.0, 1.0e-4, // epsilon, lambda +* 0.0, // initial intercept +* y, 1, 0, // y, strideY, offsetY +* w, 1, 0, // w, strideW, offsetW +* x, 2, 1, 0, // x, strideX1, strideX2, offsetX +* workspace, 1, 0 // workspace, strideWS, offsetWS +* ); +*/ +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( params2str( 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: w_eff += -eta*dloss*x <=> w_stored += (-eta*dloss/scaleFactor)*x + // w_eff = w_stored*scaleFactor + daxpy( N, update/scaleFactor, x, strideX2, ox, w, strideW, offsetW ); // eslint-disable-line max-len + + if ( fitIntercept ) { + intercept += update; + } + + u = truncFn( N, u, 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: + // sklearn does w.reset_scale() + if ( scaleFactor !== 1 ) { + dscal( N, scaleFactor, w, strideW, offsetW ); + scaleFactor = 1.0; + } + + debug( 'Finished SGD trainer.' ); + + if ( fitIntercept ) { + w[ offsetW + ( ( strideW * N ) + 1 ) ] = 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..57843440820e --- /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, indexed by penalty enumeration constant. +* +* @private +* @name DECAYS +* @constant +* @type {Array} +* +* @example +* var f = DECAYS[ 2 ]; +* // returns +*/ +var DECAYS = [ + l2Decay, // 0: elasticnet + identityDecay, // 1: l1 + l2Decay, // 2: l2 + identityDecay // 3: none +]; + + +// 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` => `[ eta, 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[ 2 ] ) * eta * params[ 1 ] ) ); // 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..6385bfdd8e35 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/dsgd_trainer.js @@ -0,0 +1,146 @@ +/** +* @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 // + +/** +* Performs one of the matrix-vector operations `y = α*A*x + β*y` or `y = α*A^T*x + β*y`, where `α` and `β` are scalars, `x` and `y` are vectors, and `A` is an `M` by `N` matrix. +* +* @private +* @param {string} order - storage layout +* @param {NonNegativeInteger} M - number of samples +* @param {NonNegativeInteger} N - number of features +* @param {Float64Array} x - `M` by `N` 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 vector +* @param {integer} strideY - stride length for `y` +* @param {Float64Array} w - weight vector +* @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 A = new Float64Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 ] ); +* var x = new Float64Array( [ 1.0, 1.0, 1.0 ] ); +* var y = new Float64Array( [ 1.0, 1.0 ] ); +* +* dsgdTrainer( 'row-major', 'no-transpose', 2, 3, 1.0, A, 3, x, 1, 1.0, y, 1 ); +* // y => [ 7.0, 16.0 ] +*/ +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. 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; + } + 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..75c5023bc440 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/dsgd_trainer.native.js @@ -0,0 +1,131 @@ +/** +* @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 // + +/** +* Performs one of the matrix-vector operations `y = α*A*x + β*y` or `y = α*A^T*x + β*y`, where `α` and `β` are scalars, `x` and `y` are vectors, and `A` is an `M` by `N` matrix. +* +* @private +* @param {string} order - storage layout +* @param {NonNegativeInteger} M - number of samples +* @param {NonNegativeInteger} N - number of features +* @param {Float64Array} x - `M` by `N` 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 vector +* @param {integer} strideY - stride length for `y` +* @param {Float64Array} w - weight vector +* @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 A = new Float64Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 ] ); +* var x = new Float64Array( [ 1.0, 1.0, 1.0 ] ); +* var y = new Float64Array( [ 1.0, 1.0 ] ); +* +* dsgdTrainer( 'row-major', 'no-transpose', 2, 3, 1.0, A, 3, x, 1, 1.0, y, 1 ); +* // y => [ 7.0, 16.0 ] +*/ +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..cba0a58050a2 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/index.js @@ -0,0 +1,72 @@ +/** +* @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'; + +/** +* BLAS level 2 routine to perform one of the matrix-vector operations `y = α*A*x + β*y` or `y = α*A^T*x + β*y`, where `α` and `β` are scalars, `x` and `y` are vectors, and `A` is an `M` by `N` matrix. +* +* @module @stdlib/ml/strided/dsgd-trainer +* +* @example +* var Float64Array = require( '@stdlib/array/float64' ); +* var dsgdTrainer = require( '@stdlib/ml/strided/dsgd-trainer' ); +* +* var A = new Float64Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 ] ); +* var x = new Float64Array( [ 1.0, 1.0, 1.0 ] ); +* var y = new Float64Array( [ 1.0, 1.0 ] ); +* +* dsgdTrainer( 'row-major', 'no-transpose', 2, 3, 1.0, A, 3, x, 1, 1.0, y, 1 ); +* // y => [ 7.0, 16.0 ] +* +* @example +* var Float64Array = require( '@stdlib/array/float64' ); +* var dsgdTrainer = require( '@stdlib/ml/strided/dsgd-trainer' ); +* +* var A = new Float64Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 ] ); +* var x = new Float64Array( [ 1.0, 1.0, 1.0 ] ); +* var y = new Float64Array( [ 1.0, 1.0 ] ); +* +* dsgdTrainer.ndarray( 'no-transpose', 2, 3, 1.0, A, 3, 1, 0, x, 1, 0, 1.0, y, 1, 0 ); +* // y => [ 7.0, 16.0 ] +*/ + +// 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..87045da1ff81 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/learning_rate.js @@ -0,0 +1,122 @@ +/** +* @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, indexed by learning rate enumeration constant. +* +* @private +* @name LEARNING_RATE_METHODS +* @constant +* @type {Array} +* +* @example +* var Float64Array = require( '@stdlib/array/float64' ); +* +* var f = LEARNING_RATE_METHODS[ 2 ]; +* // returns +* +* var eta = f( 4.0, new Float64Array( [ 0.02, 0.5 ] ) ); +* // returns 0.01 +*/ +var LEARNING_RATE_METHODS = [ + basic, // 0: basic + constant, // 1: constant + invscaling, // 2: invscaling + pegasos // 3: 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 ]; +} + +/** +* 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 inverse scaling 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..35affce4b8d0 --- /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, indexed by loss function enumeration constant. +* +* @private +* @name LOSS_FUNCTIONS +* @constant +* @type {Array} +* +* @example +* var f = LOSS_FUNCTIONS[ 1 ]; +* // returns +*/ +var LOSS_FUNCTIONS = [ + epsilonInsensitive, // 0: epsilon-insensitive + hinge, // 1: hinge + huber, // 2: huber + log, // 3: log + modifiedHuber, // 4: modified-huber + perceptron, // 5: perceptron + squaredEpsilonInsensitive, // 6: squared-epsilon-insensitive + squaredError, // 7: squared-error + squaredHinge // 8: squared-hinge +]; + + +// FUNCTIONS // + +/** +* 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 ); +} + +/** +* 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 ); +} + +/** +* 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 ); +} + +/** +* 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, y, p ); +} + +/** +* 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 ); +} + +/** +* 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 ); +} + +/** +* Computes the gradient of the 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 huber( y, p, params ) { // eslint-disable-line no-unused-vars + return huberGradient( 1.0, y, p ); +} + +/** +* 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 ); +} + +/** +* 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 ); +} + + +// 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..41c3bb7a6985 --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/ndarray.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'; + +// 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 // + +/** +* Performs one of the matrix-vector operations `y = α*A*x + β*y` or `y = α*A^T*x + β*y`, where `α` and `β` are scalars, `x` and `y` are vectors, and `A` is an `M` by `N` matrix. +* +* @param {NonNegativeInteger} M - number of samples +* @param {NonNegativeInteger} N - number of features +* @param {Float64Array} x - `M` by `N` 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 vector +* @param {integer} strideY - stride length for `y` +* @param {NonNegativeInteger} offsetY - starting index for `y` +* @param {Float64Array} w - weight vector +* @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 A = new Float64Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 ] ); +* var x = new Float64Array( [ 1.0, 1.0, 1.0 ] ); +* var y = new Float64Array( [ 1.0, 1.0 ] ); +* +* dgemv( 'no-transpose', 2, 3, 1.0, A, 3, 1, 0, x, 1, 0, 1.0, y, 1, 0 ); +* // y => [ 7.0, 16.0 ] +*/ +function dgemv( 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; + } + 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 = dgemv; 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..f6ae2721b18e --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/ndarray.native.js @@ -0,0 +1,111 @@ +/** +* @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 // + +/** +* Performs one of the matrix-vector operations `y = α*A*x + β*y` or `y = α*A^T*x + β*y`, where `α` and `β` are scalars, `x` and `y` are vectors, and `A` is an `M` by `N` matrix. +* +* @param {NonNegativeInteger} M - number of samples +* @param {NonNegativeInteger} N - number of features +* @param {Float64Array} x - `M` by `N` 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 vector +* @param {integer} strideY - stride length for `y` +* @param {NonNegativeInteger} offsetY - starting index for `y` +* @param {Float64Array} w - weight vector +* @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 A = new Float64Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 ] ); +* var x = new Float64Array( [ 1.0, 1.0, 1.0 ] ); +* var y = new Float64Array( [ 1.0, 1.0 ] ); +* +* dgemv( 'no-transpose', 2, 3, 1.0, A, 3, 1, 0, x, 1, 0, 1.0, y, 1, 0 ); +* // y => [ 7.0, 16.0 ] +*/ +function dgemv( 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 = dgemv; 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..38356f4b3d6c --- /dev/null +++ b/lib/node_modules/@stdlib/ml/strided/dsgd-trainer/lib/truncation.js @@ -0,0 +1,120 @@ +/** +* @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, indexed by penalty enumeration constant. +* +* @private +* @name TRUNCATIONS +* @constant +* @type {Array} +* +* @example +* var f = TRUNCATIONS[ 2 ]; +* // returns +*/ +var TRUNCATIONS = [ + l1Truncation, // 0: elasticnet + l1Truncation, // 1: l1 + noTruncation, // 2: l2 + noTruncation // 3: none +]; + + +// 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 vector (updated in-place) +* @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 (updated in-place) +* @param {integer} strideWS - stride length for `workspace` +* @param {NonNegativeInteger} offsetWS - starting index for `workspace` +* @param {Float64Array} params - strided array containing regularizer specific parameters. +* @returns {void} +*/ +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` => `[ eta, lambda, l1Ratio ]` +* +* @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 vector (updated in-place) +* @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 (updated in-place) +* @param {integer} strideWS - stride length for `workspace` +* @param {NonNegativeInteger} offsetWS - starting index for `workspace` +* @param {Float64Array} params - strided array containing regularizer specific parameters. +* @returns {void} +*/ +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; + + 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 * eta * params[ 1 ] * params[ 2 ]; +} + + +// EXPORTS // + +module.exports = TRUNCATIONS;