sim-ballena 0.1.2__tar.gz → 0.1.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sim_ballena-0.1.2 → sim_ballena-0.1.4}/Cargo.lock +1 -1
- {sim_ballena-0.1.2 → sim_ballena-0.1.4}/Cargo.toml +7 -1
- {sim_ballena-0.1.2 → sim_ballena-0.1.4}/PKG-INFO +1 -1
- sim_ballena-0.1.4/sim_ballena.pyi +49 -0
- {sim_ballena-0.1.2 → sim_ballena-0.1.4}/src/instances.rs +11 -4
- {sim_ballena-0.1.2 → sim_ballena-0.1.4}/src/networks.rs +21 -9
- {sim_ballena-0.1.2 → sim_ballena-0.1.4}/src/neurons.rs +21 -7
- {sim_ballena-0.1.2 → sim_ballena-0.1.4}/src/responses.rs +41 -9
- {sim_ballena-0.1.2 → sim_ballena-0.1.4}/src/simulation.rs +1 -1
- {sim_ballena-0.1.2 → sim_ballena-0.1.4}/src/utils.rs +30 -26
- {sim_ballena-0.1.2 → sim_ballena-0.1.4}/test.sh +1 -1
- sim_ballena-0.1.4/test_fast.py +45 -0
- sim_ballena-0.1.2/lista_de_deseos.py +0 -113
- sim_ballena-0.1.2/test_file.py +0 -49
- sim_ballena-0.1.2/tests.ipynb +0 -299
- {sim_ballena-0.1.2 → sim_ballena-0.1.4}/.github/workflows/CI.yml +0 -0
- {sim_ballena-0.1.2 → sim_ballena-0.1.4}/.gitignore +0 -0
- {sim_ballena-0.1.2 → sim_ballena-0.1.4}/pyproject.toml +0 -0
- {sim_ballena-0.1.2 → sim_ballena-0.1.4}/src/lib.rs +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "SimBallenaSNN"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.4"
|
|
4
4
|
edition = "2024"
|
|
5
5
|
|
|
6
6
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
@@ -12,3 +12,9 @@ crate-type = ["cdylib"]
|
|
|
12
12
|
pyo3 = "0.27.0"
|
|
13
13
|
rand = "0.9.2"
|
|
14
14
|
rand_distr = "0.5.1"
|
|
15
|
+
|
|
16
|
+
[tool.setuptools]
|
|
17
|
+
py-modules = ["sim_ballena"]
|
|
18
|
+
|
|
19
|
+
[tool.setuptools.package-data]
|
|
20
|
+
"*" = ["*.pyi"]
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
class Lif:
|
|
2
|
+
def repeat(self, n:int) -> list[Lif]: ...
|
|
3
|
+
def tau(self, tau:float) -> Lif: ...
|
|
4
|
+
def v_rest(self, v_rest:float) -> Lif: ...
|
|
5
|
+
def v_thres(self, v_thres:float) -> Lif: ...
|
|
6
|
+
def v_reset(self, v_reset:float) -> Lif: ...
|
|
7
|
+
def t_refractory(self, t_refractory:float) -> Lif: ...
|
|
8
|
+
def activity_window(self, start:float, end:float) -> Lif: ...
|
|
9
|
+
def get_voltage(self) -> float: ...
|
|
10
|
+
def get_tau(self) -> float: ...
|
|
11
|
+
def get_v_rest(self) -> float: ...
|
|
12
|
+
def get_v_thres(self) -> float: ...
|
|
13
|
+
def get_v_reset(self) -> float: ...
|
|
14
|
+
def get_t_refractory(self) -> float: ...
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Instance:
|
|
18
|
+
def __init__(spikes: list[tuple[float,int]]) -> Instance: ...
|
|
19
|
+
def get_spikes(self) -> list[tuple[float,int]]: ...
|
|
20
|
+
|
|
21
|
+
class PoissonGenerator:
|
|
22
|
+
def new(rates: list[float], max_time:float) -> PoissonGenerator: ...
|
|
23
|
+
def to_instance(self) -> Instance: ...
|
|
24
|
+
def get_spikes(self) -> list[list[float]]: ...
|
|
25
|
+
def concat(self, other:PoissonGenerator) -> PoissonGenerator: ...
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Response:
|
|
29
|
+
def outputs(self) ->list[int]: ...
|
|
30
|
+
def spk_times(self) ->list[list[float]]: ...
|
|
31
|
+
def spk_count(self) ->list[int]: ...
|
|
32
|
+
def v_trace(self) ->list[list[float]]: ...
|
|
33
|
+
def v_state(self) ->list[float]: ...
|
|
34
|
+
def time(self) ->list[float]: ...
|
|
35
|
+
def resolution(self, new_resolution:float) ->Response: ...
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class Network:
|
|
39
|
+
def new(neurons: list[Lif])-> Network: ...
|
|
40
|
+
def synapses_in(self,synapses: list[ tuple[float,int] ] ) -> Network: ...
|
|
41
|
+
def synapses_net(self,synapses: list[ tuple[float,int] ]) -> Network: ...
|
|
42
|
+
def weights_in(self, weights:list[float]) -> Network: ...
|
|
43
|
+
def weights_net(self, weights:list[float]) -> Network: ...
|
|
44
|
+
def weights(self, weights:list[float]) -> Network: ...
|
|
45
|
+
def outputs(self, outputs:list[int]) -> Network: ...
|
|
46
|
+
def mode(self, modes: list[str]) -> Network: ...
|
|
47
|
+
def get_voltages(self) -> list[float]: ...
|
|
48
|
+
def simulate(self, instance: Instance, max_time:float) -> Response: ...
|
|
49
|
+
|
|
@@ -21,6 +21,10 @@ impl Instance{
|
|
|
21
21
|
Err(e) => return Err(e)
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
+
if spikes.iter().any(|s|s.0<0.0){
|
|
25
|
+
return Err(PyValueError::new_err( "There cannot be spikes at times prior to zero" ))
|
|
26
|
+
}
|
|
27
|
+
|
|
24
28
|
spikes.sort_by(|a,b|a.0.partial_cmp(&b.0).unwrap());
|
|
25
29
|
|
|
26
30
|
Ok(Self{spikes})
|
|
@@ -34,7 +38,7 @@ impl Instance{
|
|
|
34
38
|
self.__str__()
|
|
35
39
|
}
|
|
36
40
|
|
|
37
|
-
pub fn
|
|
41
|
+
pub fn get_spikes(&self)->&Vec<(f64,usize)>{
|
|
38
42
|
&self.spikes
|
|
39
43
|
}
|
|
40
44
|
}
|
|
@@ -58,12 +62,15 @@ pub struct PoissonGenerator{
|
|
|
58
62
|
#[pymethods]
|
|
59
63
|
impl PoissonGenerator{
|
|
60
64
|
#[new]
|
|
61
|
-
fn new(rates: Vec<f64>, max_time:f64)->Self{
|
|
65
|
+
fn new(rates: Vec<f64>, max_time:f64)->PyResult<Self>{
|
|
62
66
|
let mut spike_times:Vec<Vec<f64>> = Vec::new();
|
|
63
67
|
for rate in rates{
|
|
68
|
+
if rate<0.0{
|
|
69
|
+
return Err( PyValueError::new_err("No rates can be less than zero.") )
|
|
70
|
+
}
|
|
64
71
|
spike_times.push( Self::poisson_process(rate, max_time) );
|
|
65
72
|
}
|
|
66
|
-
Self{max_time, spike_times}
|
|
73
|
+
Ok(Self{max_time, spike_times})
|
|
67
74
|
}
|
|
68
75
|
|
|
69
76
|
fn to_instance(&self)->Instance{
|
|
@@ -79,7 +86,7 @@ impl PoissonGenerator{
|
|
|
79
86
|
self.spike_times.clone()
|
|
80
87
|
}
|
|
81
88
|
|
|
82
|
-
fn concat<'py>(mut this:PyRefMut<'py, Self>, other
|
|
89
|
+
fn concat<'py>(mut this:PyRefMut<'py, Self>, other:PyRef<'py, Self>)->PyResult<PyRefMut<'py,Self>>{
|
|
83
90
|
|
|
84
91
|
// check both generators have same dimentions
|
|
85
92
|
let mut spikes_other = other.get_spikes();
|
|
@@ -138,7 +138,7 @@ impl Network{
|
|
|
138
138
|
Ok(this)
|
|
139
139
|
}
|
|
140
140
|
/* ===================== */
|
|
141
|
-
/*
|
|
141
|
+
/* SET_WEIGHTS */
|
|
142
142
|
/* ===================== */
|
|
143
143
|
fn weights_in<'py>(mut this: PyRefMut<'py, Self>, weights:Vec<f64>)->PyResult<PyRefMut<'py,Self>>{
|
|
144
144
|
match this.n_syn_in{
|
|
@@ -157,9 +157,6 @@ impl Network{
|
|
|
157
157
|
Ok(this)
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
-
/* ===================== */
|
|
161
|
-
/* SET_WEIGHTS_NET */
|
|
162
|
-
/* ===================== */
|
|
163
160
|
fn weights_net<'py>(mut this: PyRefMut<'py, Self>, weights:Vec<f64>)->PyResult<PyRefMut<'py,Self>>{
|
|
164
161
|
match this.n_syn_net{
|
|
165
162
|
Some(n) => {
|
|
@@ -177,6 +174,26 @@ impl Network{
|
|
|
177
174
|
Ok(this)
|
|
178
175
|
}
|
|
179
176
|
|
|
177
|
+
fn weights<'py>(mut this: PyRefMut<'py, Self>, weights:Vec<f64>)->PyResult<PyRefMut<'py, Self>>{
|
|
178
|
+
match (this.n_syn_in, this.n_syn_net){
|
|
179
|
+
( Some(n_in), Some(n_net) ) => {
|
|
180
|
+
if weights.len() != n_in+n_net{
|
|
181
|
+
let msg_err = format!("Number of synapses (input:{}, network:{}) does not match with weights lenght ({})",
|
|
182
|
+
n_in, n_net, weights.len());
|
|
183
|
+
return Err(PyValueError::new_err(msg_err))
|
|
184
|
+
}
|
|
185
|
+
this.w_in = Some(weights[0..n_in].to_vec());
|
|
186
|
+
this.w_net = Some(weights[n_in..n_in+n_net].to_vec());
|
|
187
|
+
},
|
|
188
|
+
( Some(_) , None ) => { return Err(PyValueError::new_err("First you need to set network synapses")) },
|
|
189
|
+
( None , Some(_) ) => { return Err(PyValueError::new_err("First you need to set input synapses")) },
|
|
190
|
+
( None , None ) => { return Err(PyValueError::new_err("First you need to set input and network synapses")) },
|
|
191
|
+
}
|
|
192
|
+
Ok(this)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
|
|
180
197
|
/* ===================== */
|
|
181
198
|
/* SET_OUPUPTS */
|
|
182
199
|
/* ===================== */
|
|
@@ -267,11 +284,6 @@ impl Network{
|
|
|
267
284
|
fn simulate(&mut self, instance: PyRef<'_, Instance>, max_time:f64)->PyResult<Response>{
|
|
268
285
|
simulate(self, instance, max_time)
|
|
269
286
|
}
|
|
270
|
-
|
|
271
|
-
/* ===================== */
|
|
272
|
-
/* COPY GETTERS */
|
|
273
|
-
/* ===================== */
|
|
274
|
-
|
|
275
287
|
}
|
|
276
288
|
|
|
277
289
|
|
|
@@ -86,12 +86,15 @@ impl Lif{
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
/* SETTERS */
|
|
89
|
-
fn tau(mut this: PyRefMut<'_, Self>, tau:f64)->PyRefMut<'_, Self
|
|
89
|
+
fn tau(mut this: PyRefMut<'_, Self>, tau:f64)->PyResult<PyRefMut<'_, Self>>{
|
|
90
|
+
if tau <= 0.0{
|
|
91
|
+
return Err( PyValueError::new_err("tau must be greater than zero.") )
|
|
92
|
+
}
|
|
90
93
|
this.tau = tau;
|
|
91
|
-
this
|
|
94
|
+
Ok(this)
|
|
92
95
|
}
|
|
93
96
|
|
|
94
|
-
fn v_rest(mut this: PyRefMut<'_, Self>, v_rest:f64)->PyRefMut<'_, Self>{
|
|
97
|
+
fn v_rest(mut this: PyRefMut<'_, Self>, v_rest:f64)->PyRefMut<'_, Self>{
|
|
95
98
|
this.v_rest = v_rest;
|
|
96
99
|
this
|
|
97
100
|
}
|
|
@@ -101,14 +104,25 @@ impl Lif{
|
|
|
101
104
|
this
|
|
102
105
|
}
|
|
103
106
|
|
|
104
|
-
fn v_reset(mut this: PyRefMut<'_, Self>, v_reset:f64)->PyRefMut<'_, Self
|
|
107
|
+
fn v_reset(mut this: PyRefMut<'_, Self>, v_reset:f64)->PyResult<PyRefMut<'_, Self>>{
|
|
108
|
+
if v_reset >= this.v_thres{
|
|
109
|
+
let msg:String = "v_reset cannot be greater than v_thres.
|
|
110
|
+
The neuron would keep firing periodically.
|
|
111
|
+
If this is the desired behavior, please model it via inputs.".into();
|
|
112
|
+
|
|
113
|
+
return Err( PyValueError::new_err(msg) )
|
|
114
|
+
}
|
|
115
|
+
|
|
105
116
|
this.v_reset = v_reset;
|
|
106
|
-
this
|
|
117
|
+
Ok(this)
|
|
107
118
|
}
|
|
108
119
|
|
|
109
|
-
fn t_refractory(mut this: PyRefMut<'_, Self>, t_refractory:f64)->PyRefMut<'_, Self
|
|
120
|
+
fn t_refractory(mut this: PyRefMut<'_, Self>, t_refractory:f64)->PyResult<PyRefMut<'_, Self>>{
|
|
121
|
+
if t_refractory < 0.0{
|
|
122
|
+
return Err( PyValueError::new_err("t_refractory must be greater than or equal to zero.") )
|
|
123
|
+
}
|
|
110
124
|
this.t_refract = t_refractory;
|
|
111
|
-
this
|
|
125
|
+
Ok(this)
|
|
112
126
|
}
|
|
113
127
|
|
|
114
128
|
fn activity_window(mut this: PyRefMut<'_, Self>, start:f64, end:f64)->PyResult<PyRefMut<'_,Self>>{
|
|
@@ -8,6 +8,7 @@ const MAX_PRECISION :f64 = 100_000.0;
|
|
|
8
8
|
/* ======= OBJECTS ========= */
|
|
9
9
|
/* ========================== */
|
|
10
10
|
#[derive(Clone)]
|
|
11
|
+
#[derive(Debug)]
|
|
11
12
|
struct VoltageMarker{
|
|
12
13
|
pub t : f64,
|
|
13
14
|
pub v : f64,
|
|
@@ -165,9 +166,9 @@ impl Response{
|
|
|
165
166
|
|
|
166
167
|
// ==================
|
|
167
168
|
// Spikes response
|
|
168
|
-
fn
|
|
169
|
+
fn spk_times(&self)->PyResult<Vec<Vec<f64>>>{
|
|
169
170
|
if !self.modes.1{
|
|
170
|
-
return Err(PyRuntimeError::new_err("
|
|
171
|
+
return Err(PyRuntimeError::new_err("Spike times response is not available for this network. Try adding SPIKES to allowed modes."))
|
|
171
172
|
}
|
|
172
173
|
|
|
173
174
|
let mut spikes:Vec<Vec<f64>> = Vec::with_capacity( self.outputs.len() );
|
|
@@ -180,11 +181,26 @@ impl Response{
|
|
|
180
181
|
Ok(spikes)
|
|
181
182
|
}
|
|
182
183
|
|
|
184
|
+
fn spk_count(&self)->PyResult<Vec<usize>>{
|
|
185
|
+
if !self.modes.1{
|
|
186
|
+
return Err(PyRuntimeError::new_err("Spikes count response is not available for this network. Try adding SPIKES to allowed modes."))
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
let mut spike_count:Vec<usize> = Vec::with_capacity( self.outputs.len() );
|
|
190
|
+
for o in self.outputs.iter(){
|
|
191
|
+
let spikes:Vec<&SpikeMarker> = self.spikes.iter().filter( |s|s.id==*o ).collect();
|
|
192
|
+
spike_count.push( spikes.len() );
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
Ok(spike_count)
|
|
196
|
+
|
|
197
|
+
}
|
|
198
|
+
|
|
183
199
|
// ==================
|
|
184
200
|
// Voltage response
|
|
185
|
-
fn
|
|
201
|
+
fn v_trace(&mut self)->PyResult<Vec<Vec<f64>>>{
|
|
186
202
|
if !self.modes.0{
|
|
187
|
-
return Err(PyRuntimeError::new_err("Voltage response is not available for this network"))
|
|
203
|
+
return Err(PyRuntimeError::new_err("Voltage trace response is not available for this network. Try adding VOLTAGES to allowed modes."))
|
|
188
204
|
}
|
|
189
205
|
let mut voltages:Vec<Vec<f64>> = Vec::with_capacity( self.outputs.len() );
|
|
190
206
|
let simulation_times = match &self.simulation_times{
|
|
@@ -198,19 +214,35 @@ impl Response{
|
|
|
198
214
|
let tau_list = self.tau_list.as_ref().unwrap();
|
|
199
215
|
let v_rest_list = self.v_rest_list.as_ref().unwrap();
|
|
200
216
|
|
|
201
|
-
for
|
|
217
|
+
for (o_idx,o) in self.outputs.iter().enumerate(){
|
|
202
218
|
let markers_serie:Vec<&VoltageMarker> = self.voltages.iter()
|
|
203
|
-
.filter(|vm|vm.id
|
|
219
|
+
.filter(|vm|vm.id==*o)
|
|
204
220
|
.collect();
|
|
205
221
|
|
|
206
|
-
let tau = *tau_list.get(
|
|
207
|
-
let v_rest = *v_rest_list.get(
|
|
222
|
+
let tau = *tau_list.get(o_idx).unwrap();
|
|
223
|
+
let v_rest = *v_rest_list.get(o_idx).unwrap();
|
|
208
224
|
voltages.push( self.extract_voltage_serie(markers_serie, simulation_times, tau, v_rest) );
|
|
209
225
|
}
|
|
210
226
|
|
|
211
227
|
Ok(voltages)
|
|
212
228
|
|
|
213
229
|
}
|
|
230
|
+
|
|
231
|
+
fn v_state(&mut self)->PyResult<Vec<f64>>{
|
|
232
|
+
if !self.modes.0{
|
|
233
|
+
return Err(PyRuntimeError::new_err("Voltage state response is not available for this network. Try adding VOLTAGES to allowed modes."))
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
let mut state:Vec<f64> = Vec::with_capacity( self.outputs.len() );
|
|
237
|
+
|
|
238
|
+
for o in self.outputs.iter(){
|
|
239
|
+
let marker = self.voltages.iter().filter( |m|m.id==*o ).last().unwrap();
|
|
240
|
+
state.push( marker.v );
|
|
241
|
+
}
|
|
242
|
+
Ok(state)
|
|
243
|
+
|
|
244
|
+
}
|
|
245
|
+
|
|
214
246
|
// ==================
|
|
215
247
|
// Time response
|
|
216
248
|
fn time(&mut self)->PyResult<Vec<f64>>{
|
|
@@ -228,7 +260,7 @@ impl Response{
|
|
|
228
260
|
|
|
229
261
|
// ==================
|
|
230
262
|
// Set resolution
|
|
231
|
-
fn
|
|
263
|
+
fn resolution(mut this:PyRefMut<'_, Self>, new_resolution:f64)->PyRefMut<'_, Self>{
|
|
232
264
|
this.resolution = new_resolution;
|
|
233
265
|
this
|
|
234
266
|
}
|
|
@@ -81,7 +81,7 @@ pub fn simulate(network:&mut Network, instance: PyRef<'_, Instance>, max_time:f6
|
|
|
81
81
|
None => return Err(PyRuntimeError::new_err("Outputs not defined"))
|
|
82
82
|
};
|
|
83
83
|
|
|
84
|
-
let mut events = EventBuffer::new( instance.
|
|
84
|
+
let mut events = EventBuffer::new( instance.get_spikes(), network.get_neurons().len() );
|
|
85
85
|
let mut response = Response::new( network.get_outputs().unwrap().clone(), network.get_modes() );
|
|
86
86
|
|
|
87
87
|
/* =========================== */
|
|
@@ -11,36 +11,40 @@ use std::collections::HashSet;
|
|
|
11
11
|
pub fn vec_of_tuples<T>(obj: &Bound<'_, PyAny>)->PyResult<Vec<(T,usize)>>
|
|
12
12
|
where for<'a, 'py> T: FromPyObject<'a, 'py>{
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
for i in 0..n{
|
|
25
|
-
let item = seq.get_item(i)?;
|
|
26
|
-
|
|
27
|
-
let len_method = item.getattr("__len__")?;
|
|
28
|
-
let py_len = len_method.call0()?;
|
|
29
|
-
if py_len.extract::<usize>()? == 2{
|
|
30
|
-
|
|
31
|
-
let e1_ = item.get_item(0)?;
|
|
32
|
-
let e2_ = item.get_item(1)?;
|
|
14
|
+
let vector = {
|
|
15
|
+
// First try with a list of tuples
|
|
16
|
+
if let Ok(vec) = obj.extract::<Vec<(T, usize)>>() {
|
|
17
|
+
vec
|
|
18
|
+
}
|
|
19
|
+
else{
|
|
20
|
+
// If not possible, try with a list of list
|
|
21
|
+
let seq = obj.cast::<PySequence>()?;
|
|
22
|
+
let n = seq.len()?;
|
|
23
|
+
let mut vec:Vec<(T,usize)> = Vec::with_capacity(n);
|
|
33
24
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
25
|
+
for i in 0..n{
|
|
26
|
+
let item = seq.get_item(i)?;
|
|
27
|
+
|
|
28
|
+
let len_method = item.getattr("__len__")?;
|
|
29
|
+
let py_len = len_method.call0()?;
|
|
30
|
+
if py_len.extract::<usize>()? == 2{
|
|
31
|
+
|
|
32
|
+
let e1_ = item.get_item(0)?;
|
|
33
|
+
let e2_ = item.get_item(1)?;
|
|
34
|
+
|
|
35
|
+
if let Ok(e1) = e1_.extract::<T>(){
|
|
36
|
+
if let Ok(e2) = e2_.extract::<usize>(){
|
|
37
|
+
vec.push((e1,e2));
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
38
41
|
}
|
|
42
|
+
return Err(PyValueError::new_err("Is not possible to convert the sequence. Please try with a list of tuples of 2 elements. [(numbe,number)...]"))
|
|
39
43
|
}
|
|
44
|
+
vec
|
|
40
45
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
Ok(resultado)
|
|
46
|
+
};
|
|
47
|
+
Ok(vector)
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import sim_ballena as ballena
|
|
2
|
+
import matplotlib.pyplot as plt
|
|
3
|
+
|
|
4
|
+
gen0 = ballena.PoissonGenerator( [100],1 )
|
|
5
|
+
gen1 = ballena.PoissonGenerator( [-10],1.5 )
|
|
6
|
+
|
|
7
|
+
print( gen0.concat( gen1 ), gen1 )
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# net = (ballena.Network( ballena.Lif().tau(2).repeat(4) )
|
|
12
|
+
# .outputs([1,3,0,2])
|
|
13
|
+
# .mode(['VOLTAGES','SPIKES']))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# # ==========
|
|
18
|
+
# # STEP 1
|
|
19
|
+
# # ==========
|
|
20
|
+
# net.synapses_in([(0,0),(0,1),(0,2),(0,3)])
|
|
21
|
+
# net.synapses_net([[0,1]])
|
|
22
|
+
# net.weights([2,5,10,3,2])
|
|
23
|
+
|
|
24
|
+
# times = [ (1, 0) ]
|
|
25
|
+
# instance = ballena.Instance( times )
|
|
26
|
+
# res = net.simulate( instance, 2 ).set_resolution(0.001)
|
|
27
|
+
|
|
28
|
+
# print(res.v_state())
|
|
29
|
+
# print( res.spk_times() )
|
|
30
|
+
# print( res.spk_count() )
|
|
31
|
+
|
|
32
|
+
# time = res.time()
|
|
33
|
+
# volt = res.v_trace()
|
|
34
|
+
|
|
35
|
+
# plt.plot( time, volt[0], label='0')
|
|
36
|
+
# plt.plot( time, volt[1], label='1')
|
|
37
|
+
# plt.plot( time, volt[2], label='2')
|
|
38
|
+
# plt.plot( time, volt[3], label='3')
|
|
39
|
+
# plt.legend()
|
|
40
|
+
# plt.grid()
|
|
41
|
+
|
|
42
|
+
# plt.show()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import Ballena as ballena
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
# ======================
|
|
5
|
-
# CREAR OBJETOS
|
|
6
|
-
# ======================
|
|
7
|
-
|
|
8
|
-
instance = [(0.4, 0),
|
|
9
|
-
(0.5, 1),
|
|
10
|
-
(2.1, 0),
|
|
11
|
-
(3.0, 0),
|
|
12
|
-
(3.4, 1)] # [ (TIME, INPUT_ID) ... ]
|
|
13
|
-
|
|
14
|
-
instance = ballena.create_input( instance ) # WRAPPER
|
|
15
|
-
|
|
16
|
-
synapses_in = [(0,1),(0,2),(1,1)] # [ (neu_pre, neu_post) ... ]
|
|
17
|
-
synapses_net = [(0,1),(0,2),(2,1)] # [ (neu_pre, neu_post) ... ]
|
|
18
|
-
weights_in = [0,1,2] # [ w1,w2,w3 ... ]
|
|
19
|
-
weights_net = [3,4,5]
|
|
20
|
-
|
|
21
|
-
neurons = [ballena.lif(tau=2, v_thres=-55, v_rest=-70, refract=5),
|
|
22
|
-
ballena.lif(tau=3, ),
|
|
23
|
-
ballena.lif(tau=4, )]
|
|
24
|
-
|
|
25
|
-
outputs = [2]
|
|
26
|
-
|
|
27
|
-
# ======================
|
|
28
|
-
# CREAR RED Y SIMULAR
|
|
29
|
-
# ======================
|
|
30
|
-
|
|
31
|
-
net = (ballena.network(neurons)
|
|
32
|
-
.set_synpases_in(synapses_in)
|
|
33
|
-
.set_synpases_net(synapses_net)
|
|
34
|
-
.set_weights_in(weights_in)
|
|
35
|
-
.set_weights_net(weights_net)
|
|
36
|
-
.set_outputs(outputs))
|
|
37
|
-
|
|
38
|
-
spikes = net.simulate( instance, t=300 ).get_spikes()
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
# ===========================================
|
|
44
|
-
# CASO DE USO EVALUAR ACCURACY CON SPIKES
|
|
45
|
-
# ===========================================
|
|
46
|
-
|
|
47
|
-
net = (
|
|
48
|
-
ballena.network(neurons)
|
|
49
|
-
.set_synpases_in(synapses_in)
|
|
50
|
-
.set_synpases_net(synapses_net)
|
|
51
|
-
.set_outputs([10,11,12]) # Topologia de la red fija
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
def evaluar_weights(weights): # Esta pudiera ser una funcion objetivo
|
|
55
|
-
net.set_weights( weights )
|
|
56
|
-
|
|
57
|
-
resultados = [ net.simulate(instance, t=10).get_spikes() for instance in dataset ]
|
|
58
|
-
return accuracy(resultados, dataset)
|
|
59
|
-
|
|
60
|
-
# =============================
|
|
61
|
-
# CASO DE USO PLOTEAR RESPUESTA
|
|
62
|
-
# =============================
|
|
63
|
-
net = (
|
|
64
|
-
ballena.network(neurons)
|
|
65
|
-
.set_synpases_in(synapses_in)
|
|
66
|
-
.set_synpases_net(synapses_net)
|
|
67
|
-
.net.set_weights( weights )
|
|
68
|
-
.set_outputs([1])
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
voltage = net.simulate(instance, t=10).get_voltage()
|
|
72
|
-
plt.plot(voltage[0])
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
# ==========================
|
|
76
|
-
# CASO DE USO NEUROEVOLUCION
|
|
77
|
-
# ==========================
|
|
78
|
-
|
|
79
|
-
net = ballena.network(neurons).set_synapses_in(synapses_in).set_outputs([2,3])
|
|
80
|
-
|
|
81
|
-
def evaluar_topologia(synapses, weights):
|
|
82
|
-
net.set_synapses_net(synapses).set_weights(weights)
|
|
83
|
-
|
|
84
|
-
resultados = [ net.simulate(instance, t=10).spikes() for instance in dataset ]
|
|
85
|
-
return accuracy(resultados, dataset)
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
# ==================================
|
|
90
|
-
# CASO DE USO NEUROEVOLUCION
|
|
91
|
-
# CON AJUSTE AUTOMATICO DE PESOS
|
|
92
|
-
# ==================================
|
|
93
|
-
|
|
94
|
-
net = (
|
|
95
|
-
ballena.network(neurons)
|
|
96
|
-
.set_synapses_in(synapses_in)
|
|
97
|
-
.set_outputs([2,3])
|
|
98
|
-
.stdp( True ) # Ajuste automatico
|
|
99
|
-
)
|
|
100
|
-
|
|
101
|
-
def evaluar_topologia(synapses):
|
|
102
|
-
net.set_synapses_net(synapses) # se contruye topologia
|
|
103
|
-
|
|
104
|
-
for e in range(epochs): # Se ajustan los pesos
|
|
105
|
-
for instancia in dataset.train():
|
|
106
|
-
res = net.simulate( instancia, t=10 ).get_spikes() # pass forward
|
|
107
|
-
err = calcular_error(res,instancia) # loss function
|
|
108
|
-
net.dopamina( err ) # ajuste pesos
|
|
109
|
-
|
|
110
|
-
res = [ net.simulate(instancia, t=10) for instancia in dataset.validation() ]
|
|
111
|
-
return acc(res, dataset.validation())
|
|
112
|
-
|
|
113
|
-
|
sim_ballena-0.1.2/test_file.py
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import sim_ballena as ballena
|
|
2
|
-
import matplotlib.pyplot as plt
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
net = (ballena.Network( ballena.Lif().tau(5).t_refractory(0).repeat(2) )
|
|
6
|
-
.synapses_net([])
|
|
7
|
-
.weights_net([])
|
|
8
|
-
.outputs([0,1])
|
|
9
|
-
.mode(['VOLTAGES','SPIKES']))
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
# ==========
|
|
14
|
-
# STEP 1
|
|
15
|
-
# ==========
|
|
16
|
-
net = net.synapses_in([(0,0),(0,1),(1,0),(1,1)])
|
|
17
|
-
net = net.weights_in([16,0,-16,0])
|
|
18
|
-
|
|
19
|
-
times = [ (1, 0), (5, 1) ]
|
|
20
|
-
instance = ballena.Instance( times )
|
|
21
|
-
res = net.simulate( instance, 10 )
|
|
22
|
-
|
|
23
|
-
time = res.time()
|
|
24
|
-
volt = res.voltages()
|
|
25
|
-
plt.figure(figsize=(10,4))
|
|
26
|
-
plt.subplot(121)
|
|
27
|
-
plt.plot( time, volt[0], label='true')
|
|
28
|
-
plt.subplot(122)
|
|
29
|
-
plt.plot( time, volt[1], label='false')
|
|
30
|
-
plt.show()
|
|
31
|
-
|
|
32
|
-
# ==========
|
|
33
|
-
# STEP 1
|
|
34
|
-
# ==========
|
|
35
|
-
net = net.weights_in([16,0,-16,0])
|
|
36
|
-
|
|
37
|
-
times = [ (1, 0), (5, 1) ]
|
|
38
|
-
instance = ballena.Instance( times )
|
|
39
|
-
res = net.simulate( instance, 10 )
|
|
40
|
-
|
|
41
|
-
time = res.time()
|
|
42
|
-
volt = res.voltages()
|
|
43
|
-
plt.figure(figsize=(10,4))
|
|
44
|
-
plt.subplot(121)
|
|
45
|
-
plt.plot( time, volt[0], label='true')
|
|
46
|
-
plt.subplot(122)
|
|
47
|
-
plt.plot( time, volt[1], label='false')
|
|
48
|
-
plt.show()
|
|
49
|
-
|
sim_ballena-0.1.2/tests.ipynb
DELETED
|
@@ -1,299 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"cells": [
|
|
3
|
-
{
|
|
4
|
-
"cell_type": "code",
|
|
5
|
-
"execution_count": 1,
|
|
6
|
-
"metadata": {},
|
|
7
|
-
"outputs": [
|
|
8
|
-
{
|
|
9
|
-
"name": "stdout",
|
|
10
|
-
"output_type": "stream",
|
|
11
|
-
"text": [
|
|
12
|
-
"\n",
|
|
13
|
-
" -- N E S T --\n",
|
|
14
|
-
" Copyright (C) 2004 The NEST Initiative\n",
|
|
15
|
-
"\n",
|
|
16
|
-
" Version: 3.9.0\n",
|
|
17
|
-
" Built: Sep 26 2025 04:43:22\n",
|
|
18
|
-
"\n",
|
|
19
|
-
" This program is provided AS IS and comes with\n",
|
|
20
|
-
" NO WARRANTY. See the file LICENSE for details.\n",
|
|
21
|
-
"\n",
|
|
22
|
-
" Problems or suggestions?\n",
|
|
23
|
-
" Visit https://www.nest-simulator.org\n",
|
|
24
|
-
"\n",
|
|
25
|
-
" Type 'nest.help()' to find out more about NEST.\n",
|
|
26
|
-
"\n"
|
|
27
|
-
]
|
|
28
|
-
}
|
|
29
|
-
],
|
|
30
|
-
"source": [
|
|
31
|
-
"import nest\n",
|
|
32
|
-
"import matplotlib.pyplot as plt\n",
|
|
33
|
-
"\n",
|
|
34
|
-
"import sim_ballena as ballena\n",
|
|
35
|
-
"import time\n",
|
|
36
|
-
"import numpy as np\n"
|
|
37
|
-
]
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
"cell_type": "code",
|
|
41
|
-
"execution_count": 2,
|
|
42
|
-
"metadata": {},
|
|
43
|
-
"outputs": [],
|
|
44
|
-
"source": [
|
|
45
|
-
"def spike_generator(max_time, dt, n_spikes, ch=0):\n",
|
|
46
|
-
"\tassert n_spikes<((max_time/dt)-1), \"No puede haber mas spikes que timesteps\"\n",
|
|
47
|
-
"\tprob = n_spikes/((max_time/dt)-1)\n",
|
|
48
|
-
"\n",
|
|
49
|
-
"\tspikes = []\n",
|
|
50
|
-
"\tt = dt\n",
|
|
51
|
-
"\twhile t<max_time:\n",
|
|
52
|
-
"\t\tif np.random.random()<prob:\n",
|
|
53
|
-
"\t\t\tspikes.append((t,ch))\n",
|
|
54
|
-
"\t\tt = round(t+dt, 6)\n",
|
|
55
|
-
"\treturn spikes"
|
|
56
|
-
]
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
"cell_type": "code",
|
|
60
|
-
"execution_count": 3,
|
|
61
|
-
"metadata": {},
|
|
62
|
-
"outputs": [],
|
|
63
|
-
"source": [
|
|
64
|
-
"# ======================\n",
|
|
65
|
-
"# SIMULATE NEST PLOT\n",
|
|
66
|
-
"# ======================\n",
|
|
67
|
-
"def simulate_nest_voltages(inputs, w_in, w_net, tau=[10,10], refrac=2, max_time=20):\n",
|
|
68
|
-
"\tinput_t,_ = zip(*inputs)\n",
|
|
69
|
-
"\tnest.ResetKernel()\n",
|
|
70
|
-
"\tnest.set_verbosity(\"M_FATAL\")\n",
|
|
71
|
-
"\tnest.resolution = 0.01\n",
|
|
72
|
-
"\n",
|
|
73
|
-
"\t# ===============\n",
|
|
74
|
-
"\t# CREATE OBJECTS\n",
|
|
75
|
-
"\tI = nest.Create(\"spike_generator\", params={\"spike_times\": input_t})\n",
|
|
76
|
-
"\n",
|
|
77
|
-
"\tN0 = nest.Create('iaf_psc_delta', params={'V_m': -70,'V_th': -55,'tau_m': tau[0],'t_ref': refrac,'V_reset': -70})\n",
|
|
78
|
-
"\tN1 = nest.Create('iaf_psc_delta', params={'V_m': -70,'V_th': -55,'tau_m': tau[1],'t_ref': 0 ,'V_reset': -70})\n",
|
|
79
|
-
"\n",
|
|
80
|
-
"\tmultimeter = nest.Create(\"multimeter\")\n",
|
|
81
|
-
"\tmultimeter.interval = 0.01\n",
|
|
82
|
-
"\tmultimeter.set(record_from=[\"V_m\"])\n",
|
|
83
|
-
"\t\n",
|
|
84
|
-
"\tnest.Connect(I, N0, syn_spec={\"weight\":w_in, \"delay\":0.01})\n",
|
|
85
|
-
"\tnest.Connect(N0, N1, syn_spec={\"weight\":w_net, \"delay\":0.01})\n",
|
|
86
|
-
"\tnest.Connect(multimeter, N0)\n",
|
|
87
|
-
"\tnest.Connect(multimeter, N1)\n",
|
|
88
|
-
"\n",
|
|
89
|
-
"\t# ===============\n",
|
|
90
|
-
"\t# RUN AND PLOT\n",
|
|
91
|
-
"\tnest.Simulate(max_time)\n",
|
|
92
|
-
"\n",
|
|
93
|
-
"\tv0 = multimeter.get('events')['V_m'][::2]\n",
|
|
94
|
-
"\tv1 = multimeter.get('events')['V_m'][1::2]\n",
|
|
95
|
-
"\tt0 = multimeter.get('events')['times'][::2]\n",
|
|
96
|
-
"\tt1 = multimeter.get('events')['times'][1::2]\n",
|
|
97
|
-
"\tplt.plot( t0, v0, label='nest neu0')\n",
|
|
98
|
-
"\tplt.plot( t1, v1, label='nest neu1')\n",
|
|
99
|
-
"\n",
|
|
100
|
-
"# =======================\n",
|
|
101
|
-
"# SIMULATE BALLENA PLOT\n",
|
|
102
|
-
"# =======================\n",
|
|
103
|
-
"def simulate_ballena_voltages(inputs,w_in,w_net,tau=[10,10], refrac=2,max_time=20):\n",
|
|
104
|
-
"\tinstance = ballena.Instance(inputs)\n",
|
|
105
|
-
"\n",
|
|
106
|
-
"\tn = [ballena.Lif().tau(tau[0]).t_refractory(refrac),\n",
|
|
107
|
-
"\t \t ballena.Lif().tau(tau[1]).t_refractory(0)]\n",
|
|
108
|
-
"\n",
|
|
109
|
-
"\tnet = (ballena.Network(n)\n",
|
|
110
|
-
"\t\t\t.synapses_in([(0,0)])\n",
|
|
111
|
-
"\t\t\t.synapses_net([(0,1)])\n",
|
|
112
|
-
"\t\t\t.weights_in([w_in])\n",
|
|
113
|
-
"\t\t\t.weights_net([w_net])\n",
|
|
114
|
-
"\t\t\t.outputs([0,1])\n",
|
|
115
|
-
"\t\t\t.mode(['VOLTAGES']))\n",
|
|
116
|
-
"\tres = net.simulate( instance, max_time ).set_resolution(0.001)\n",
|
|
117
|
-
"\ttime = res.time()\n",
|
|
118
|
-
"\tvolt = res.voltages()\n",
|
|
119
|
-
"\n",
|
|
120
|
-
"\tplt.plot( time, volt[0], label='Ballena neu0' )\n",
|
|
121
|
-
"\tplt.plot( time, volt[1], label='Ballena neu1' )\n"
|
|
122
|
-
]
|
|
123
|
-
},
|
|
124
|
-
{
|
|
125
|
-
"cell_type": "code",
|
|
126
|
-
"execution_count": 4,
|
|
127
|
-
"metadata": {},
|
|
128
|
-
"outputs": [
|
|
129
|
-
{
|
|
130
|
-
"data": {
|
|
131
|
-
"image/png": "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rrnmGlasWFHucktOT3300UckJSURFRXFfffdR25ubvmxLT0337lz5xIdHV1qvkuWLEFRFMfvSUlJvPrqqzz44INERUU5nRd3MHh1aUIIIQKLOR9equX1xSoDdqIawyv12by8PObNm0f9+vWpVq2a4/WIiAjmzp1LrVq1+P333xk8eDARERE8/fTTTs136tSpzJs3j7fffpsGDRqwZs0a+vXrR1xcHDfddJNjunHjxjFr1izi4uIYOnQoDz30EOvWrXPE1qNHD6ZMmYLJZOLDDz+kV69e7Nq1i7p165a57D179rBkyRKWLl3KyZMnueeee5g2bRpTpkwpO7ahQ4irFsNNbVtXJo1eJcWKEEKIgLd06VKqVq0KwJkzZ6hZsyZLly5Fpzt3guG5555z/JyUlMTo0aNZuHChU8VKUVERL730EitWrKBt27YApKSksHbtWt55551SxcqUKVMcv48ZM4aePXtSWFhIaGgozZs3L3WKZfLkyXzxxRd89dVXDBs2rMzl22w25s6dS0REBAD9+/dn5cqVTJkypezYln/JO/M+l2JFCCFEgDOGw7OHvL5Y9XBxhabv2LEjb731FgAnT57kzTff5JZbbmHTpk3Uq1cPgE8++YTXXnuNPXv2kJeXh8ViITIy0qn5//333+Tn59OlS5dSrxcXF9OyZctSrzVr1szxc82aNQE4cuQIdevWJS8vj4kTJ7Js2TKysrKwWCwUFBSQmZlZ7vKTkpIchUrJfI8cOXKZ2Ipo2bSRU9/P16RYEUIIUXmKAiFVfLBc58eSAFSpUoX69es7fv/f//5HVFQU7777Li+++CIbNmzggQceYNKkSXTr1o2oqCgWLlzIrFmznJp/Xl4eAMuWLaN27dql3jOZTKV+NxqN577G2TEhNpsNgNGjR5Oens7MmTOpX78+YWFh3H333RQXl1+cnT/PkvmWzLPM2A7vwBQSAoBOp7tobGRFxut4mhQrQgghgo6iKOh0OgoKCgBYv3499erVY9y4cY5p9u/f7/T8mjRpgslkIjMzs9Qpn4pat24dAwcO5I477gDshUZGRkal51dubOHnBuDGxcWRm5vLmTNnqFLFXnxu3brVpeW6kxQrQgghAl5RURHZ2dmA/TTQ66+/Tl5eHr169QKgQYMGZGZmsnDhQq655hqWLVvGF1984fT8IyIiGD16NCNHjsRms3H9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Qggh/JoUK36rrPPccv7bVZLBiig7WzIWw0nlpEkyKPyBFtqhFCv+Tu5l4QaSQ1dJBoX/kVYZTKRY0QxZMV0mKXQLVZVEukoyKETFSLEihBBCCL8mxYoIWDKmwnUauhu335J26A4lOZQ+qWAlxYqfk3szuE5y6A6SQ9dJDoWoLClWhBBCCOHXpFjxW9J1LPyBtEPXSQ6FcJUUKyLoyK7DXSSTrpIMCl/Q4glJKVb83MXjLWTzVmFaXDP9jiSxslQZpRzw5s6dS3R0tK/DcMjKyqJv3740bNgQnU5X6nb+WiXFikbIveFcJwNthRDBoKioiLi4OJ577jmaN2/u63DcQooVvyVHY66THLpOcijKp6oqtvx8z/4rKICCQigodLxWkR6rDh068MQTT/D0008TGxtLQkLCRQ8UPHXqFI888ghxcXFERkZy8803s23bNsf727Zto2PHjkRERBAZGUnr1q35+eefWb16NYMGDeL06dMoioKiKBfNu8TEiRNp0aIFH330EUlJSURFRXHfffeRm5vrmMZmszF16lSSk5MJCwujefPmfPbZZ473L9WLs2TJEpTzjmiTkpJ49dVXefDBB4mKirpEJNo7cJOnLgshhKg0taCAXa1ae3w5JUfWu87+r6R/AWHOf/6DDz5g1KhRbNy4kQ0bNjBw4EDat29Ply5dAOjTpw9hYWF88803REVF8c4779CpUyd2795NbGwsDzzwAC1btuStt95Cr9ezdetWjEYj7dq14z//+Q/PP/88u3bZo6tatWqZcezZs4clS5awdOlSTp48yT333MO0adOYMmUKAFOnTmXevHm8/fbbNGjQgDVr1tCvXz/i4uK46aabKpq2gCHFip/TXv3rf+T0j+skg+4gWfSlZs2aMWHCBAAaNGjA66+/zsqVK+nSpQtr165l06ZNHDlyBJPJBMDMmTNZsmQJn332GY8++iiZmZk89dRTNGrUyDGPElFRUSiKQkJCwmXjsNlszJ07l4iICAD69+/PypUrmTJlCkVFRbz00kusWLGCtm3bApCSksLatWt55513pFgRQgghKkMJC+PKLb94dBk5RQUczMsAdDSudiUAu88cBvKdnkezZs1K/V6zZk2OHDkC2E/x5OXlUa1atVLTFBQUsGfPHgBGjRrFI488wkcffUTnzp3p06cPV1xxRYW/S1JSkqNQuTCOv//+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",
|
|
132
|
-
"text/plain": [
|
|
133
|
-
"<Figure size 640x480 with 1 Axes>"
|
|
134
|
-
]
|
|
135
|
-
},
|
|
136
|
-
"metadata": {},
|
|
137
|
-
"output_type": "display_data"
|
|
138
|
-
}
|
|
139
|
-
],
|
|
140
|
-
"source": [
|
|
141
|
-
"inputs = [(t,0) for t in range(1,100,1)]\n",
|
|
142
|
-
"\n",
|
|
143
|
-
"simulate_ballena_voltages(inputs,w_in=2, w_net=9, tau=[100,50], refrac=10, max_time=100)\n",
|
|
144
|
-
"simulate_nest_voltages(inputs, w_in=2, w_net=9, tau=[100,50], refrac=10, max_time=100)\n",
|
|
145
|
-
"plt.legend()\n",
|
|
146
|
-
"plt.grid()\n",
|
|
147
|
-
"plt.show()\n"
|
|
148
|
-
]
|
|
149
|
-
},
|
|
150
|
-
{
|
|
151
|
-
"cell_type": "code",
|
|
152
|
-
"execution_count": 5,
|
|
153
|
-
"metadata": {},
|
|
154
|
-
"outputs": [],
|
|
155
|
-
"source": [
|
|
156
|
-
"# ========================\n",
|
|
157
|
-
"# SIMULATE NEST VELOCITY\n",
|
|
158
|
-
"# ========================\n",
|
|
159
|
-
"def simulate_nest_fast(inputs, w, tau=10, max_time=20, epochs=100):\n",
|
|
160
|
-
" input_t,_ = zip(*inputs)\n",
|
|
161
|
-
" \n",
|
|
162
|
-
" # MUEVE LA CONFIGURACIÓN FUERA DEL LOOP\n",
|
|
163
|
-
" nest.ResetKernel()\n",
|
|
164
|
-
" nest.set_verbosity(\"M_FATAL\")\n",
|
|
165
|
-
" nest.resolution = 0.01\n",
|
|
166
|
-
" \n",
|
|
167
|
-
" # CREA OBJETOS UNA SOLA VEZ\n",
|
|
168
|
-
" I = nest.Create(\"spike_generator\", params={\"spike_times\": input_t})\n",
|
|
169
|
-
" N1 = nest.Create('iaf_psc_delta', params={'V_m': -70,'V_th': -55,'tau_m': tau,'t_ref': 0,'V_reset': -70})\n",
|
|
170
|
-
" N2 = nest.Create('iaf_psc_delta', params={'V_m': -70,'V_th': -55,'tau_m': tau,'t_ref': 0,'V_reset': -70})\n",
|
|
171
|
-
" N3 = nest.Create('iaf_psc_delta', params={'V_m': -70,'V_th': -55,'tau_m': tau,'t_ref': 0,'V_reset': -70})\n",
|
|
172
|
-
" \n",
|
|
173
|
-
" nest.Connect(I, N1, syn_spec={\"weight\":w, \"delay\":0.1})\n",
|
|
174
|
-
" nest.Connect(I, N2, syn_spec={\"weight\":w, \"delay\":0.1})\n",
|
|
175
|
-
" nest.Connect(I, N3, syn_spec={\"weight\":w, \"delay\":0.1})\n",
|
|
176
|
-
" \n",
|
|
177
|
-
" # SOLO SIMULA EN EL LOOP\n",
|
|
178
|
-
" start = time.time()\n",
|
|
179
|
-
" for _ in range(epochs):\n",
|
|
180
|
-
" nest.SetStatus(N1, {\"V_m\": -70}) # Resetear estado\n",
|
|
181
|
-
" nest.SetStatus(N2, {\"V_m\": -70}) # Resetear estado\n",
|
|
182
|
-
" nest.SetStatus(N3, {\"V_m\": -70}) # Resetear estado\n",
|
|
183
|
-
" nest.Simulate(max_time)\n",
|
|
184
|
-
" return time.time()-start\n",
|
|
185
|
-
"\n",
|
|
186
|
-
"# ===========================\n",
|
|
187
|
-
"# SIMULATE BALLENA VELOCITY\n",
|
|
188
|
-
"# ===========================\n",
|
|
189
|
-
"def simulate_ballena_fast(inputs,w,tau=10,max_time=20, epochs=100):\n",
|
|
190
|
-
"\tinstance = ballena.Instance(inputs)\n",
|
|
191
|
-
"\n",
|
|
192
|
-
"\tn = ballena.Lif().tau(tau).repeat(3)\n",
|
|
193
|
-
"\tnet = (ballena.Network(n)\n",
|
|
194
|
-
"\t\t\t.synapses_in([(0,0),(0,1),(0,2)])\n",
|
|
195
|
-
"\t\t\t.synapses_net([])\n",
|
|
196
|
-
"\t\t\t.weights_in([w]*3)\n",
|
|
197
|
-
"\t\t\t.weights_net([])\n",
|
|
198
|
-
"\t\t\t.outputs([0]))\n",
|
|
199
|
-
"\tstart = time.time()\n",
|
|
200
|
-
"\tfor _ in range(epochs):\n",
|
|
201
|
-
"\t\tres = net.simulate( instance, max_time )\n",
|
|
202
|
-
"\n",
|
|
203
|
-
"\treturn time.time()-start\n"
|
|
204
|
-
]
|
|
205
|
-
},
|
|
206
|
-
{
|
|
207
|
-
"cell_type": "code",
|
|
208
|
-
"execution_count": 6,
|
|
209
|
-
"metadata": {},
|
|
210
|
-
"outputs": [],
|
|
211
|
-
"source": [
|
|
212
|
-
"# inputs = spike_generator(100,0.01,100)\n",
|
|
213
|
-
"\n",
|
|
214
|
-
"# t_ballena = simulate_ballena_fast(inputs, w=4, tau=4, max_time=100, epochs=5000)\n",
|
|
215
|
-
"# t_nest = simulate_nest_fast(inputs, w=4, tau=4, max_time=100, epochs=5000)\n",
|
|
216
|
-
"\n",
|
|
217
|
-
"# print('t_ballena', t_ballena)\n",
|
|
218
|
-
"# print('t_nest', t_nest)\n",
|
|
219
|
-
"# print(f'ballena es {t_nest/t_ballena} veces mas rapido')"
|
|
220
|
-
]
|
|
221
|
-
},
|
|
222
|
-
{
|
|
223
|
-
"cell_type": "code",
|
|
224
|
-
"execution_count": null,
|
|
225
|
-
"metadata": {},
|
|
226
|
-
"outputs": [],
|
|
227
|
-
"source": [
|
|
228
|
-
"poisson = ballena.PoissonGenerator( [200,100,50], 0.05 )"
|
|
229
|
-
]
|
|
230
|
-
},
|
|
231
|
-
{
|
|
232
|
-
"cell_type": "code",
|
|
233
|
-
"execution_count": 8,
|
|
234
|
-
"metadata": {},
|
|
235
|
-
"outputs": [
|
|
236
|
-
{
|
|
237
|
-
"name": "stdout",
|
|
238
|
-
"output_type": "stream",
|
|
239
|
-
"text": [
|
|
240
|
-
"[11, 3, 1]\n"
|
|
241
|
-
]
|
|
242
|
-
},
|
|
243
|
-
{
|
|
244
|
-
"data": {
|
|
245
|
-
"text/plain": [
|
|
246
|
-
"[]"
|
|
247
|
-
]
|
|
248
|
-
},
|
|
249
|
-
"execution_count": 8,
|
|
250
|
-
"metadata": {},
|
|
251
|
-
"output_type": "execute_result"
|
|
252
|
-
},
|
|
253
|
-
{
|
|
254
|
-
"data": {
|
|
255
|
-
"image/png": "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juLi4hM4EQCQSpK/Z6SpqSlGjx6d4q0BgJNM3ndGDh48GK+//nrn12+88UY0NTXFGWecEePGjYtly5bFW2+9Fffdd19ERKxcuTImTJgQ5513XnzwwQdxzz33xNNPPx2///3ve+8sAIB+K+8Y2bZtW1xyySWdXy9dujQiIhYsWBDr1q2Lffv2xe7duztfb29vj5tuuineeuutGDZsWHzxi1+Mp556qsv3AAAGr+N6gPVEOdoHYACAk8fR/vz2b9MAAEmJEQAgKTECACQlRgCApMQIAJCUGAEAkhIjAEBSYgQASEqMAABJiREAICkxAgAkJUYAgKTECACQlBgBAJISIwBAUmIEAEhKjAAASYkRACApMQIAJCVGAICkxAgAkJQYAQCSEiMAQFJiBABISowAAEmJEQAgKTECACQlRgCApMQIAJCUGAEAkhIjAEBSYgQASEqMAABJiREAICkxAgAkJUaAfu299o9i/K2bYvytm+K99o8G7QxwLE6W/3fFCACQlBgBAJISIwBAUmIEAEhKjAAASYkRACApMQIAJCVGAICkxAgAkJQYAQCSEiMAQFJiBABISowAAEmJEQAgKTECACQlRgCApMQIAJCUGAEAkhIjAEBSYgQASEqMAABJiREAICkxAgAklXeMPPvss3H55ZdHZWVlFBQUxMaNG494zObNm+OCCy6I4uLiOPvss2PdunXHMCoAMBDlHSOHDh2K6urqWLVq1VGtf+ONN2LOnDlxySWXRFNTU9x4441x3XXXxZNPPpn3sADAwFOY7wGzZ8+O2bNnH/X61atXx4QJE2LFihUREXHOOefEc889F3feeWfMmjUr37cHAAaYPn9mpLGxMWpra7vsmzVrVjQ2NvZ4TFtbW+RyuS4bADAw9XmMNDc3R3l5eZd95eXlkcvl4v333+/2mLq6uigrK+vcqqqq+npMACCRk/LTNMuWLYvW1tbObc+ePalHAgD6SN7PjOSroqIiWlpauuxraWmJ0tLSOPXUU7s9pri4OIqLi/t6NADgJNDnd0ZqamqioaGhy776+vqoqanp67cGAPqBvGPk4MGD0dTUFE1NTRHx74/uNjU1xe7duyPi33/EMn/+/M71119/ffz973+P73znO/Hqq6/GL37xi3jggQfi29/+du+cAQDQr+UdI9u2bYspU6bElClTIiJi6dKlMWXKlLj99tsjImLfvn2dYRIRMWHChNi0aVPU19dHdXV1rFixIu655x4f6wUAIuIYnhmZOXNmZFnW4+vd/e2qM2fOjB07duT7VgDAIHBSfpoGABg8xAgAkJQYAQCSEiMAQFJiBABISowAAEmJEQAgKTECACQlRgCApMQIAJCUGAEAkhIjAEBSYgQASEqMAABJiREAICkxAgAkJUYAgKTECACQlBgBAJISIwBAUmIEAEiqIMuyLPUQR5LL5aKsrCxaW1ujtLQ09TgAwFE42p/f7owAAEmJEQAgKTECACQlRgCApMQIAJCUGAEAkhIjAEBSYgQASEqMAABJiREAICkxAgAkJUYAgKTECACQlBgBAJISIwBAUmIEAEhKjAAASYkRACApMQIAJCVGAICkxAgAkJQYAQCSEiMAQFJiBABISowAAEmJEQAgKTECACQlRgCApMQIAJCUGAEAkhIjAEBSYgQASEqMAABJiREAICkxAgAkJUYAgKTECACQ1DHFyKpVq2L8+PFRUlIS06dPj61bt/a4dt26dVFQUNBlKykpOeaBAYCBJe8Y2bBhQyxdujSWL18eL774YlRXV8esWbNi//79PR5TWloa+/bt69x27dp1XEMDAANH3jFyxx13xKJFi2LhwoVx7rnnxurVq2PYsGGxdu3aHo8pKCiIioqKzq28vPy4hgYABo68YqS9vT22b98etbW1//kGQ4ZEbW1tNDY29njcwYMH46yzzoqqqqr42te+Fi+//PKnvk9bW1vkcrkuGwAwMOUVI++8804cPnz4E3c2ysvLo7m5udtjvvCFL8TatWvj0Ucfjd/85jfR0dERF198cbz55ps9vk9dXV2UlZV1blVVVfmMCQD0I33+aZqampqYP39+TJ48OWbMmBEPP/xwfPazn43/+7//6/GYZcuWRWtra+e2Z8+evh4TAEikMJ/FI0eOjKFDh0ZLS0uX/S0tLVFRUXFU3+OUU06JKVOmxOuvv97jmuLi4iguLs5nNACgn8rrzkhRUVFMnTo1GhoaOvd1dHREQ0ND1NTUHNX3OHz4cLz00ksxevTo/CYFAAakvO6MREQsXbo0FixYEBdeeGFMmzYtVq5cGYcOHYqFCxdGRMT8+fNjzJgxUVdXFxERP/jBD+Kiiy6Ks88+O9599934yU9+Ert27Yrrrruud88EAOiX8o6Rq666Kt5+++24/fbbo7m5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",
|
|
256
|
-
"text/plain": [
|
|
257
|
-
"<Figure size 640x480 with 1 Axes>"
|
|
258
|
-
]
|
|
259
|
-
},
|
|
260
|
-
"metadata": {},
|
|
261
|
-
"output_type": "display_data"
|
|
262
|
-
}
|
|
263
|
-
],
|
|
264
|
-
"source": [
|
|
265
|
-
"poisson = ballena.PoissonGenerator( [200,100,50], 0.05 )\n",
|
|
266
|
-
"\n",
|
|
267
|
-
"\n",
|
|
268
|
-
"spikes = poisson.get_spikes()\n",
|
|
269
|
-
"print( [len(spk) for spk in spikes] )\n",
|
|
270
|
-
"\n",
|
|
271
|
-
"for idx,ch in enumerate(spikes):\n",
|
|
272
|
-
" plt.vlines(ch, idx-0.2, idx+0.2)\n",
|
|
273
|
-
" \n",
|
|
274
|
-
"plt.plot()"
|
|
275
|
-
]
|
|
276
|
-
}
|
|
277
|
-
],
|
|
278
|
-
"metadata": {
|
|
279
|
-
"kernelspec": {
|
|
280
|
-
"display_name": ".venv",
|
|
281
|
-
"language": "python",
|
|
282
|
-
"name": "python3"
|
|
283
|
-
},
|
|
284
|
-
"language_info": {
|
|
285
|
-
"codemirror_mode": {
|
|
286
|
-
"name": "ipython",
|
|
287
|
-
"version": 3
|
|
288
|
-
},
|
|
289
|
-
"file_extension": ".py",
|
|
290
|
-
"mimetype": "text/x-python",
|
|
291
|
-
"name": "python",
|
|
292
|
-
"nbconvert_exporter": "python",
|
|
293
|
-
"pygments_lexer": "ipython3",
|
|
294
|
-
"version": "3.12.3"
|
|
295
|
-
}
|
|
296
|
-
},
|
|
297
|
-
"nbformat": 4,
|
|
298
|
-
"nbformat_minor": 2
|
|
299
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|