diff options
| author | Your Name <[email protected]> | 2026-08-28 17:29:33 -0700 |
|---|---|---|
| committer | Your Name <[email protected]> | 2026-08-28 17:29:33 -0700 |
| commit | 02158c7613495f1a02a2a8f108cd1c397656ce2b (patch) | |
| tree | 5cdd0d716b0ef6ae98375354d952c4cac1670846 | |
| parent | 2a3cb69b71539953489752cea5f34e87cdb7814a (diff) | |
cleaned up
| -rwxr-xr-x | bin/benchmark | bin | 288832 -> 294352 bytes | |||
| -rw-r--r-- | build/benchmark.o | bin | 35080 -> 35184 bytes | |||
| -rw-r--r-- | build/crossbar_generator.o | bin | 20472 -> 20568 bytes | |||
| -rw-r--r-- | build/online_crossbar.o | bin | 29384 -> 30760 bytes | |||
| -rw-r--r-- | build/read_crossbar.o | bin | 15472 -> 16200 bytes | |||
| -rw-r--r-- | build/spires_interface.o | bin | 12216 -> 14176 bytes | |||
| -rw-r--r-- | src/benchmark.c | 162 | ||||
| -rw-r--r-- | src/benchmark.h | 2 | ||||
| -rw-r--r-- | src/crossbar_generator.c | 140 | ||||
| -rw-r--r-- | src/crossbar_generator.h | 29 | ||||
| -rw-r--r-- | src/online_crossbar.c | 161 | ||||
| -rw-r--r-- | src/online_crossbar.h | 1 | ||||
| -rw-r--r-- | src/read_crossbar.c | 113 | ||||
| -rw-r--r-- | src/read_crossbar.h | 12 | ||||
| -rw-r--r-- | src/spires_interface.c | 96 | ||||
| -rw-r--r-- | src/spires_interface.h | 7 |
16 files changed, 427 insertions, 296 deletions
diff --git a/bin/benchmark b/bin/benchmark Binary files differindex c7996b2..36680a6 100755 --- a/bin/benchmark +++ b/bin/benchmark diff --git a/build/benchmark.o b/build/benchmark.o Binary files differindex 8d29915..906f19b 100644 --- a/build/benchmark.o +++ b/build/benchmark.o diff --git a/build/crossbar_generator.o b/build/crossbar_generator.o Binary files differindex c654c93..23dcbf9 100644 --- a/build/crossbar_generator.o +++ b/build/crossbar_generator.o diff --git a/build/online_crossbar.o b/build/online_crossbar.o Binary files differindex cfcf3e1..dae53a4 100644 --- a/build/online_crossbar.o +++ b/build/online_crossbar.o diff --git a/build/read_crossbar.o b/build/read_crossbar.o Binary files differindex 7d280dc..6c86243 100644 --- a/build/read_crossbar.o +++ b/build/read_crossbar.o diff --git a/build/spires_interface.o b/build/spires_interface.o Binary files differindex b12254e..986d773 100644 --- a/build/spires_interface.o +++ b/build/spires_interface.o diff --git a/src/benchmark.c b/src/benchmark.c index d3427e4..b6410e3 100644 --- a/src/benchmark.c +++ b/src/benchmark.c @@ -1,12 +1,13 @@ #include "benchmark.h" #include "crossbar_generator.h" +#include "online_crossbar.h" #include "read_crossbar.h" #include "spires_interface.h" -#include "online_crossbar.h" #include <math.h> #include <plplot/plplot.h> #include <spires.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -21,8 +22,14 @@ int run_online_benchmark(const spires_reservoir_config *config, double *predictions_out) { if (!config || !reservoir || !input_series || num_timesteps == 0 || - !model_path || !subcircuit_name || !predictions_out) + config->num_neurons == 0 || config->num_inputs == 0 || + config->num_outputs == 0 || !model_path || !subcircuit_name || + !predictions_out) { return -1; + } + if (config->num_neurons > SIZE_MAX / sizeof(double)) { + return -1; + } Online_Crossbar_Config online_config = { .num_neurons = config->num_neurons, @@ -39,8 +46,9 @@ int run_online_benchmark(const spires_reservoir_config *config, Online_Crossbar *crossbar = NULL; double *state = malloc(config->num_neurons * sizeof(*state)); - if (!state) + if (!state) { return -1; + } if (spires_reservoir_reset(reservoir) != SPIRES_OK || online_crossbar_init(&online_config, reservoir, &crossbar) != 0 || online_crossbar_start(crossbar) != 0) { @@ -53,15 +61,19 @@ int run_online_benchmark(const spires_reservoir_config *config, const double *input = input_series + timestep * config->num_inputs; int output_ready = 0; - double *previous = timestep == 0 - ? NULL - : predictions_out + - (timestep - 1) * config->num_outputs; + double *previous = + timestep == 0 ? NULL + : predictions_out + + (timestep - 1) * config->num_outputs; if (spires_step(reservoir, input) != SPIRES_OK || - spires_read_reservoir_state(reservoir, state) != SPIRES_OK || + spires_read_reservoir_state(reservoir, state) != + SPIRES_OK || online_crossbar_submit(crossbar, timestep, state, previous, &output_ready) != 0 || output_ready != (timestep != 0)) { + fprintf(stderr, + "Online benchmark failed at timestep %zu\n", + timestep); free(state); online_crossbar_destroy(crossbar); return -1; @@ -69,8 +81,8 @@ int run_online_benchmark(const spires_reservoir_config *config, } int status = online_crossbar_finish( - crossbar, predictions_out + - (num_timesteps - 1) * config->num_outputs); + crossbar, + predictions_out + (num_timesteps - 1) * config->num_outputs); free(state); online_crossbar_destroy(crossbar); return status; @@ -81,26 +93,38 @@ int run_benchmark(const spires_reservoir_config *config, Reservoir_State_Matrix *state_matrix, const char *model_path, const char *subcircuit_name, double *predictions_out) { - // copy readout weights and convert to conductances + if (!config || !reservoir || !state_matrix || !state_matrix->states || + state_matrix->num_samples == 0 || state_matrix->num_features == 0 || + state_matrix->num_features != config->num_neurons || + config->num_outputs == 0 || !model_path || !subcircuit_name || + !predictions_out) { + return -1; + } + if (state_matrix->num_samples > SIZE_MAX / state_matrix->num_features || + state_matrix->num_samples * state_matrix->num_features > + SIZE_MAX / sizeof(double)) { + return -1; + } + double *initial_resistances = NULL; + double *row_voltages = NULL; + Crossbar_Output_Matrix crossbar_output = {0}; conductance_mapping mapping; + int status = -1; if (convert_weights_to_resistances( reservoir, config->num_neurons, config->num_outputs, 1000.0, 100000.0, &initial_resistances, &mapping) != 0) { - return -1; + goto cleanup; } - double *row_voltages = + row_voltages = malloc(state_matrix->num_features * state_matrix->num_samples * - sizeof(double)); + sizeof(*row_voltages)); - if (row_voltages == NULL) { - fprintf(stderr, "Failed to allocate spikes voltages"); - free(initial_resistances); - free_reservoir_state_matrix(state_matrix); - spires_reservoir_destroy(reservoir); - return -1; + if (!row_voltages) { + fprintf(stderr, "Failed to allocate row voltages\n"); + goto cleanup; } for (size_t sample = 0; sample < state_matrix->num_samples; sample++) { @@ -125,60 +149,49 @@ int run_benchmark(const spires_reservoir_config *config, .subcircuit_name = subcircuit_name, .time_step = 1e-6, .stop_time = state_matrix->num_samples * 1e-6, - .print_state_nodes = 0}; // state nodes is not acutally implemented + .print_state_nodes = 0}; if (generate_crossbar("output/crossbar.cir", &crossbar_config) < 0) { - fprintf(stderr, "failed to create crossbar config"); - free(initial_resistances); - return -1; + fprintf(stderr, "Failed to generate crossbar netlist\n"); + goto cleanup; } - printf("Generated crossbar!!"); - // call ngspice for crossbar if (run_ngspice("output/crossbar.cir") < 0) { - fprintf(stderr, "Failed to run_ngspice"); - free(initial_resistances); - return -1; + fprintf(stderr, "Failed to run ngspice\n"); + goto cleanup; } - printf("ran ngspice!!"); - - // crossbar parameters needed for reading - Crossbar_Output_Matrix crossbar_output = { - // .num_samples = state_matrix->num_samples, - // .num_outputs = config->num_outputs * 2, - .time = NULL, - .voltages = NULL}; if (read_crossbar("output/crossbar_output.dat", config->num_outputs * 2, &crossbar_output) < 0) { - fprintf(stderr, "Failed to read crossbar output file"); - free(initial_resistances); - free_crossbar_output_matrix(&crossbar_output); - return -1; + fprintf(stderr, "Failed to read crossbar output\n"); + goto cleanup; } - printf("read the crossbar outputs!!\n"); if (convert_output_to_software( config->num_neurons, config->num_outputs, state_matrix->num_samples, crossbar_output.voltages, initial_resistances, crossbar_config.load_resistance, &mapping, row_voltages, SPIKE_AMPLITUDE, predictions_out) < 0) { - fprintf(stderr, - "Failed to convert crossbar outputs back to software"); - return -1; + fprintf(stderr, "Failed to decode crossbar output\n"); + goto cleanup; } - printf("YAY IT WORKED!!! Cleaning up :)\n"); + status = 0; + +cleanup: free(initial_resistances); free(row_voltages); free_crossbar_output_matrix(&crossbar_output); - - return 0; + return status; } double calculate_MSE(const double *expected, const double *predicted, const size_t num_steps, const size_t num_outputs) { + if (!expected || !predicted || num_steps == 0 || num_outputs == 0) { + return NAN; + } + double aggregate = 0.0; for (size_t output = 0; output < num_outputs; output++) { for (size_t timestep = 0; timestep < num_steps; timestep++) { @@ -197,15 +210,11 @@ double calculate_MSE(const double *expected, const double *predicted, int plot_raster(const Reservoir_State_Matrix *matrix, const size_t neurons_to_plot, const double spike_threshold) { - if (!matrix || !matrix->states || matrix->num_samples == 0) { + if (!matrix || !matrix->states || matrix->num_samples == 0 || + matrix->num_features == 0 || neurons_to_plot == 0 || + neurons_to_plot > matrix->num_features) { return -1; } - - // if (neurons_to_plot > matrix->num_features) { - // neurons_to_plot = matrix->num_features; - // } - - // count spikes size_t spike_count = 0; for (size_t t = 0; t < matrix->num_samples; t++) { for (size_t n = 0; n < neurons_to_plot; n++) { @@ -231,7 +240,6 @@ int plot_raster(const Reservoir_State_Matrix *matrix, return -1; } - // fill spike coordinates size_t k = 0; for (size_t t = 0; t < matrix->num_samples; t++) { for (size_t n = 0; n < neurons_to_plot; n++) { @@ -293,9 +301,10 @@ int plot_reservoir_predictions(const double *expected, const double *predicted, PLFLT y_min; PLFLT y_max; - if (!expected || !predicted || num_samples == 0 || - output_to_plot >= num_outputs) + if (!expected || !predicted || !model_path || num_samples == 0 || + num_outputs == 0 || output_to_plot >= num_outputs) { return -1; + } x = malloc(num_samples * sizeof(*x)); y_expected = malloc(num_samples * sizeof(*y_expected)); @@ -320,17 +329,21 @@ int plot_reservoir_predictions(const double *expected, const double *predicted, y_expected[sample] = (PLFLT)expected[index]; y_predicted[sample] = (PLFLT)predicted[index]; - if (y_expected[sample] < y_min) + if (y_expected[sample] < y_min) { y_min = y_expected[sample]; + } - if (y_expected[sample] > y_max) + if (y_expected[sample] > y_max) { y_max = y_expected[sample]; + } - if (y_predicted[sample] < y_min) + if (y_predicted[sample] < y_min) { y_min = y_predicted[sample]; + } - if (y_predicted[sample] > y_max) + if (y_predicted[sample] > y_max) { y_max = y_predicted[sample]; + } } { @@ -338,8 +351,9 @@ int plot_reservoir_predictions(const double *expected, const double *predicted, margin = (y_max - y_min) * 0.1; - if (margin == 0.0) + if (margin == 0.0) { margin = 1.0; + } y_min -= margin; y_max += margin; @@ -347,8 +361,6 @@ int plot_reservoir_predictions(const double *expected, const double *predicted, plsdev("svg"); - // parses real model name - // written by chatGPT char filename[256]; char model_name[128]; const char *base; @@ -361,8 +373,9 @@ int plot_reservoir_predictions(const double *expected, const double *predicted, dot = strrchr(base, '.'); len = dot ? (size_t)(dot - base) : strlen(base); - if (len >= sizeof(model_name)) + if (len >= sizeof(model_name)) { len = sizeof(model_name) - 1; + } memcpy(model_name, base, len); model_name[len] = '\0'; @@ -371,6 +384,9 @@ int plot_reservoir_predictions(const double *expected, const double *predicted, "output/reservoir_prediction_%s.svg", model_name) >= (int)sizeof(filename)) { fprintf(stderr, "Output filename is too long\n"); + free(x); + free(y_expected); + free(y_predicted); return -1; } @@ -433,8 +449,9 @@ int plot_model_delta(const double *fixed, const double *model, size_t len; if (!fixed || !model || !model_path || num_samples == 0 || - output_to_plot >= num_outputs) + num_outputs == 0 || output_to_plot >= num_outputs) { return -1; + } x = malloc(num_samples * sizeof(*x)); delta = malloc(num_samples * sizeof(*delta)); @@ -453,14 +470,16 @@ int plot_model_delta(const double *fixed, const double *model, x[sample] = (PLFLT)sample; delta[sample] = (PLFLT)fabs(model[index] - fixed[index]); - abs_delta = (PLFLT)fabs(delta[sample]); + abs_delta = delta[sample]; - if (abs_delta > max_abs_delta) + if (abs_delta > max_abs_delta) { max_abs_delta = abs_delta; + } } - if (max_abs_delta == 0.0) + if (max_abs_delta == 0.0) { max_abs_delta = 1.0e-6; + } max_abs_delta *= 1.1; @@ -470,8 +489,9 @@ int plot_model_delta(const double *fixed, const double *model, dot = strrchr(base, '.'); len = dot ? (size_t)(dot - base) : strlen(base); - if (len >= sizeof(model_name)) + if (len >= sizeof(model_name)) { len = sizeof(model_name) - 1; + } memcpy(model_name, base, len); model_name[len] = '\0'; diff --git a/src/benchmark.h b/src/benchmark.h index c7ffab6..8c37a9e 100644 --- a/src/benchmark.h +++ b/src/benchmark.h @@ -1,8 +1,6 @@ #ifndef BENCHMARK_H #define BENCHMARK_H -#include "crossbar_generator.h" -#include "read_crossbar.h" #include "spires_interface.h" #include <stddef.h> diff --git a/src/crossbar_generator.c b/src/crossbar_generator.c index 4bf6746..2633230 100644 --- a/src/crossbar_generator.c +++ b/src/crossbar_generator.c @@ -3,47 +3,19 @@ static int validate_config(const Crossbar_Config *config) { - printf("validating configs...\n"); - if (config == NULL) { - return -1; - } - if (config->rows == 0) { - return -1; - } - if (config->columns == 0) { - return -1; - } - if (config->input_series == NULL) { - return -1; - } - if (config->num_samples == 0) { - return -1; - } - if (config->initial_resistance == NULL) { - return -1; - } - if (config->model_path == NULL) { - return -1; - } - if (config->subcircuit_name == NULL) { - return -1; - } - if (config->load_resistance <= 0.0) { - return -1; - } - if (config->time_step <= 0.0) { - return -1; - } - if (config->stop_time <= 0.0) { - return -1; - } - if (config->print_state_nodes != 0 && config->print_state_nodes != 1) { + if (!config || config->rows == 0 || config->columns == 0 || + !config->input_series || config->num_samples == 0 || + !config->initial_resistance || !config->model_path || + !config->subcircuit_name || config->load_resistance <= 0.0 || + config->time_step <= 0.0 || config->stop_time <= 0.0 || + (config->print_state_nodes != 0 && + config->print_state_nodes != 1)) { return -1; } + return 0; } -// thus starts fprintf hell... static int write_header(FILE *file, const Crossbar_Config *config) { if (fprintf(file, @@ -51,13 +23,12 @@ static int write_header(FILE *file, const Crossbar_Config *config) "*Rows: %zu\n" "*Columns: %zu\n", config->rows, config->columns) < 0) { - fprintf(stderr, "Failed to write header at crossbar size"); + fprintf(stderr, "Failed to write crossbar header\n"); return -1; } - // include the memristor model if (fprintf(file, "\n.include \"%s\"\n", config->model_path) < 0) { - fprintf(stderr, "Faile to write header at .include memristor"); + fprintf(stderr, "Failed to write model include\n"); return -1; } @@ -66,35 +37,42 @@ static int write_header(FILE *file, const Crossbar_Config *config) static int write_input_series(FILE *file, const Crossbar_Config *config) { - fprintf(file, "\n* SPIRES reservoir states\n"); + if (fprintf(file, "\n* SPIRES reservoir states\n") < 0) { + return -1; + } for (size_t row = 0; row < config->rows; row++) { - fprintf(file, "VROW%zu row%zu 0 PWL(", row, row); + if (fprintf(file, "VROW%zu row%zu 0 PWL(", row, row) < 0) { + return -1; + } for (size_t sample = 0; sample < config->num_samples; sample++) { double time = sample * config->time_step; - double state = + double voltage = config->input_series[sample * config->rows + row]; - - double voltage = state; - - fprintf(file, "%.17g %.17g ", time, voltage); + if (fprintf(file, "%.17g %.17g ", time, voltage) < 0) { + return -1; + } + } + if (fprintf(file, ")\n") < 0) { + return -1; } - fprintf(file, ")\n"); } - return ferror(file) ? -1 : 0; + return 0; } static int write_external_inputs(FILE *file, const Crossbar_Config *config) { - if (fprintf(file, "\n* Online SPIRES reservoir states\n") < 0) + if (fprintf(file, "\n* Online SPIRES reservoir states\n") < 0) { return -1; + } for (size_t row = 0; row < config->rows; row++) { if (fprintf(file, "VROW%zu row%zu 0 dc 0 external\n", row, - row) < 0) + row) < 0) { return -1; + } } return 0; } @@ -102,14 +80,12 @@ static int write_external_inputs(FILE *file, const Crossbar_Config *config) static int write_memristor_array(FILE *file, const Crossbar_Config *config) { if (fprintf(file, "\n* Memristor Array\n") < 0) { - fprintf(stderr, - "Failed to write memristor array comment label thing"); + fprintf(stderr, "Failed to write memristor array header\n"); return -1; } for (size_t row = 0; row < config->rows; row++) { for (size_t column = 0; column < config->columns; column++) { - // initial_resistance here is done wrong, need to fix if (fprintf(file, "X%zu%zu row%zu col%zu %s" " PARAMS: Rinit=%.12g\n", @@ -118,7 +94,7 @@ static int write_memristor_array(FILE *file, const Crossbar_Config *config) config->initial_resistance [row * config->columns + column]) < 0) { fprintf(stderr, - "Failed to write memristor array"); + "Failed to write memristor array\n"); return -1; } } @@ -129,14 +105,14 @@ static int write_memristor_array(FILE *file, const Crossbar_Config *config) static int write_column_loads(FILE *file, const Crossbar_Config *config) { if (fprintf(file, "\n* Column Loads\n") < 0) { - fprintf(stderr, - "Failed to write column loads comment label thing"); + fprintf(stderr, "Failed to write column-load header\n"); + return -1; } for (size_t column = 0; column < config->columns; column++) { if (fprintf(file, "RLOAD%zu col%zu 0 %.0f\n", column, column, config->load_resistance) < 0) { - fprintf(stderr, "Failed to write column loads"); + fprintf(stderr, "Failed to write column loads\n"); return -1; } } @@ -146,37 +122,44 @@ static int write_column_loads(FILE *file, const Crossbar_Config *config) static int write_simulation(FILE *file, const Crossbar_Config *config) { if (fprintf(file, "\n* Simulation\n") < 0) { - fprintf(stderr, "Failed to write simulation header"); + fprintf(stderr, "Failed to write simulation header\n"); return -1; } if (fprintf(file, ".tran %.17g %.17g uic\n.control\nrun\n", config->time_step, config->stop_time) < 0) { - fprintf(stderr, "Failed to write simulation command"); + fprintf(stderr, "Failed to write simulation command\n"); return -1; } return 0; } -static int write_save_date(FILE *file, const Crossbar_Config *config) +static int write_save_data(FILE *file, const Crossbar_Config *config) { - fprintf(file, "linearize"); - for (int column = 0; column < config->columns; column++) { + if (fprintf(file, "linearize") < 0) { + return -1; + } + for (size_t column = 0; column < config->columns; column++) { if (fprintf(file, " v(col%zu)", column) < 0) { - fprintf(stderr, "Failed to write linearize statements"); + fprintf(stderr, + "Failed to write linearize statement\n"); return -1; } } - fprintf(file, "\nwrdata output/crossbar_output.dat"); - for (int column = 0; column < config->columns; column++) { - fprintf(file, " v(col%zu)", - column); // ive given up on error checking - // Im doing too much of it + if (fprintf(file, "\nwrdata output/crossbar_output.dat") < 0) { + return -1; + } + for (size_t column = 0; column < config->columns; column++) { + if (fprintf(file, " v(col%zu)", column) < 0) { + fprintf(stderr, + "Failed to write output-data statement\n"); + return -1; + } } if (fprintf(file, "\n.endc\n.end") < 0) { - fprintf(stderr, "Failed to write end statements"); + fprintf(stderr, "Failed to write closing statements\n"); return -1; } return 0; @@ -185,15 +168,15 @@ static int write_save_date(FILE *file, const Crossbar_Config *config) int generate_crossbar(const char *output_filename, const Crossbar_Config *config) { - if (validate_config(config) < 0) { - fprintf(stderr, "invalid config"); + if (!output_filename || validate_config(config) < 0) { + fprintf(stderr, "Invalid crossbar configuration\n"); return -1; } FILE *file = fopen(output_filename, "w"); if (file == NULL) { - fprintf(stderr, "could not create file"); + fprintf(stderr, "Could not create crossbar netlist\n"); return -1; } @@ -202,14 +185,14 @@ int generate_crossbar(const char *output_filename, write_memristor_array(file, config) != 0 || write_column_loads(file, config) != 0 || write_simulation(file, config) != 0 || - write_save_date(file, config) != 0) { + write_save_data(file, config) != 0) { fclose(file); return -1; } if (fclose(file) != 0) { - fprintf(stderr, "failed to close output file"); + fprintf(stderr, "Failed to close crossbar netlist\n"); return -1; } return 0; @@ -218,14 +201,14 @@ int generate_crossbar(const char *output_filename, int generate_online_crossbar(const char *output_filename, const Crossbar_Config *config) { - if (validate_config(config) < 0) { - fprintf(stderr, "invalid config"); + if (!output_filename || validate_config(config) < 0) { + fprintf(stderr, "Invalid online crossbar configuration\n"); return -1; } FILE *file = fopen(output_filename, "w"); if (file == NULL) { - fprintf(stderr, "could not create online crossbar file"); + fprintf(stderr, "Could not create online crossbar netlist\n"); return -1; } @@ -247,7 +230,8 @@ int generate_online_crossbar(const char *output_filename, } } - if (fprintf(file, "\n.end\n") < 0 || fclose(file) != 0) + if (fprintf(file, "\n.end\n") < 0 || fclose(file) != 0) { return -1; + } return 0; } diff --git a/src/crossbar_generator.h b/src/crossbar_generator.h index 8fc754b..b76179b 100644 --- a/src/crossbar_generator.h +++ b/src/crossbar_generator.h @@ -4,33 +4,38 @@ #include <stddef.h> typedef struct { - size_t rows; // number of neurons - size_t columns; // number of outputs + /* Physical crossbar dimensions. */ + size_t rows; + size_t columns; - // array of row input voltages. - // length must equal rows (# of rows) + /* + * Flat row-major input voltages. Offline generation reads + * input_series[sample * rows + row] for num_samples samples. Online + * generation uses external voltage sources and does not read the values. + */ const double *input_series; size_t num_samples; - // array of initial resistances - // stored in row-major order - // size must be rows * colums + /* + * Flat row-major initial resistances with rows * columns elements: + * initial_resistance[row * columns + column]. + */ const double *initial_resistance; - // path to the memory component path + /* Path to the SPICE model included by the generated netlist. */ const char *model_path; - // name of subcircuit(specific model) + /* Name of the model's SPICE subcircuit. */ const char *subcircuit_name; - // resistance from each column to ground + /* Resistance from each physical column to ground. */ double load_resistance; - // transient sim settings + /* Transient-analysis sample interval and stop time. */ double time_step; double stop_time; - // 1 = true | 0 = false + /* Reserved for state-node output; currently must be 0 or 1. */ int print_state_nodes; } Crossbar_Config; diff --git a/src/online_crossbar.c b/src/online_crossbar.c index b01ea19..ff346bd 100644 --- a/src/online_crossbar.c +++ b/src/online_crossbar.c @@ -8,6 +8,7 @@ #include <ngspice/sharedspice.h> #include <pthread.h> #include <stdbool.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -32,14 +33,41 @@ struct Online_Crossbar { pthread_cond_t progress; }; +static int config_is_valid(const Online_Crossbar_Config *config) +{ + if (!config || config->num_neurons == 0 || config->num_outputs == 0 || + config->num_timesteps == 0 || !isfinite(config->time_step) || + config->time_step <= 0.0 || !isfinite(config->spike_amplitude) || + config->spike_amplitude == 0.0 || + !isfinite(config->load_resistance) || + config->load_resistance <= 0.0 || !isfinite(config->r_on) || + !isfinite(config->r_off) || config->r_on <= 0.0 || + config->r_off <= config->r_on || !config->model_path || + !config->subcircuit_name || !config->netlist_path) { + return 0; + } + + if (config->num_outputs > SIZE_MAX / 2 || + config->num_neurons > SIZE_MAX / 2 || + config->num_timesteps > SIZE_MAX / config->num_outputs || + config->num_timesteps * config->num_outputs > + SIZE_MAX / sizeof(double)) { + return 0; + } + + double stop_time = config->num_timesteps * config->time_step; + return isfinite(stop_time) && stop_time > 0.0; +} + static int callback_text(char *text, int ident, void *user_data) { (void)ident; (void)user_data; if (text && (strncmp(text, "stderr", 6) == 0 || strstr(text, "Error") != NULL || - strstr(text, "error") != NULL)) + strstr(text, "error") != NULL)) { fprintf(stderr, "ngspice: %s\n", text); + } return 0; } @@ -77,12 +105,14 @@ static int callback_background(NG_BOOL running, int ident, void *user_data) (void)ident; Online_Crossbar *crossbar = user_data; /* libngspice passes false at worker start and true at worker exit. */ - if (!running) + if (!running) { return 0; + } pthread_mutex_lock(&crossbar->mutex); crossbar->simulation_done = 1; - if (crossbar->result_count < crossbar->config.num_timesteps) + if (crossbar->result_count < crossbar->config.num_timesteps) { fail_locked(crossbar); + } pthread_cond_broadcast(&crossbar->progress); pthread_cond_broadcast(&crossbar->state_available); pthread_mutex_unlock(&crossbar->mutex); @@ -92,28 +122,33 @@ static int callback_background(NG_BOOL running, int ident, void *user_data) static int row_from_name(const char *name, size_t *row_out) { const char *digits = name + strlen(name); - while (digits > name && isdigit((unsigned char)digits[-1])) + while (digits > name && isdigit((unsigned char)digits[-1])) { digits--; - if (*digits == '\0') + } + if (*digits == '\0') { return -1; + } char *end = NULL; unsigned long value = strtoul(digits, &end, 10); - if (end == digits || *end != '\0') + if (end == digits || *end != '\0') { return -1; + } *row_out = (size_t)value; return 0; } static size_t timestep_for_time(const Online_Crossbar *crossbar, double time) { - if (time <= 0.0) + if (time <= 0.0) { return 0; + } double scaled = time / crossbar->config.time_step; /* Hold state[t] over the full interval (t*dt, (t+1)*dt]. */ double interval = ceil(scaled - 1.0e-9); size_t timestep = interval <= 1.0 ? 0 : (size_t)interval - 1; - if (timestep >= crossbar->config.num_timesteps) + if (timestep >= crossbar->config.num_timesteps) { timestep = crossbar->config.num_timesteps - 1; + } return timestep; } @@ -132,8 +167,9 @@ static int callback_voltage(double *voltage, double time, char *name, int ident, size_t timestep = timestep_for_time(crossbar, time); pthread_mutex_lock(&crossbar->mutex); - while (!crossbar->failed && crossbar->published_count <= timestep) + while (!crossbar->failed && crossbar->published_count <= timestep) { pthread_cond_wait(&crossbar->state_available, &crossbar->mutex); + } if (crossbar->failed) { pthread_mutex_unlock(&crossbar->mutex); return 1; @@ -178,8 +214,9 @@ static int callback_data(pvecvaluesall values, int count, int ident, continue; } const char *col = strstr(value->name, "col"); - if (!col) + if (!col) { continue; + } char *end = NULL; unsigned long index = strtoul(col + 3, &end, 10); if (end != col + 3 && @@ -197,8 +234,9 @@ static int callback_data(pvecvaluesall values, int count, int ident, size_t timestep = (size_t)rounded - 1; int complete = 1; for (size_t i = 0; i < crossbar->config.num_outputs * 2; - i++) + i++) { complete = complete && found[i]; + } if (complete) { double *decoded = crossbar->results + @@ -213,14 +251,16 @@ static int callback_data(pvecvaluesall values, int count, int ident, decoded) == 0) { pthread_mutex_lock(&crossbar->mutex); if (crossbar->result_count < - timestep + 1) + timestep + 1) { crossbar->result_count = timestep + 1; + } if (timestep + 1 < - crossbar->config.num_timesteps) + crossbar->config.num_timesteps) { ngSpice_SetBkpt( (timestep + 2) * crossbar->config.time_step); + } pthread_cond_broadcast( &crossbar->progress); pthread_mutex_unlock(&crossbar->mutex); @@ -238,38 +278,44 @@ int online_crossbar_init(const Online_Crossbar_Config *config, const spires_reservoir *reservoir, Online_Crossbar **crossbar_out) { - if (!config || !reservoir || !crossbar_out || - config->num_neurons == 0 || config->num_outputs == 0 || - config->num_timesteps == 0 || config->time_step <= 0.0 || - config->spike_amplitude == 0.0 || config->load_resistance <= 0.0 || - !config->model_path || !config->subcircuit_name || - !config->netlist_path) + if (!crossbar_out) { return -1; + } + *crossbar_out = NULL; + if (!reservoir || !config_is_valid(config)) { + return -1; + } Online_Crossbar *crossbar = calloc(1, sizeof(*crossbar)); - if (!crossbar) + if (!crossbar) { return -1; + } crossbar->config = *config; - if (pthread_mutex_init(&crossbar->mutex, NULL) != 0) + if (pthread_mutex_init(&crossbar->mutex, NULL) != 0) { goto fail; + } crossbar->mutex_initialized = 1; - if (pthread_cond_init(&crossbar->state_available, NULL) != 0) + if (pthread_cond_init(&crossbar->state_available, NULL) != 0) { goto fail; + } crossbar->state_condition_initialized = 1; - if (pthread_cond_init(&crossbar->progress, NULL) != 0) + if (pthread_cond_init(&crossbar->progress, NULL) != 0) { goto fail; + } crossbar->progress_condition_initialized = 1; crossbar->state_ring = calloc(2 * config->num_neurons, sizeof(double)); crossbar->results = calloc(config->num_timesteps * config->num_outputs, sizeof(double)); - if (!crossbar->state_ring || !crossbar->results) + if (!crossbar->state_ring || !crossbar->results) { goto fail; + } if (convert_weights_to_resistances( reservoir, config->num_neurons, config->num_outputs, config->r_on, config->r_off, &crossbar->resistances, - &crossbar->mapping) != 0) + &crossbar->mapping) != 0) { goto fail; + } Crossbar_Config netlist = {.rows = config->num_neurons, .columns = config->num_outputs * 2, @@ -283,8 +329,9 @@ int online_crossbar_init(const Online_Crossbar_Config *config, .stop_time = config->num_timesteps * config->time_step, .print_state_nodes = 0}; - if (generate_online_crossbar(config->netlist_path, &netlist) != 0) + if (generate_online_crossbar(config->netlist_path, &netlist) != 0) { goto fail; + } *crossbar_out = crossbar; return 0; @@ -296,23 +343,32 @@ fail: int online_crossbar_start(Online_Crossbar *crossbar) { - if (!crossbar || crossbar->started) + if (!crossbar || crossbar->started) { return -1; + } if (ngSpice_Init(callback_text, callback_text, callback_exit, callback_data, callback_init_data, callback_background, - crossbar) != 0 || - ngSpice_Init_Sync(callback_voltage, NULL, NULL, NULL, crossbar) != - 0) + crossbar) != 0) { return -1; + } + if (ngSpice_Init_Sync(callback_voltage, NULL, NULL, NULL, crossbar) != + 0) { + ngSpice_Reset(); + return -1; + } char command[4096]; - if (snprintf(command, sizeof(command), "source %s", - crossbar->config.netlist_path) >= (int)sizeof(command) || - ngSpice_Command(command) != 0) + int written = snprintf(command, sizeof(command), "source %s", + crossbar->config.netlist_path); + if (written < 0 || (size_t)written >= sizeof(command) || + ngSpice_Command(command) != 0) { + ngSpice_Reset(); return -1; + } crossbar->started = 1; ngSpice_SetBkpt(crossbar->config.time_step); if (ngSpice_Command("bg_run") != 0) { + ngSpice_Reset(); crossbar->started = 0; return -1; } @@ -325,12 +381,15 @@ int online_crossbar_submit(Online_Crossbar *crossbar, size_t timestep, { if (!crossbar || !state || !output_ready || !crossbar->started || timestep >= crossbar->config.num_timesteps || - timestep != crossbar->published_count) + timestep != crossbar->published_count || + (timestep != 0 && !previous_output)) { return -1; + } pthread_mutex_lock(&crossbar->mutex); - while (!crossbar->failed && timestep >= crossbar->consumed_count + 2) + while (!crossbar->failed && timestep >= crossbar->consumed_count + 2) { pthread_cond_wait(&crossbar->progress, &crossbar->mutex); + } if (crossbar->failed) { pthread_mutex_unlock(&crossbar->mutex); return -1; @@ -343,13 +402,10 @@ int online_crossbar_submit(Online_Crossbar *crossbar, size_t timestep, *output_ready = timestep != 0; if (timestep != 0) { - if (!previous_output) { - pthread_mutex_unlock(&crossbar->mutex); - return -1; - } - while (!crossbar->failed && crossbar->result_count < timestep) + while (!crossbar->failed && crossbar->result_count < timestep) { pthread_cond_wait(&crossbar->progress, &crossbar->mutex); + } if (crossbar->failed) { pthread_mutex_unlock(&crossbar->mutex); return -1; @@ -366,12 +422,14 @@ int online_crossbar_submit(Online_Crossbar *crossbar, size_t timestep, int online_crossbar_finish(Online_Crossbar *crossbar, double *final_output) { if (!crossbar || !final_output || - crossbar->published_count != crossbar->config.num_timesteps) + crossbar->published_count != crossbar->config.num_timesteps) { return -1; + } pthread_mutex_lock(&crossbar->mutex); while (!crossbar->failed && - crossbar->result_count < crossbar->config.num_timesteps) + crossbar->result_count < crossbar->config.num_timesteps) { pthread_cond_wait(&crossbar->progress, &crossbar->mutex); + } if (crossbar->failed) { pthread_mutex_unlock(&crossbar->mutex); return -1; @@ -386,28 +444,35 @@ int online_crossbar_finish(Online_Crossbar *crossbar, double *final_output) void online_crossbar_destroy(Online_Crossbar *crossbar) { - if (!crossbar) + if (!crossbar) { return; + } if (crossbar->started && ngSpice_running()) { ngSpice_Command("bg_halt"); if (crossbar->mutex_initialized) { pthread_mutex_lock(&crossbar->mutex); - while (!crossbar->simulation_done && ngSpice_running()) + while (!crossbar->simulation_done && + ngSpice_running()) { pthread_cond_wait(&crossbar->progress, &crossbar->mutex); + } pthread_mutex_unlock(&crossbar->mutex); } } - if (crossbar->started) + if (crossbar->started) { ngSpice_Reset(); + } free(crossbar->resistances); free(crossbar->state_ring); free(crossbar->results); - if (crossbar->state_condition_initialized) + if (crossbar->state_condition_initialized) { pthread_cond_destroy(&crossbar->state_available); - if (crossbar->progress_condition_initialized) + } + if (crossbar->progress_condition_initialized) { pthread_cond_destroy(&crossbar->progress); - if (crossbar->mutex_initialized) + } + if (crossbar->mutex_initialized) { pthread_mutex_destroy(&crossbar->mutex); + } free(crossbar); } diff --git a/src/online_crossbar.h b/src/online_crossbar.h index f89a306..a27d88b 100644 --- a/src/online_crossbar.h +++ b/src/online_crossbar.h @@ -23,6 +23,7 @@ typedef struct { int online_crossbar_init(const Online_Crossbar_Config *config, const spires_reservoir *reservoir, Online_Crossbar **crossbar_out); + int online_crossbar_start(Online_Crossbar *crossbar); /* diff --git a/src/read_crossbar.c b/src/read_crossbar.c index 5b79233..0825147 100644 --- a/src/read_crossbar.c +++ b/src/read_crossbar.c @@ -1,19 +1,18 @@ #include "read_crossbar.h" -#include <stdbool.h> #include <ngspice/sharedspice.h> +#include <stdbool.h> #include <sched.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> int run_ngspice(const char *crossbar_path) { - if (crossbar_path == NULL) { - fprintf( - stderr, - "One of the file paths returned null when running ngspice"); + if (!crossbar_path) { + fprintf(stderr, "Crossbar path is required\n"); return -1; } @@ -22,14 +21,15 @@ int run_ngspice(const char *crossbar_path) snprintf(command, sizeof(command), "ngspice -b \"%s\" > /dev/null 2>&1", crossbar_path); - if (written < 0) { - fprintf(stderr, "Failed to write spice command"); + if (written < 0 || (size_t)written >= sizeof(command)) { + fprintf(stderr, "Could not construct ngspice command\n"); return -1; } int status = system(command); - if (status < 0) { - fprintf(stderr, "Failed to run ngspice command"); + if (status != 0) { + fprintf(stderr, "ngspice command failed\n"); + return -1; } return 0; @@ -38,50 +38,68 @@ int run_ngspice(const char *crossbar_path) int read_crossbar(const char *data_path, size_t num_outputs, Crossbar_Output_Matrix *result) { - - FILE *file = fopen(data_path, "r"); - if (!file) { - fprintf(stderr, "Faile to open data file"); + if (!data_path || num_outputs == 0 || !result) { return -1; } - // result->num_samples = 0; - // result->num_outputs = num_outputs; result->time = NULL; result->voltages = NULL; - size_t capacity = 100000; + FILE *file = fopen(data_path, "r"); + if (!file) { + fprintf(stderr, "Failed to open crossbar data file\n"); + return -1; + } + + size_t capacity = 1024; + if (num_outputs > SIZE_MAX / capacity || + capacity * num_outputs > SIZE_MAX / sizeof(*result->voltages)) { + fclose(file); + return -1; + } result->time = malloc(capacity * sizeof(*result->time)); result->voltages = malloc(capacity * num_outputs * sizeof(*result->voltages)); if (result->time == NULL || result->voltages == NULL) { - fprintf(stderr, "Failed to allocate memory for results"); - fclose(file); - free_crossbar_output_matrix(result); - return -1; - } - - size_t expected_fields = num_outputs * 2; - double *fields = malloc(expected_fields * sizeof(*fields)); - if (fields == NULL) { - fprintf(stderr, "failed to allocate memory for fields"); + fprintf(stderr, "Failed to allocate crossbar results\n"); fclose(file); free_crossbar_output_matrix(result); return -1; } char line[16384]; - // size_t line_number = 0; - size_t sample = 0; while (fgets(line, sizeof(line), file) != NULL) { + if (sample == capacity) { + if (capacity > SIZE_MAX / 2 || + capacity * 2 > SIZE_MAX / num_outputs || + capacity * 2 * num_outputs > + SIZE_MAX / sizeof(*result->voltages)) { + goto fail; + } + capacity *= 2; + + double *new_times = realloc( + result->time, capacity * sizeof(*result->time)); + if (!new_times) { + goto fail; + } + result->time = new_times; + + double *new_voltages = realloc( + result->voltages, + capacity * num_outputs * sizeof(*result->voltages)); + if (!new_voltages) { + goto fail; + } + result->voltages = new_voltages; + } + char *position = line; double sample_time = 0.0; - // each loop reads one time-voltage pair, saves it - // then moves to next loop (next time-voltage pair) for (size_t output = 0; output < num_outputs; output++) { double time; double voltage; @@ -89,9 +107,10 @@ int read_crossbar(const char *data_path, size_t num_outputs, if (sscanf(position, "%lf %lf %n", &time, &voltage, &char_count) != 2) { - fprintf(stderr, "invalide data line"); - fclose(file); - return -1; + fprintf(stderr, + "Invalid crossbar data at sample %zu\n", + sample); + goto fail; } if (output == 0) { @@ -105,8 +124,16 @@ int read_crossbar(const char *data_path, size_t num_outputs, result->time[sample] = sample_time; sample++; } - fclose(file); + if (ferror(file) || fclose(file) != 0) { + free_crossbar_output_matrix(result); + return -1; + } return 0; + +fail: + fclose(file); + free_crossbar_output_matrix(result); + return -1; } int convert_output_to_software(size_t num_neurons, size_t num_outputs, @@ -117,17 +144,19 @@ int convert_output_to_software(size_t num_neurons, size_t num_outputs, const double *row_voltages, double spike_amplitude, double *decoded_outputs) { - // Using differential pair mapping - size_t num_physical_columns; + (void)row_voltages; - if (!voltages || !resistances || !mapping || !decoded_outputs) + if (num_neurons == 0 || num_outputs == 0 || num_timesteps == 0 || + !voltages || !resistances || !mapping || !decoded_outputs) { return -1; + } if (load_resistance <= 0.0 || mapping->alpha == 0.0 || - spike_amplitude == 0.0) + spike_amplitude == 0.0) { return -1; + } - num_physical_columns = num_outputs * 2; + size_t num_physical_columns = num_outputs * 2; for (size_t timestep = 0; timestep < num_timesteps; timestep++) { for (size_t output = 0; output < num_outputs; output++) { @@ -177,12 +206,10 @@ int convert_output_to_software(size_t num_neurons, size_t num_outputs, timestep * num_physical_columns + negative_column; positive_voltage = voltages[positive_voltage_index]; - negative_voltage = voltages[negative_voltage_index]; positive_load_current = positive_voltage / load_resistance; - negative_load_current = negative_voltage / load_resistance; @@ -208,15 +235,13 @@ int convert_output_to_software(size_t num_neurons, size_t num_outputs, void free_crossbar_output_matrix(Crossbar_Output_Matrix *result) { - if (result == NULL) { + if (!result) { return; } free(result->time); free(result->voltages); - // result->num_samples = 0; - // result->num_outputs = 0; result->time = NULL; result->voltages = NULL; } diff --git a/src/read_crossbar.h b/src/read_crossbar.h index 968bc94..c6a87fa 100644 --- a/src/read_crossbar.h +++ b/src/read_crossbar.h @@ -5,14 +5,9 @@ #include <stddef.h> typedef struct { - // size_t num_samples; - // size_t num_outputs; double *time; - - // stored in row major order - // voltages[sameple * num_outputs + output] + /* Flat row-major values: voltages[sample * num_outputs + output]. */ double *voltages; - } Crossbar_Output_Matrix; int run_ngspice(const char *crossbar_path); @@ -20,6 +15,10 @@ int run_ngspice(const char *crossbar_path); int read_crossbar(const char *data_path, size_t num_outputs, Crossbar_Output_Matrix *result); +/* + * Decode differential crossbar voltages into software readout values. + * row_voltages is retained for API compatibility and is currently unused. + */ int convert_output_to_software(size_t num_neurons, size_t num_outputs, size_t num_timesteps, const double *voltages, const double *resistances, @@ -28,6 +27,7 @@ int convert_output_to_software(size_t num_neurons, size_t num_outputs, const double *row_voltages, double spike_amplitude, double *decoded_outputs); +/* Release storage allocated by read_crossbar(). */ void free_crossbar_output_matrix(Crossbar_Output_Matrix *result); #endif diff --git a/src/spires_interface.c b/src/spires_interface.c index 665defc..df09985 100644 --- a/src/spires_interface.c +++ b/src/spires_interface.c @@ -11,49 +11,57 @@ int collect_reservoir_states(spires_reservoir *reservoir, const double *input_series, size_t series_length, Reservoir_State_Matrix *result) { - // error checking - // RIP + if (!reservoir || !input_series || series_length == 0 || !result) { + return -1; + } - // clear the result first result->num_samples = 0; result->num_features = 0; result->states = NULL; const size_t num_inputs = spires_num_inputs(reservoir); - const size_t num_neurons = spires_num_neurons(reservoir); + if (num_inputs == 0 || num_neurons == 0) { + return -1; + } if (series_length > SIZE_MAX / num_neurons || series_length * num_neurons > SIZE_MAX / sizeof(double)) { - fprintf(stderr, "matrix size overloaded!!"); + fprintf(stderr, "Reservoir state matrix is too large\n"); return -1; } double *states = malloc(num_neurons * series_length * sizeof(*states)); if (!states) { - fprintf(stderr, "failed to allocate memory for states"); + fprintf(stderr, "Failed to allocate reservoir state matrix\n"); return -1; } spires_status status = spires_reservoir_reset(reservoir); if (status != SPIRES_OK) { - fprintf(stderr, "reservoir reset error"); + fprintf(stderr, "Failed to reset SPIRES reservoir\n"); free(states); return -1; } - // build state_matrix - for (size_t i = 0; i < series_length; i++) { - const double *current_input = &input_series[i * num_inputs]; + for (size_t timestep = 0; timestep < series_length; timestep++) { + const double *current_input = + input_series + timestep * num_inputs; status = spires_step(reservoir, current_input); if (status != SPIRES_OK) { + fprintf(stderr, "SPIRES step failed at timestep %zu\n", + timestep); free(states); return -1; } - double *current_state = &states[i * num_neurons]; + double *current_state = states + timestep * num_neurons; status = spires_read_reservoir_state(reservoir, current_state); if (status != SPIRES_OK) { + fprintf( + stderr, + "Failed to read reservoir state at timestep %zu\n", + timestep); free(states); return -1; } @@ -72,29 +80,48 @@ int convert_weights_to_resistances(const spires_reservoir *reservoir, double **resistances_out, conductance_mapping *mapping) { - double max_abs_weight = 0.0; + if (!reservoir || num_neurons == 0 || num_outputs == 0 || r_on <= 0.0 || + r_off <= r_on || !resistances_out || !mapping) { + return -1; + } *resistances_out = NULL; + if (num_outputs > SIZE_MAX / 2 || + num_neurons > SIZE_MAX / num_outputs) { + return -1; + } + size_t weight_count = num_neurons * num_outputs; size_t num_physical_columns = num_outputs * 2; + if (num_neurons > SIZE_MAX / num_physical_columns || + num_neurons * num_physical_columns > SIZE_MAX / sizeof(double)) { + return -1; + } - double *readout = malloc(num_neurons * num_outputs * sizeof(double)); - - spires_read_readout(reservoir, readout); + double *readout = malloc(weight_count * sizeof(*readout)); + if (!readout) { + fprintf(stderr, "Failed to allocate readout weights\n"); + return -1; + } + if (spires_read_readout(reservoir, readout) != SPIRES_OK) { + fprintf(stderr, "Failed to read SPIRES readout weights\n"); + free(readout); + return -1; + } double *resistances = - malloc(num_neurons * num_physical_columns * sizeof(double)); - if (resistances == NULL) { - fprintf(stderr, "Failed to allocated crossbar resistances"); + malloc(num_neurons * num_physical_columns * sizeof(*resistances)); + if (!resistances) { + fprintf(stderr, "Failed to allocate crossbar resistances\n"); free(readout); return -1; } - // get the max weight + double max_abs_weight = 0.0; for (size_t i = 0; i < weight_count; i++) { if (!isfinite(readout[i])) { fprintf(stderr, - "invalid readout weight at index %zu: %g\n", i, + "Invalid readout weight at index %zu: %g\n", i, readout[i]); free(readout); free(resistances); @@ -106,15 +133,20 @@ int convert_weights_to_resistances(const spires_reservoir *reservoir, max_abs_weight = abs_weight; } } + if (max_abs_weight == 0.0) { + fprintf(stderr, + "Cannot map an all-zero readout to resistances\n"); + free(readout); + free(resistances); + return -1; + } mapping->g_min = 1.0 / r_off; mapping->g_max = 1.0 / r_on; mapping->max_abs_weight = max_abs_weight; - // calculate the alpha scaling parameter to normalize the weights mapping->alpha = (mapping->g_max - mapping->g_min) / max_abs_weight; - // differential pair mapping for (size_t neuron = 0; neuron < num_neurons; neuron++) { for (size_t output = 0; output < num_outputs; output++) { size_t weight_index = output * num_neurons + neuron; @@ -133,8 +165,7 @@ int convert_weights_to_resistances(const spires_reservoir *reservoir, positive_conductance = mapping->g_min + mapping->alpha * weight; negative_conductance = mapping->g_min; - } - if (weight < 0.0) { + } else { positive_conductance = mapping->g_min; negative_conductance = mapping->g_min + mapping->alpha * (-weight); @@ -146,10 +177,6 @@ int convert_weights_to_resistances(const spires_reservoir *reservoir, 1.0 / negative_conductance; } } - // printf("max absolute weight = %.12e\n", mapping->max_abs_weight); - // printf("alpha = %.12e\n", mapping->alpha); - // printf("physical crossbar dimensions: %zu x %zu\n", num_neurons, - // num_physical_columns); *resistances_out = resistances; free(readout); @@ -160,12 +187,16 @@ int convert_weights_to_resistances(const spires_reservoir *reservoir, int train_reservoir(spires_reservoir *reservoir, double *input_series, double *target_series, size_t series_length, double lambda) { - spires_status status = - spires_train_ridge(reservoir, (double *)input_series, - (double *)target_series, series_length, lambda); + if (!reservoir || !input_series || !target_series || + series_length == 0 || lambda < 0.0) { + return -1; + } + + spires_status status = spires_train_ridge( + reservoir, input_series, target_series, series_length, lambda); if (status != SPIRES_OK) { - fprintf(stderr, "Spires ridge training failed"); + fprintf(stderr, "SPIRES ridge training failed\n"); return -1; } @@ -179,7 +210,6 @@ void free_reservoir_state_matrix(Reservoir_State_Matrix *matrix) } free(matrix->states); - matrix->states = NULL; matrix->num_samples = 0; matrix->num_features = 0; diff --git a/src/spires_interface.h b/src/spires_interface.h index 006b9d7..f3552b9 100644 --- a/src/spires_interface.h +++ b/src/spires_interface.h @@ -5,8 +5,9 @@ #include <stddef.h> typedef struct { - size_t num_samples; // number of time steps + size_t num_samples; size_t num_features; + /* Row-major continuous reservoir states: [sample][feature]. */ double *states; } Reservoir_State_Matrix; @@ -21,8 +22,9 @@ int collect_reservoir_states(spires_reservoir *reservoir, const double *input_series, size_t series_length, Reservoir_State_Matrix *result); +/* Train the SPIRES-owned readout using its configured state representation. */ int train_reservoir(spires_reservoir *reservoir, double *input_series, - double *taret_series, size_t series_length, double lambda); + double *target_series, size_t series_length, double lambda); int convert_weights_to_resistances(const spires_reservoir *reservoir, size_t num_neurons, size_t num_outputs, @@ -30,6 +32,7 @@ int convert_weights_to_resistances(const spires_reservoir *reservoir, double **resistances_out, conductance_mapping *mapping); +/* Release storage allocated by collect_reservoir_states(). */ void free_reservoir_state_matrix(Reservoir_State_Matrix *matrix); #endif |
