1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
|
#include "spires_interface.h"
#include <spires.h>
#include <math.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
int collect_reservoir_states(spires_reservoir *reservoir,
const double *input_series, size_t series_length,
Reservoir_State_Matrix *result)
{
// error checking
// RIP
// 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 (series_length > SIZE_MAX / num_neurons ||
series_length * num_neurons > SIZE_MAX / sizeof(double)) {
fprintf(stderr, "matrix size overloaded!!");
return -1;
}
double *states = malloc(num_neurons * series_length * sizeof(*states));
if (!states) {
fprintf(stderr, "failed to allocate memory for states");
return -1;
}
spires_status status = spires_reservoir_reset(reservoir);
if (status != SPIRES_OK) {
fprintf(stderr, "reservoir reset error");
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];
status = spires_step(reservoir, current_input);
if (status != SPIRES_OK) {
free(states);
return -1;
}
double *current_state = &states[i * num_neurons];
status = spires_read_reservoir_state(reservoir, current_state);
if (status != SPIRES_OK) {
free(states);
return -1;
}
}
result->num_samples = series_length;
result->num_features = num_neurons;
result->states = states;
return 0;
}
int convert_weights_to_resistances(const spires_reservoir *reservoir,
size_t num_neurons, size_t num_outputs,
double r_on, double r_off,
double **resistances_out,
conductance_mapping *mapping)
{
double max_abs_weight = 0.0;
*resistances_out = NULL;
size_t weight_count = num_neurons * num_outputs;
size_t num_physical_columns = num_outputs * 2;
double *readout = malloc(num_neurons * num_outputs * sizeof(double));
spires_read_readout(reservoir, readout);
double *resistances =
malloc(num_neurons * num_physical_columns * sizeof(double));
if (resistances == NULL) {
fprintf(stderr, "Failed to allocated crossbar resistances");
free(readout);
return -1;
}
// get the max weight
for (size_t i = 0; i < weight_count; i++) {
if (!isfinite(readout[i])) {
fprintf(stderr,
"invalid readout weight at index %zu: %g\n", i,
readout[i]);
free(readout);
free(resistances);
return -1;
}
double abs_weight = fabs(readout[i]);
if (abs_weight > max_abs_weight) {
max_abs_weight = abs_weight;
}
}
printf("max absolute weight = %.12e\n", max_abs_weight);
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;
size_t positive_column = 2 * output;
size_t negative_column = positive_column + 1;
double positive_conductance;
double negative_conductance;
size_t positive_index =
neuron * num_physical_columns + positive_column;
size_t negative_index =
neuron * num_physical_columns + negative_column;
double weight = readout[weight_index];
if (weight >= 0.0) {
positive_conductance =
mapping->g_min + mapping->alpha * weight;
negative_conductance = mapping->g_min;
}
if (weight < 0.0) {
positive_conductance = mapping->g_min;
negative_conductance =
mapping->g_min + mapping->alpha * (-weight);
}
resistances[positive_index] =
1.0 / positive_conductance;
resistances[negative_index] =
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);
return 0;
}
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 (status != SPIRES_OK) {
fprintf(stderr, "Spires ridge training failed");
return -1;
}
return 0;
}
void free_reservoir_state_matrix(Reservoir_State_Matrix *matrix)
{
if (!matrix) {
return;
}
free(matrix->states);
matrix->states = NULL;
matrix->num_samples = 0;
matrix->num_features = 0;
}
|