summaryrefslogtreecommitdiff
path: root/src/spires_interface.c
blob: 0d69ebad25a84ac72e758be8c57cfd76127ce35e (plain)
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
188
189
190
#include "spires_interface.h"
#include <spires.h>

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <math.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 map_signed_weights_to_resistance(
        const double *weights,
        size_t num_neurons,
        size_t num_outputs,
        double resistance_on,
        double resistance_off,
        double *resistances,
        double *conductance_offset,
        double *conductance_scale
        ) {
   //safety check arguments
   if (weights == NULL || resistances == NULL || num_neurons == 0 ||
        num_outputs == 0 || resistance_on <= 0 || resistance_off <= resistance_on) {
       return -1;
   }

   const size_t count = num_neurons * num_outputs;

    const double conductance_max = 1.0 / resistance_on;
    const double conductance_min = 1.0 / resistance_off;

    double max_absolute_weight = 0.0;

    for (size_t i = 0; i < count; i++) {
        double magnitude = fabs(weights[i]);

        if (magnitude > max_absolute_weight) {
            max_absolute_weight = magnitude;
        }
    }

    const double offset =
        0.5 * (conductance_max + conductance_min);

    if (max_absolute_weight == 0.0) {
        for (size_t i = 0; i < count; i++) {
            resistances[i] = 1.0 / offset;
        }

        if (conductance_offset != NULL) {
            *conductance_offset = offset;
        }

        if (conductance_scale != NULL) {
            *conductance_scale = 0.0;
        }

        return 0;
    }

    const double scale =
        (conductance_max - conductance_min) /
        (2.0 * max_absolute_weight);

    for (size_t i = 0; i < count; i++) {
        double conductance =
            offset + scale * weights[i];

        /*
         * Protect against small floating-point excursions.
         */
        if (conductance < conductance_min) {
            conductance = conductance_min;
        } else if (conductance > conductance_max) {
            conductance = conductance_max;
        }

        resistances[i] = 1.0 / conductance;
    }

    if (conductance_offset != NULL) {
        *conductance_offset = offset;
    }

    if (conductance_scale != NULL) {
        *conductance_scale = scale;
    }

    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;
    }
    //need to figure out how to scale weights to conductance values,
    
    //that are then the reciprocal of the resistances
    
    //Also need to find some conversion for output column current, and target_series
    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;
}