XOR Neural Network test - JD.pine
Copy
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Joris Duyck
//@version=4
study("XOR test Neural Network - JD")
//{
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// The arrays provided in Pinescript are linear 1D strucures that can be seen either as a large vertical stack or
// a horizontal row containing a list of values, colors, bools,..
//
// With the FUNCTIONS in this script the 1D ARRAY LIST can be CONVERTED INTO A 2D MATRIX form
//
//
///////////////////////////////////////////
/// BASIC INFO ON THE MATRIX STRUCTURE: ///
///////////////////////////////////////////
//
// The matrix is set up as an 2D structure and is devided in ROWS and COLUMNS.
// following the standard mathematical notation:
//
// a 3 x 4 matrix = 4 columns
// 0 1 2 3 column index
// 0 [a b c d]
// 3 rows 1 [e f g h]
// 2 [i j k l]
// row
// index
//
// With the use of some purpose-built functions, values can be placed or retrieved in a specific column of a certain row
// this can be done by intuitively using row_nr and column_nr coördinates,
// without having to worry on what exact index of the Pine array this value is located (the functions do these conversions for you)
//
//
// the syntax I propose for the 2D Matrix array has the following structure:
//
// - the array starts with 2 VALUES describing the DIMENSION INFORMATION, (rows, columns)
// these are ignored in the actual calculations and serve as a metadata header (similar to the "location, time,... etc." data that is stored in photo files)
// so the array always carries it's own info about the nr. of rows and columns and doesn't need is seperate "info" file!
//
// To stay consistent with the standard Pinescript (array and [lookback]) indexing:
// - indexes for sheets and columns start from 0 (first) and run up to the (total nr of sheets or columns) - 1
// - indexes for rows also start from 0 (most recent, cfr.[lookback]) and run up to the (total nr of rows) - 1
//
// - this 2 value metadata header is followed by the actual df data
// the actual data array can consist of (100,000 - 2) usable items,
//
// In a theoretical example, you can have a matrix with almost 20,000 rows with each 5 columns of data (eg. open, high, low, close, volume) in it!!!
//
///////////////////////////////////
/// SCHEMATIC OF THE STRUCTURE: ///
///////////////////////////////////
//
////// (metadata header with dimensions info)
//
// (0) (1) (array index)
// [nr_of_rows/records, nr_of_columns,
//
//
////// (actual matrix array data)
//
// 0 1 2 3 column_index
//
// (2) (3) (4) (5) (array index)
// 0 record0_val0, record0_val1, record0_val2, record0_val3, ...,
//
// (6) (7) (8) (9) (array index)
// 1 record1_val0, record1_val1, record1_val2, record1_val3, ...,
//
// (10) (11) (12) (13) (array index)
// 2 record2_val0, record2_val1, record2_val2, record2_val3, ...,
//
// 3 ...
//
// row_index
//
////////////////////////////////////
/// DATA MANIPULATION FUNCTIONS: ///
////////////////////////////////////
//
// A set of functions are implemented to write info to and retrieve info from the dataframe.
// Of course the possibilities are not limited to the functions here, these were written because they were needed for the script!
// A whole list of other functions can be easily written for other manipulation purposes.
// The ones written in the script are 2D versions of most of the functions that are provided for normal 1D arrays.
//
// Feel free to use the functions library below in your scripts! A little shoutout would be nice, of course!!
//
//
///////////////////////////////////////////////////////////////////////
/// LIST OF FUNCTIONS contained in this script: ///
/// (many other can of course be written, using the same structure) ///
///////////////////////////////////////////////////////////////////////
//
// - INFO functions
// * get the number of COLUMNS
// * get the number of ROWS
// * get the total SIZE (number of columns, number of rows)
//
// - INITIALISATION and SET functions (commands to build the 2D matrix)
// * MATRIX INITIALISATION function, builds 2D matrix with dimensional metadata/size in first 2 values //
// a new matrix array is built (cfr. the conventions I propose above) containing a certain nr of rows and columns
// * function to SET a single value in a certain row/lookback period and column
//
// - GET functions (to retrieve info from the 2D matrix)
// * function to GET/retrieve a single VALUE from a certain row/lookback period and column
// * function to cut of the metadata header and return the array body (as an array)
// * function to GET/retrieve the values from a ROW/RECORD from a certain row/lookback period, the values are returned as an array
//
// - 1D - 2D COORDINATE CONVERSION FUNCTIONS (handy with for loop indexes) ///
// * function to get the ROW INDEX in a 2D matrix from the 1D array index
// * function to get the COLUMN INDEX in a 2D matrix from the 1D array index
// * function to get (row, column) coordinates in a 2D matrix from the 1D array index
// * function to get the 1D array index from (row, column) coordinates in a 2D matrix
//
// - Matrix MANIPULATION functions
// * function to ADD a ROW/RECORD on the TOP of the array, shift the whole list one down and REMOVE the OLDEST row/record
// (2D version of "unshift" + "pop" but with a whole row at once)
// * function to REMOVE one or more ROWS/RECORDS from a 2D matrix
// (if from_row == to_row, only this row is removed)
// * function to REMOVE one or more COLUMNS from a 2D matrix
// (if from_column == to_column, only this column is removed)
// * function to INSERT an ARRAY of ROWS/RECORDS at a certain row number in a 2D matrix
// * function to INSERT an ARRAY of COLUMNS at a certain column number in a 2D matrix
// * function to APPEND/ADD an ARRAY of ROWS/RECORDS to the BOTTOM of a 2D matrix
// * function to APPEND/ADD an ARRAY of COLUMNS to the SIDE of a 2D matrix
// * function to POP/REMOVE and return the last ROW/RECORD from the BOTTOM of a 2D matrix
// * function to POP/REMOVE and return the last column from the SIDE of a 2D matrix
//
// - function to print a matrix
// This function is mainly used for debugging purposes and displays the array as a 2D matrix notation
//
//
// Enjoy!
// JD.
//
// #NotTradingAdvice #DYOR
//
// Disclaimer.
// I AM NOT A FINANCIAL ADVISOR.
// THESE IDEAS ARE NOT ADVICE AND ARE FOR EDUCATION PURPOSES ONLY.
// ALWAYS DO YOUR OWN RESEARCH!
//}
/////////////////////////////////////////////////////////////////////////////////
// Function declarations //
//{
// Psuedo random number generator, by @RicardoSantos
//{
//| -------------------------------------------------------------------------------|
var float[] seed = array.new_float(size=1, initial_value=42.0) //-|
f_prng(_range)=> //---|
//| pseudo random function: // O O |
var float _return = 1.0 //- V -|
float _seed = array.get(id=seed, index=0) //NNNN|
float _reseed = 271.828 // A |
_return := ((3.1416 * _return % _seed) * _reseed) % _range // V |
array.set(id=seed, index=0, value=(1 + (_seed * 1.618) % _range) + _return) //||
_return // ||
//| -------------------------------------------------------------------------------|
//}
/////////////////////////////////////////////////////////////////////////////////
/// Matrix Functions ///
/////////////////////////////////////////////////////////////////////////////////
//{
/// MATRIX INFO FUNCTIONS ///
//{
// function to get total number of rows from a 2D matrix
f_matrix_get_nr_of_rows(_m) =>
//{
_rows = int(array.get(_m, 0))
_rows
//}
// function to get total number of columns from a 2D matrix
f_matrix_get_nr_of_columns(_m) =>
//{
_columns = int(array.get(_m, 1))
_columns
//}
// function to get total number of columns and rows from a 2D matrix
f_matrix_get_size(_m) =>
//{
_rows = int(array.get(_m, 0))
_columns = int(array.get(_m, 1))
[_rows, _columns]
//}
//}
/// INITIALISATION AND SET FUNCTIONS ///
//{
// 2D matrix init function, builds 2D matrix with dimensional metadata in first two values //
f_matrix_init(_nr_of_rows, _nr_of_columns, _fill) =>
//{
_size = int(_nr_of_rows * _nr_of_columns)
_m = array.new_float(_size + 2, _fill)
array.set(_m, 0, _nr_of_rows), array.set(_m, 1, _nr_of_columns)
_m
//}
// function to set values in 2D matrix with (row index, column index) (index for rows and columns both starts at 0 !!)
f_matrix_set(_m, _row_index, _column_index, _val) =>
//{
_columns = f_matrix_get_nr_of_columns(_m)
array.set(_m, 2 + int((_row_index * _columns) + _column_index), _val)
//}
// function to set all elements in a 2D matrix with a value
f_matrix_fill(_m, _val) =>
//{
array.fill(_m, _val, 2)
//}
//}
/// GET FUNCTIONS ///
//{
// function to get values from 2D matrix with (row index, column index) (index for rows and columns both starts at 0 !!)
f_matrix_get(_m, _row_index, _column_index) =>
//{
_columns = f_matrix_get_nr_of_columns(_m)
_val = array.get(_m, 2 + int((_row_index * _columns) + _column_index))
_val
//}
// function to cut of the metadata header and return the array body
f_matrix_copy_slice_body(_m, _do_slice) =>
//{ '_do_slice' variable should be set as:
// - 'false' to only make a copy,
// changes to the new array copy will NOT ALTER the ORIGINAL
// - 'true' to make a slice
// changes to the new array slice WILL(!) ALTER the ORIGINAL
_matrix_slice = array.slice(_m, 2, array.size(_m))
_matrix_body = _do_slice ? _matrix_slice : array.copy(_matrix_slice)
// for i = 0 to 1
// array.remove(_matrix_body, 0)
_matrix_body
//}
// function to GET/retrieve the values from a ROW/RECORD from a certain row/lookback period, the values are returned as an array
f_matrix_get_record(_m, _row_index) =>
//{
_nr_of_columns = f_matrix_get_nr_of_columns(_m)
_start_index = int(2 + _row_index * _nr_of_columns)
_row_slice = array.slice(_m, _start_index , _start_index + _nr_of_columns)
_record = array.copy(_row_slice)
_record
//}
//}
/// 1D - 2D COORDINATE CONVERSION FUNCTIONS (handy with for loop indexes) ///
//{
// function to get row nr. in a 2D matrix from 1D index (index for rows and columns both starts at 0 !!)
f_matrix_get_row_index(_m, _array_index) =>
//{
_columns = f_matrix_get_nr_of_columns(_m)
_row_index = int(floor(_array_index / _columns))
_row_index
//}
// function to get column nr. in a 2D matrix from 1D index (index for rows and columns both starts at 0 !!)
f_matrix_get_column_index(_m, _array_index) =>
//{
_columns = f_matrix_get_nr_of_columns(_m)
_column_index = int(_array_index % _columns)
_column_index
//}
// function to get (row, column) coordinates in 2D matrix from 1D index (index starts at 0 and does not include the header!!)
f_matrix_get_row_column_index(_m, _array_index) =>
//{
_columns = f_matrix_get_nr_of_columns(_m)
_row = int(floor(_array_index / _columns))
_column = int( _array_index % _columns)
[_row, _column]
//}
// function to get 1D index from (row, column) coordinates in 2D matrix (index for row and column both starts at 0 !! Index starts at 0 and does not include the header!!)
f_matrix_get_array_index(_m, _row_index, _column_index) =>
//{
_columns = f_matrix_get_nr_of_columns(_m)
_array_index = int(_row_index * _column_index + _column_index)
_array_index
//}
//}
/// MANIPULATION FUNCTIONS ///
//{
// function to ADD a ROW/RECORD on the TOP of a sheet, shift the whole list one down and REMOVE the OLDEST row/record
// (2D version of "unshift" + "pop" but with a whole row at once)
f_matrix_add_row(_m, _insert_array) =>
//{
[_rows, _columns] = f_matrix_get_size(_m)
[_rows_insert_array, _columns_insert_array] = f_matrix_get_size(_insert_array)
_size_insert_array = _columns_insert_array * _rows_insert_array
if _columns == _columns_insert_array
_insert_array_body = f_matrix_copy_slice_body(_insert_array, false)
for index = (_size_insert_array - 1) to 0
array.insert(_m, 2, array.get(_insert_array_body, index))
array.pop(_m)
array.set(_m, 0, _rows + _rows_insert_array)
_m
//}
// function to remove one or more rows/records from a 2D matrix (if from_row = to_row, only this row is removed)
f_matrix_remove_rows(_m, _from_row, _to_row) =>
//{
[_rows, _columns] = f_matrix_get_size(_m)
if not(_from_row == 0 and _to_row == _rows - 1) and not(_to_row < _from_row)
_start_remove_index = 2 + _from_row * _columns
for i = 1 to (min(_rows - 1, _to_row) - _from_row + 1) * _columns
array.remove(_m, max(_start_remove_index, 2))
array.set(_m, 0, _rows - (min(_rows - 1, _to_row) - _from_row) - 1)
_m
//}
// function to remove one or more columns from a 2D matrix (if from_column = to_column, only this column is removed)
f_matrix_remove_columns(_m, _from_column, _to_column) =>
//{
[_rows, _columns] = f_matrix_get_size(_m)
if not(_from_column == 0 and _to_column == _columns - 1) and not(_to_column < _from_column)
for _row_nr = (_rows - 1) to 0
_start_remove_index = 2 + _row_nr * _columns + _from_column
for i = _from_column to _to_column
array.remove(_m, max(_start_remove_index, 2))
array.set(_m, 1, _columns - (min(_columns, _to_column) - _from_column) - 1)
_m
//}
// function to insert an array of rows/records at a certain row number in a 2D matrix
f_matrix_insert_array_of_rows(_m, _insert_array, _from_row) =>
//{
[_rows, _columns] = f_matrix_get_size(_m)
[_rows_insert_array, _columns_insert_array] = f_matrix_get_size(_insert_array)
_size_insert_array = _columns_insert_array * _rows_insert_array
if _columns == _columns_insert_array
_insert_array_body = f_matrix_copy_slice_body(_insert_array, false)
_start_insert_index = 2 + min(_rows, _from_row) * _columns
for index = (_size_insert_array - 1) to 0
array.insert(_m, _start_insert_index, array.get(_insert_array_body, index))
array.set(_m, 0, _rows + _rows_insert_array)
_m
//}
// function to insert an array of columns at a certain column number in a 2D matrix
f_matrix_insert_array_of_columns(_m, _insert_array, _from_column) =>
//{
[_rows, _columns] = f_matrix_get_size(_m)
[_rows_insert_array, _columns_insert_array] = f_matrix_get_size(_insert_array)
_size_insert_array = _columns_insert_array * _rows_insert_array
if _rows == _rows_insert_array
for _row_nr = (_rows - 1) to 0
_start_insert_index = 2 + _row_nr * _columns + min(_columns - 1, _from_column)
for index = 0 to (_columns_insert_array - 1)
array.insert(_m,
max(_start_insert_index, 2),
array.get(_insert_array, int(2 + (_size_insert_array - 1) - index - ((_rows_insert_array - 1) - _row_nr) * _columns_insert_array)))
array.set(_m, 1, _columns + _columns_insert_array)
_m
//}
// function to append/add an array of rows/records to the bottom of a 2D matrix
f_matrix_append_array_of_rows(_m, _append_array) =>
//{
[_rows, _columns] = f_matrix_get_size(_m)
[_rows_append_array, _columns_append_array] = f_matrix_get_size(_append_array)
if _columns == _columns_append_array
_append_array_body = f_matrix_copy_slice_body(_append_array, false)
array.concat(_m, _append_array_body)
array.set(_m, 0, _rows + _rows_append_array)
_m
//}
// function to append/add an array of columns to the side of a 2D matrix
f_matrix_append_array_of_columns(_m, _append_array) =>
//{
[_rows, _columns] = f_matrix_get_size(_m)
[_rows_append_array, _columns_append_array] = f_matrix_get_size(_append_array)
_size_append_array = _columns_append_array * _rows_append_array
if _rows == _rows_append_array
for _row_nr = (_rows - 1) to 0
_start_append_index = 2 + _row_nr * _columns + _columns
for index = 0 to (_columns_append_array - 1)
array.insert(_m,
max(_start_append_index, 2),
array.get(_append_array, int(2 + (_size_append_array - 1) - index - ((_rows_append_array - 1) - _row_nr) * _columns_append_array)))
array.set(_m, 1, _columns + _columns_append_array)
_m
//}
// function to remove the last row/record from a 2D matrix
f_matrix_pop_row(_m) =>
//{
[_rows, _columns] = f_matrix_get_size(_m)
_return_array = array.new_float()
_start_remove_index = 2 + (_rows - 1) * _columns
for i = 1 to _columns
array.push(_return_array, array.get(_m, max(_start_remove_index, 2)))
array.remove(_m, max(_start_remove_index, 2))
array.set(_m, 0, _rows - 1)
_return_array
//}
// function to remove and return the last column from a 2D matrix
f_matrix_pop_column(_m) =>
//{
[_rows, _columns] = f_matrix_get_size(_m)
_return_array = array.new_float()
for _row_nr = (_rows - 1) to 0
_start_remove_index = 2 + _row_nr * _columns + (_columns - 1)
array.insert(_return_array, 0, array.get(_m, max(_start_remove_index, 2)))
array.remove(_m, max(_start_remove_index, 2))
array.set(_m, 1, _columns - 1)
_return_array
//}
//}
/// CALCULATION FUNCTIONS ///
//{
// function to randomize the values of a 2D matrix //
f_matrix_randomize(_m, _range) =>
//{
[_rows, _columns] = f_matrix_get_size(_m)
for i = 0 to _rows - 1
for j = 0 to _columns - 1
_rnd_value = f_prng(_range)
f_matrix_set(_m, i, j, _rnd_value)
_m
//}
f_matrix_replace(_m, _add_m) =>
//{
[ _rows, _columns] = f_matrix_get_size(_m)
[_add_rows, _add_columns] = f_matrix_get_size(_add_m)
if _rows == _add_rows and _columns == _add_columns
for i = 0 to _rows - 1
for j = 0 to _columns - 1
_add_value = f_matrix_get(_add_m, i, j)
f_matrix_set(_m, i, j, _add_value)
_m
//}
// function to add a certain value to every element of a 2D matrix //
f_matrix_add_value(_m, _add_value) =>
//{
[_rows, _columns] = f_matrix_get_size(_m)
_return_array = f_matrix_init(_rows, _columns, 0)
for i = 0 to _rows - 1
for j = 0 to _columns - 1
_value = f_matrix_get(_m, i, j)
f_matrix_set(_return_array, i, j, _value + _add_value)
_return_array
// [_rows, _columns] = f_matrix_get_size(_m)
// for i = 0 to _rows - 1
// for j = 0 to _columns - 1
// _value = f_matrix_get(_m, i, j)
// f_matrix_set(_m, i, j, _value + _add_value)
// _m
//}
f_matrix_addition(_m, _add_m) =>
//{
[ _rows, _columns] = f_matrix_get_size(_m)
[_add_rows, _add_columns] = f_matrix_get_size(_add_m)
_return_array = f_matrix_init(_rows, _columns, 0)
if _rows == _add_rows and _columns == _add_columns
for i = 0 to _rows - 1
for j = 0 to _columns - 1
_value = f_matrix_get( _m, i, j)
_add_value = f_matrix_get(_add_m, i, j)
f_matrix_set(_return_array, i, j, _value + _add_value)
_return_array
// [ _rows, _columns] = f_matrix_get_size(_m)
// [_add_rows, _add_columns] = f_matrix_get_size(_add_m)
// if _rows == _add_rows and _columns == _add_columns
// for i = 0 to _rows - 1
// for j = 0 to _columns - 1
// _value = f_matrix_get( _m, i, j)
// _add_value = f_matrix_get(_add_m, i, j)
// f_matrix_set(_m, i, j, _value + _add_value)
// _m
//}
f_matrix_subtraction(_m, _subtract_m) =>
//{
[ _rows, _columns] = f_matrix_get_size(_m)
[_subtract_rows, _subtract_columns] = f_matrix_get_size(_subtract_m)
_return_array = f_matrix_init(_rows, _columns, 0)
if _rows == _subtract_rows and _columns == _subtract_columns
for i = 0 to _rows - 1
for j = 0 to _columns - 1
_value = f_matrix_get( _m, i, j)
_subtract_value = f_matrix_get(_subtract_m, i, j)
f_matrix_set(_return_array, i, j, _value - _subtract_value)
_return_array
// [ _rows, _columns] = f_matrix_get_size(_m)
// [_subtract_rows, _subtract_columns] = f_matrix_get_size(_subtract_m)
// if _rows == _subtract_rows and _columns == _subtract_columns
// for i = 0 to _rows - 1
// for j = 0 to _columns - 1
// _value = f_matrix_get( _m, i, j)
// _subtract_value = f_matrix_get(_subtract_m, i, j)
// f_matrix_set(_m, i, j, _value - _subtract_value)
// _m
//}
// function to add a certain value to every element of a 2D matrix //
f_matrix_subtract_value(_m, _subtract_value) =>
//{
[_rows, _columns] = f_matrix_get_size(_m)
_return_array = f_matrix_init(_rows, _columns, 0)
for i = 0 to _rows - 1
for j = 0 to _columns - 1
_value = f_matrix_get(_m, i, j)
f_matrix_set(_return_array, i, j, _value - _subtract_value)
_return_array
// [_rows, _columns] = f_matrix_get_size(_m)
// for i = 0 to _rows - 1
// for j = 0 to _columns - 1
// _value = f_matrix_get(_m, i, j)
// f_matrix_set(_m, i, j, _value - _subtract_value)
// _m
//}
// function to scale/multiply a 2D matrix by a certain value //
f_matrix_scalar_multiply(_m, _scalar) =>
//{
[_rows, _columns] = f_matrix_get_size(_m)
_return_array = f_matrix_init(_rows, _columns, 0)
for i = 0 to _rows - 1
for j = 0 to _columns - 1
_value = f_matrix_get(_m, i, j)
f_matrix_set(_return_array, i, j, _value * _scalar)
_return_array
// [_rows, _columns] = f_matrix_get_size(_m)
// for i = 0 to _rows - 1
// for j = 0 to _columns - 1
// _value = f_matrix_get(_m, i, j)
// f_matrix_set(_m, i, j, _value * _scalar)
// _m
//}
// function to transpose a 2D matrices //
f_matrix_transpose(_m) =>
//{
[_rows, _columns] = f_matrix_get_size(_m)
_return_array = f_matrix_init(_columns, _rows, 0)
for i = 0 to _rows - 1
for j = 0 to _columns - 1
_value = f_matrix_get(_m, i, j)
f_matrix_set(_return_array, j, i, _value)
_return_array
//}
// function for ELEMENT WISE MULTIPLICATION of 2D matrices, returns matrix c //
f_matrix_mult_elem(_a, _b) =>
//{
[_rows_a, _columns_a] = f_matrix_get_size(_a)
[_rows_b, _columns_b] = f_matrix_get_size(_b)
_return_array = f_matrix_init(_rows_a, _columns_a, 0)
if _rows_a == _rows_b and _columns_a == _columns_b
for i = 0 to _rows_a - 1
for j = 0 to _columns_a - 1
_value_a = f_matrix_get( _a, i, j)
_value_b = f_matrix_get( _b, i, j)
f_matrix_set(_return_array, i, j, _value_a * + _value_b)
_return_array
// [_rows_a, _columns_a] = f_matrix_get_size(_a)
// [_rows_b, _columns_b] = f_matrix_get_size(_b)
// if _rows_a == _rows_b and _columns_a == _columns_b
// for i = 0 to _rows_a - 1
// for j = 0 to _columns_a - 1
// _value_a = f_matrix_get( _a, i, j)
// _value_b = f_matrix_get( _b, i, j)
// f_matrix_set(_a, i, j, _value_a * + _value_b)
// _a
//}
// function for DOT PROCUCT MULTIPLICATION of 2D matrices, returns matrix c //
f_matrix_mult(_a, _b) =>
//{
// get matrix metadata //
[_a_row, _a_col] = f_matrix_get_size(_a), _a_size = _a_row * _a_col
[_b_row, _b_col] = f_matrix_get_size(_b), _b_size = _b_row * _b_col
_c_row = _a_row, _c_col = _b_col , _c_size = _a_row * _b_col
_c = f_matrix_init(_a_row, _b_col, 0)
// error check //
if _a_col != _b_row
array.fill(_c, 0)
// multiplication //
else
_a_row_nr = 0, _a_col_nr = 0
_b_row_nr = 0, _b_col_nr = 0
_c_row_nr = 0, _c_col_nr = 0
for i = 0 to (_c_size -1)
i_val = 0.0
_c_row_nr := floor(i / _b_col)
// row_nr := row_nr + ((i % _b_x) == 0 ? 1 : 0)
_c_col_nr := i % _b_col
for j = 0 to (_a_col - 1)
_a_row_nr := _c_row_nr , _a_col_nr := j % _a_col
_b_row_nr := j % _a_col , _b_col_nr := _c_col_nr
i_val := i_val + f_matrix_get(_a, _a_row_nr, _a_col_nr) * f_matrix_get(_b, _b_row_nr, _b_col_nr)
array.set(_c, 2 + i, i_val)
// return //
_c
//}
// Matrix Determinants //
//{
// function to calculate the determinant of 2D matrices //
f_matrix_determinant_2x2(_m) =>
//{
[_rows, _columns] = f_matrix_get_size(_m)
var float _determinant = na
// error check //
if _rows == _columns and _rows == 2
// matrix form:
// |a b|
// det: | | = a * d - c * b
// |c d|
_a = f_matrix_get(_m, 0, 0), _b = f_matrix_get(_m, 0, 1)
_c = f_matrix_get(_m, 1, 0), _d = f_matrix_get(_m, 1, 1)
_determinant := _a * _d - _c * _b
_determinant
f_matrix_determinant_3x3(_m) =>
//{
[_rows, _columns] = f_matrix_get_size(_m)
var float _determinant = na
// error check //
if _rows == _columns and _rows == 3
// matrix form:
// |a b c|
// det: |d e f| = a * e * i + b * f * g + c * d * h - g * e * c - d * b * i - a * h * f
// |g h i|
_determinant := 0.0
for i = 0 to _columns - 1
_cofactor_matrix = array.copy(_m)
f_matrix_remove_rows( _cofactor_matrix, 0, 0)
f_matrix_remove_columns(_cofactor_matrix, i, i)
_determinant := _determinant + pow(-1, i + 1) * f_matrix_get(_m, 0, i) * f_matrix_determinant_2x2(_cofactor_matrix)
_determinant
//}
//}
f_matrix_determinant_4x4(_m) =>
//{
[_rows, _columns] = f_matrix_get_size(_m)
var float _determinant = na
// error check //
if _rows == _columns and _rows == 4
_determinant := 0.0
for i = 0 to _columns - 1
_cofactor_matrix = array.copy(_m)
f_matrix_remove_rows( _cofactor_matrix, 0, 0)
f_matrix_remove_columns(_cofactor_matrix, i, i)
_determinant := _determinant + pow(-1, i + 1) * f_matrix_get(_m, 0, i) * f_matrix_determinant_3x3(_cofactor_matrix)
_determinant
//}
// function to print matrix
f_matrix_print(_m) =>
//{
_text = "(" + tostring(array.get(_m, 0)) + "x" + tostring(array.get(_m, 1)) + ")"
_text := _text + "\n" + "\n"+"["
for i = 2 to array.size(_m) - 1
_text := _text + (i == 2 ? "" : ((i - 2) % array.get(_m, 1)) == 0 ? "]" + "\n" + "[" : " ") + tostring(array.get(_m, i))
_text := _text + "]"
//}
//}
//}
//}
//////////////////////////////
// Neural Network functions //
//////////////////////////////
//{
// function calculate the Sigmoid function //
// S(x) = (1 / (1 + e^-x)) //
//{
f_sigmoid(_x) =>
_S = 1 / (1 + exp(-1 * _x))
//}
// function calculate the inverse Sigmoid function //
// S'(x) = S(x) * (1 - S(x)) //
//{
f_inv_sigmoid(_x) =>
_sigm_x = f_sigmoid(_x)
_inv_S = _sigm_x * (1 - _sigm_x)
_inv_S
//}
// function to map a 2D matrices with a Sigmoid function //
// S(x) = (1 / (1 + e^-x)) //
f_matrix_sigmoid(_m) =>
//{
[_rows, _columns] = f_matrix_get_size(_m)
_return_array = f_matrix_init(_rows, _columns, 0)
for i = 0 to _rows - 1
for j = 0 to _columns - 1
_value = f_matrix_get(_m, i, j)
f_matrix_set(_return_array, i, j, f_sigmoid(_value))
_return_array
//}
// function to map a 2D matrices with a inverse Sigmoid function //
// S'(x) = S(x) * (1 - S(x)) //
f_matrix_inverse_sigmoid(_m) =>
//{
// inverse Sigmoid function
// S'(x) = S(x) * (1 - S(x))
// Output * (1 - Output)
[_rows, _columns] = f_matrix_get_size(_m)
_return_array = f_matrix_init(_rows, _columns, 0)
for i = 0 to _rows - 1
for j = 0 to _columns - 1
_value = f_matrix_get(_m, i, j)
f_matrix_set(_return_array, i, j, _value * (1 - _value))
_return_array
//}
// Feedforward function //
f_feedforward(_Inputs, _Weights, _Bias) =>
//{
///// Output = Input x Weights + Bias /////
_weighted_inputs = f_matrix_mult( _Weights, _Inputs)
_Output = f_matrix_addition(_weighted_inputs, _Bias)
_Output_sigm = f_matrix_sigmoid(_Output)
_Output_sigm
//}
// Backpropagation function //
f_backpropagation(_Inputs_transp, _Weights, _Bias, _Outputs, _Errors, _learning_rate) =>
//{
///// delta Bias = learning rate x Errors x (Output x (1 - Output) /////
///// delta Weights = learning rate x Errors x (Output x (1 - Output) x Input transposed /////
// learning rate x Errors
_gradient = f_matrix_scalar_multiply(_Errors, _learning_rate)
// (Output x (1 - Output)
_inv_sigmoid = f_matrix_inverse_sigmoid(_Outputs)
// delta Bias = learning rate x Errors x (Output x (1 - Output)
_delta_bias = f_matrix_mult_elem(_gradient, _inv_sigmoid)
_Bias_adj = f_matrix_addition( _Bias, _delta_bias)
f_matrix_replace(_Bias, _Bias_adj)
// delta Weights = learning rate x Errors x (Output x (1 - Output) x Input transposed
_delta_weights = f_matrix_mult( _delta_bias, _Inputs_transp)
_Weights_adj = f_matrix_addition(_Weights, _delta_weights)
f_matrix_replace(_Weights, _Weights_adj)
//}
//}
//}
/////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////
/// Main Script ///
/// ///
/// (inspired by "The Coding Train 10. Neural Networks - The Nature of Code") ///
/////////////////////////////////////////////////////////////////////////////////
//{
// Inputs //
training_loop = input(50, title = "Number of Training loops", minval = 1)
learning_rate = input(0.1, title = "Learning rate", minval = 0.001, maxval = 1)
//////////////////////////////////////////////////////////////
// Training Data //
//////////////////////////////////////////////////////////////
// Demo model of XOR gate containing: //
// 4 rows of data //
// with 2 columns of input data and 1 column of result data //
//////////////////////////////////////////////////////////////
// NETWORK SIZE //
// Number of nodes //
/////////////////////////////////////
Input_nodes = 2 //
// //
Hidden1_nodes = 4 //
// //
Output_nodes = 2 //
/////////////////////////////////////
// column names //
input1 = 0, input2 = 1, result = 2 //
/////////////////////////////////////
Training_data_rows = 4, Training_data_columns = 3
var float[] Training_data = f_matrix_init(Training_data_rows, Training_data_columns, 0)
// set inputs
f_matrix_set(Training_data, 0, input1, 0), f_matrix_set(Training_data, 0, input2, 0), f_matrix_set(Training_data, 0, result, 0)
f_matrix_set(Training_data, 1, input1, 0), f_matrix_set(Training_data, 1, input2, 1), f_matrix_set(Training_data, 1, result, 1)
f_matrix_set(Training_data, 2, input1, 1), f_matrix_set(Training_data, 2, input2, 0), f_matrix_set(Training_data, 2, result, 1)
f_matrix_set(Training_data, 3, input1, 1), f_matrix_set(Training_data, 3, input2, 1), f_matrix_set(Training_data, 3, result, 0)
/////////////////////
/// Input vector ///
/////////////////////
Inputs = f_matrix_init(Input_nodes, 1, 0)
///////////////////////////
/// Hidden nodes vector ///
///////////////////////////
// Set hidden matrix
var float[] Hidden1 = f_matrix_init(Hidden1_nodes, 1, 0)
Hidden1_transp = f_matrix_transpose(Hidden1)
///////////////////////////
/// Hidden Error vector ///
///////////////////////////
var float[] Errors_hidden = f_matrix_init(Hidden1_nodes, 1, 0)
/////////////////////
/// Output vector ///
/////////////////////
var float[] Output = f_matrix_init(Output_nodes, 1, 0)
/////////////////////
/// Target Matrix ///
/////////////////////
var float[] Targets = f_matrix_init(Output_nodes, 1, 0)
///////////////////////////
/// Output Error vector ///
///////////////////////////
var float[] Errors = f_matrix_init(Output_nodes, 1, 0)
//////////////////////////////////////////////////
/// Weights and Biases Inputs to Hidden matrix ///
//////////////////////////////////////////////////
// set weights matrix and bias vector
var float[] Weights_I_to_H = f_matrix_init(Hidden1_nodes, Input_nodes, 0)
var float[] Bias_I_to_H = f_matrix_init(Hidden1_nodes, 1, 0)
// set weights to random values (0, 1)
if barstate.isfirst
f_matrix_randomize(Weights_I_to_H, 1)
f_matrix_randomize(Bias_I_to_H, 1)
///////////////////////////////////////////////////
/// Weights and Biases Hidden to Outputs matrix ///
///////////////////////////////////////////////////
// set weights matrix and bias vector
var float[] Weights_H_to_O = f_matrix_init(Output_nodes, Hidden1_nodes, 0)
var float[] Bias_H_to_O = f_matrix_init(Output_nodes, 1, 0)
// set weights to random values
if barstate.isfirst
f_matrix_randomize(Weights_H_to_O, 1)
f_matrix_randomize( Bias_H_to_O, 1)
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
/// TRAINING CALCULATIONS ///
//////////////////////////////////////////////////////////////
for i = 1 to training_loop
//////////////////////////////////////////
// SELECT RANDOM TRAINING DATA FROM SET //
//////////////////////////////////////////
// set inputs to random row from training
random_row = int(min(Training_data_rows, f_prng(4)))
input_data1 = f_matrix_get(Training_data, random_row, input1)
input_data2 = f_matrix_get(Training_data, random_row, input2)
f_matrix_set(Inputs, input1, 0, input_data1)
f_matrix_set(Inputs, input2, 0, input_data2)
// transpose Input vector
Inputs_transp = f_matrix_transpose(Inputs)
// set targets to random row from training
result_data = f_matrix_get(Training_data, random_row, result)
f_matrix_set(Targets, 0, 0, result_data)
f_matrix_set(Targets, 0, 0, 1 - result_data)
/////////////////
// FEEDFORWARD //
/////////////////
// calculate output vector
Hidden1 := f_feedforward(Inputs, Weights_I_to_H, Bias_I_to_H)
Output := f_feedforward(Hidden1, Weights_H_to_O, Bias_H_to_O)
/////////////////////
// BACKPROPAGATION //
/////////////////////
Hidden1_transp := f_matrix_transpose(Hidden1)
Weights_H_to_O_transp = f_matrix_transpose(Weights_H_to_O)
// calculate error vector
Errors := f_matrix_subtraction(Targets, Output)
Errors_hidden := f_matrix_mult(Weights_H_to_O_transp, Errors)
// // adjust Weights and Biases
f_backpropagation(Hidden1_transp, Weights_H_to_O, Bias_H_to_O, Output, Errors, learning_rate)
f_backpropagation( Inputs_transp, Weights_I_to_H, Bias_I_to_H, Hidden1, Errors_hidden, learning_rate)
//////////////////////////////////////////////////////////////
// DEBUG PLOTS //
//{
if barstate.islast
var label lbl_Training_data = na, label.delete(lbl_Training_data)
lbl_Training_data := label.new(bar_index - 110, 0, text = "Training_data = " + f_matrix_print(Training_data), color = color.gray, textalign = text.align_left)
var label lbl_Inputs = na, label.delete(lbl_Inputs)
lbl_Inputs := label.new(bar_index - 100, 0, text = "random Inputs = " + f_matrix_print(Inputs), color = color.gray, textalign = text.align_left)
var label lbl_Targets = na, label.delete(lbl_Targets)
lbl_Targets := label.new(bar_index - 10, 0, text = "Targets = " + f_matrix_print(Targets), color = color.gray, textalign = text.align_left)
var label lbl_Weights_I_to_H = na, label.delete(lbl_Weights_I_to_H)
lbl_Weights_I_to_H := label.new(bar_index - 90, 0, text = "Weights_I_to_H = " + f_matrix_print(Weights_I_to_H), color = color.gray, textalign = text.align_left)
var label lbl_Bias_I_to_H = na, label.delete(lbl_Bias_I_to_H)
lbl_Bias_I_to_H := label.new(bar_index - 80, 0, text = "Bias_I_to_H = " + f_matrix_print(Bias_I_to_H), color = color.gray, textalign = text.align_left)
var label lbl_Hidden1 = na, label.delete(lbl_Hidden1)
lbl_Hidden1 := label.new(bar_index - 70, 0, text = "Hidden1 = " + f_matrix_print(Hidden1), color = color.gray, textalign = text.align_left)
var label lbl_Weights_H_to_O = na, label.delete(lbl_Weights_H_to_O)
lbl_Weights_H_to_O := label.new(bar_index - 50, 0, text = "Weights_H_to_O = " + f_matrix_print(Weights_H_to_O), color = color.gray, textalign = text.align_left)
var label lbl_Bias_H_to_O = na, label.delete(lbl_Bias_H_to_O)
lbl_Bias_H_to_O := label.new(bar_index - 40, 0, text = "Bias_H_to_O = " + f_matrix_print(Bias_H_to_O), color = color.gray, textalign = text.align_left)
var label lbl_Output = na, label.delete(lbl_Output)
lbl_Output := label.new(bar_index - 30, 0, text = "Output = " + f_matrix_print(Output), color = color.gray, textalign = text.align_left)
var label lbl_Errors = na, label.delete(lbl_Errors)
lbl_Errors := label.new(bar_index - 20, 0, text = "Errors = " + f_matrix_print(Errors), textcolor = color.black, textalign = text.align_left)
var label lbl_Errors_hidden = na, label.delete(lbl_Errors_hidden)
lbl_Errors_hidden := label.new(bar_index - 60, 0, text = "hidden Errors = " + f_matrix_print(Errors_hidden), textcolor = color.black, textalign = text.align_left)
plot(f_matrix_get(Errors, 0, 0), color = color.fuchsia)
plot((f_matrix_get(Inputs, 0, 0) + f_matrix_get(Inputs, 1, 0)) == 1 ? 1 : 0, color = color.yellow)
plot(f_matrix_get(Output, 0, 0), color = color.green)
//}
Show all 945 lines