summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorYour Name <[email protected]>2026-08-28 17:29:33 -0700
committerYour Name <[email protected]>2026-08-28 17:29:33 -0700
commit02158c7613495f1a02a2a8f108cd1c397656ce2b (patch)
tree5cdd0d716b0ef6ae98375354d952c4cac1670846 /src
parent2a3cb69b71539953489752cea5f34e87cdb7814a (diff)
cleaned up
Diffstat (limited to 'src')
-rw-r--r--src/benchmark.c162
-rw-r--r--src/benchmark.h2
-rw-r--r--src/crossbar_generator.c140
-rw-r--r--src/crossbar_generator.h29
-rw-r--r--src/online_crossbar.c161
-rw-r--r--src/online_crossbar.h1
-rw-r--r--src/read_crossbar.c113
-rw-r--r--src/read_crossbar.h12
-rw-r--r--src/spires_interface.c96
-rw-r--r--src/spires_interface.h7
10 files changed, 427 insertions, 296 deletions
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