diff --git a/.gitignore b/.gitignore
index d0a1ba4e..36ad3c27 100644
--- a/.gitignore
+++ b/.gitignore
@@ -274,6 +274,9 @@ Thumbs.db #thumbnail cache on Windows
/examples/PyTorch/simple_pytorch_to_mdf.1
/examples/MDF/NLP/TestNLP_3
/examples/MDF/NLP/TestNLP
+/examples/MDF/networks/Net_acyclical
+/examples/MDF/net/Net_acyclical
+/examples/MDF/net/weight.py
/examples/MDF/conditions/composite_example
/examples/NeuroML/arm64
/examples/MDF/RNN/iaf.net
diff --git a/docs/MDF_specification.json b/docs/MDF_specification.json
index 202a92c7..eeaca631 100644
--- a/docs/MDF_specification.json
+++ b/docs/MDF_specification.json
@@ -106,6 +106,10 @@
"type": "str",
"description": "The unique (for this Node) id of the input port,"
},
+ "default_value": {
+ "type": "Union[EvaluableExpression, List, Dict, ndarray, int, float, str, NoneType]",
+ "description": "Value to set at this input port if no edge connected to it."
+ },
"shape": {
"type": "Union[Tuple[int, ...], NoneType]",
"description": "The shape of the input port. This uses the same syntax as numpy ndarray shapes\n(e.g., :code:`numpy.zeros(shape)` would produce an array with the correct shape"
@@ -113,6 +117,10 @@
"type": {
"type": "Union[str, NoneType]",
"description": "The data type of the input received at a port."
+ },
+ "reduce": {
+ "type": "Union[str, NoneType]",
+ "description": "Specifies how to deal with multiple inputs to one port during a single timestep: add: add up all the values; multiply: multiply the values, overwrite: just use the last value supplied (default)"
}
}
},
diff --git a/docs/MDF_specification.yaml b/docs/MDF_specification.yaml
index aa520e8d..c1d4fc2f 100644
--- a/docs/MDF_specification.yaml
+++ b/docs/MDF_specification.yaml
@@ -91,6 +91,11 @@ specification:
id:
type: str
description: The unique (for this Node) id of the input port,
+ default_value:
+ type: Union[EvaluableExpression, List, Dict, ndarray, int, float,
+ str, NoneType]
+ description: Value to set at this input port if no edge connected
+ to it.
shape:
type: Union[Tuple[int, ...], NoneType]
description: 'The shape of the input port. This uses the same syntax
@@ -101,6 +106,12 @@ specification:
type:
type: Union[str, NoneType]
description: The data type of the input received at a port.
+ reduce:
+ type: Union[str, NoneType]
+ description: 'Specifies how to deal with multiple inputs to one port
+ during a single timestep: add: add up all the values; multiply:
+ multiply the values, overwrite: just use the last value supplied
+ (default)'
Function:
definition: A single value which is evaluated as a function of values on :class:`InputPort`(s)
and other Functions
diff --git a/docs/README.md b/docs/README.md
index 0db2109c..8b2365af 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -179,6 +179,13 @@ The InputPort is an attribute of a Node which allows ex
+
+ default_value |
+ Union[EvaluableExpression, List, Dict, ndarray, int, float, str, NoneType] |
+ Value to set at this input port if no edge connected to it. |
+
+
+
shape |
Union[Tuple[int, ...], NoneType] |
@@ -194,6 +201,13 @@ The InputPort is an attribute of a Node which allows ex
+
+ reduce |
+ Union[str, NoneType] |
+ Specifies how to deal with multiple inputs to one port during a single timestep: add: add up all the values; multiply: multiply the values, overwrite: just use the last value supplied (default) |
+
+
+
## Function
diff --git a/docs/sphinx/source/api/Specification.rst b/docs/sphinx/source/api/Specification.rst
index 18761b00..6e5f1cf0 100644
--- a/docs/sphinx/source/api/Specification.rst
+++ b/docs/sphinx/source/api/Specification.rst
@@ -87,15 +87,17 @@ The `InputPort <#inputport>`__ is an attribute of a Node which allows external i
**Allowed parameters**
-=============== ================================ =============================================================================================
-Allowed field Data Type Description
-=============== ================================ =============================================================================================
-**metadata** Union[Any, NoneType] Optional metadata field, an arbitrary dictionary of string keys and JSON serializable values.
-**id** str The unique (for this Node) id of the input port,
-**shape** Union[Tuple[int, ...], NoneType] The shape of the input port. This uses the same syntax as numpy ndarray shapes
- (e.g., **numpy.zeros(shape)** would produce an array with the correct shape
-**type** Union[str, NoneType] The data type of the input received at a port.
-=============== ================================ =============================================================================================
+================= ========================================================================== =================================================================================================================================================================================================
+Allowed field Data Type Description
+================= ========================================================================== =================================================================================================================================================================================================
+**metadata** Union[Any, NoneType] Optional metadata field, an arbitrary dictionary of string keys and JSON serializable values.
+**id** str The unique (for this Node) id of the input port,
+**default_value** Union[EvaluableExpression, List, Dict, ndarray, int, float, str, NoneType] Value to set at this input port if no edge connected to it.
+**shape** Union[Tuple[int, ...], NoneType] The shape of the input port. This uses the same syntax as numpy ndarray shapes
+ (e.g., **numpy.zeros(shape)** would produce an array with the correct shape
+**type** Union[str, NoneType] The data type of the input received at a port.
+**reduce** Union[str, NoneType] Specifies how to deal with multiple inputs to one port during a single timestep: add: add up all the values; multiply: multiply the values, overwrite: just use the last value supplied (default)
+================= ========================================================================== =================================================================================================================================================================================================
========
Function
diff --git a/docs/sphinx/source/api/export_format/NeuroML/Izh.png b/docs/sphinx/source/api/export_format/NeuroML/Izh.png
index 0ee1a7b9..077a4381 100644
Binary files a/docs/sphinx/source/api/export_format/NeuroML/Izh.png and b/docs/sphinx/source/api/export_format/NeuroML/Izh.png differ
diff --git a/docs/sphinx/source/api/export_format/NeuroML/IzhikevichTest.gv.png b/docs/sphinx/source/api/export_format/NeuroML/IzhikevichTest.gv.png
index 120eef86..9334cdb1 100644
Binary files a/docs/sphinx/source/api/export_format/NeuroML/IzhikevichTest.gv.png and b/docs/sphinx/source/api/export_format/NeuroML/IzhikevichTest.gv.png differ
diff --git a/docs/sphinx/source/api/export_format/ONNX/abc.png b/docs/sphinx/source/api/export_format/ONNX/abc.png
index e6cdc653..22c62cf2 100644
Binary files a/docs/sphinx/source/api/export_format/ONNX/abc.png and b/docs/sphinx/source/api/export_format/ONNX/abc.png differ
diff --git a/examples/MDF/RNN/IAF_net.json b/examples/MDF/RNN/IAF_net.json
index cf60851e..e93ef7e5 100644
--- a/examples/MDF/RNN/IAF_net.json
+++ b/examples/MDF/RNN/IAF_net.json
@@ -56,7 +56,8 @@
"current_input": {
"shape": [
8
- ]
+ ],
+ "reduce": "add"
}
},
"parameters": {
@@ -140,7 +141,8 @@
"current_input": {
"shape": [
8
- ]
+ ],
+ "reduce": "add"
}
},
"parameters": {
@@ -224,7 +226,8 @@
"spike_input": {
"shape": [
8
- ]
+ ],
+ "reduce": "add"
}
},
"parameters": {
diff --git a/examples/MDF/RNN/IAF_net.yaml b/examples/MDF/RNN/IAF_net.yaml
index a6556eb4..a3e0fea3 100644
--- a/examples/MDF/RNN/IAF_net.yaml
+++ b/examples/MDF/RNN/IAF_net.yaml
@@ -40,6 +40,7 @@ IAF_net:
current_input:
shape:
- 8
+ reduce: add
parameters:
v0:
value:
@@ -99,6 +100,7 @@ IAF_net:
current_input:
shape:
- 8
+ reduce: add
parameters:
v0:
value:
@@ -158,6 +160,7 @@ IAF_net:
spike_input:
shape:
- 8
+ reduce: add
parameters:
syn_tau:
value: 10
diff --git a/examples/MDF/RNN/IAF_net2.json b/examples/MDF/RNN/IAF_net2.json
index 3e29332b..44d49a5f 100644
--- a/examples/MDF/RNN/IAF_net2.json
+++ b/examples/MDF/RNN/IAF_net2.json
@@ -65,7 +65,8 @@
"current_input": {
"shape": [
8
- ]
+ ],
+ "reduce": "add"
}
},
"parameters": {
@@ -149,7 +150,8 @@
"current_input": {
"shape": [
8
- ]
+ ],
+ "reduce": "add"
}
},
"parameters": {
@@ -233,7 +235,8 @@
"spike_input": {
"shape": [
8
- ]
+ ],
+ "reduce": "add"
}
},
"parameters": {
diff --git a/examples/MDF/RNN/IAF_net2.yaml b/examples/MDF/RNN/IAF_net2.yaml
index 3b170ea3..1c64aa20 100644
--- a/examples/MDF/RNN/IAF_net2.yaml
+++ b/examples/MDF/RNN/IAF_net2.yaml
@@ -48,6 +48,7 @@ IAF_net2:
current_input:
shape:
- 8
+ reduce: add
parameters:
v0:
value:
@@ -107,6 +108,7 @@ IAF_net2:
current_input:
shape:
- 8
+ reduce: add
parameters:
v0:
value:
@@ -166,6 +168,7 @@ IAF_net2:
spike_input:
shape:
- 8
+ reduce: add
parameters:
syn_tau:
value: 10
diff --git a/examples/MDF/RNN/IAF_net3.json b/examples/MDF/RNN/IAF_net3.json
new file mode 100644
index 00000000..cc2b342d
--- /dev/null
+++ b/examples/MDF/RNN/IAF_net3.json
@@ -0,0 +1,283 @@
+{
+ "IAF_net3": {
+ "format": "ModECI MDF v0.4",
+ "generating_application": "Python modeci-mdf v0.4.12",
+ "graphs": {
+ "iaf_example": {
+ "nodes": {
+ "current_input_node": {
+ "parameters": {
+ "time": {
+ "default_initial_value": 0,
+ "time_derivative": "1"
+ },
+ "start": {
+ "value": 20
+ },
+ "duration": {
+ "value": 10
+ },
+ "amplitude": {
+ "value": 10
+ },
+ "level": {
+ "value": 0,
+ "conditions": [
+ {
+ "id": "on",
+ "test": "time > start",
+ "value": "amplitude"
+ },
+ {
+ "id": "off",
+ "test": "time > start + duration",
+ "value": "amplitude*0"
+ }
+ ]
+ }
+ },
+ "output_ports": {
+ "current_output": {
+ "value": "level"
+ }
+ }
+ },
+ "current_input_node2": {
+ "parameters": {
+ "time": {
+ "default_initial_value": 0,
+ "time_derivative": "1"
+ },
+ "start": {
+ "value": 60
+ },
+ "duration": {
+ "value": 10
+ },
+ "amplitude": {
+ "value": 3
+ },
+ "level": {
+ "value": 0,
+ "conditions": [
+ {
+ "id": "on",
+ "test": "time > start",
+ "value": "amplitude"
+ },
+ {
+ "id": "off",
+ "test": "time > start + duration",
+ "value": "amplitude*0"
+ }
+ ]
+ }
+ },
+ "output_ports": {
+ "current_output": {
+ "value": "level"
+ }
+ }
+ },
+ "pre": {
+ "input_ports": {
+ "current_input": {
+ "shape": [
+ 1
+ ],
+ "reduce": "add"
+ }
+ },
+ "parameters": {
+ "v0": {
+ "value": [
+ -60
+ ]
+ },
+ "erev": {
+ "value": [
+ -70
+ ]
+ },
+ "tau": {
+ "value": 10.0
+ },
+ "thresh": {
+ "value": [
+ -20
+ ]
+ },
+ "spiking": {
+ "default_initial_value": "0",
+ "conditions": [
+ {
+ "id": "is_spiking",
+ "test": "v >= thresh",
+ "value": "1"
+ },
+ {
+ "id": "not_spiking",
+ "test": "v < thresh",
+ "value": "0"
+ }
+ ]
+ },
+ "v": {
+ "default_initial_value": "v0",
+ "time_derivative": "-1 * (v-erev)/tau + current_input",
+ "conditions": [
+ {
+ "id": "reset",
+ "test": "v > thresh",
+ "value": "erev"
+ }
+ ]
+ }
+ },
+ "output_ports": {
+ "v_output": {
+ "value": "v"
+ },
+ "spiking_output": {
+ "value": "spiking"
+ }
+ }
+ },
+ "post": {
+ "input_ports": {
+ "current_input": {
+ "shape": [
+ 1
+ ],
+ "reduce": "add"
+ }
+ },
+ "parameters": {
+ "v0": {
+ "value": [
+ -60
+ ]
+ },
+ "erev": {
+ "value": [
+ -70
+ ]
+ },
+ "tau": {
+ "value": 10.0
+ },
+ "thresh": {
+ "value": [
+ -20
+ ]
+ },
+ "spiking": {
+ "default_initial_value": "0",
+ "conditions": [
+ {
+ "id": "is_spiking",
+ "test": "v >= thresh",
+ "value": "1"
+ },
+ {
+ "id": "not_spiking",
+ "test": "v < thresh",
+ "value": "0"
+ }
+ ]
+ },
+ "v": {
+ "default_initial_value": "v0",
+ "time_derivative": "-1 * (v-erev)/tau + current_input",
+ "conditions": [
+ {
+ "id": "reset",
+ "test": "v > thresh",
+ "value": "erev"
+ }
+ ]
+ }
+ },
+ "output_ports": {
+ "v_output": {
+ "value": "v"
+ },
+ "spiking_output": {
+ "value": "spiking"
+ }
+ }
+ },
+ "syn_post": {
+ "input_ports": {
+ "spike_input": {
+ "shape": [
+ 1
+ ],
+ "reduce": "add"
+ }
+ },
+ "parameters": {
+ "syn_tau": {
+ "value": 10
+ },
+ "spike_weights": {
+ "value": [
+ 40
+ ]
+ },
+ "weighted_spike": {
+ "function": "MatMul",
+ "args": {
+ "A": "spike_weights",
+ "B": "spike_input"
+ }
+ },
+ "syn_i": {
+ "default_initial_value": "0",
+ "time_derivative": "-1 * syn_i",
+ "conditions": [
+ {
+ "id": "spike_detected",
+ "test": "spike_input > 0",
+ "value": "weighted_spike"
+ }
+ ]
+ }
+ },
+ "output_ports": {
+ "current_output": {
+ "value": "syn_i"
+ }
+ }
+ }
+ },
+ "edges": {
+ "input_edge": {
+ "sender": "current_input_node",
+ "receiver": "pre",
+ "sender_port": "current_output",
+ "receiver_port": "current_input"
+ },
+ "input_edge2": {
+ "sender": "current_input_node2",
+ "receiver": "pre",
+ "sender_port": "current_output",
+ "receiver_port": "current_input"
+ },
+ "post_internal_edge": {
+ "sender": "syn_post",
+ "receiver": "post",
+ "sender_port": "current_output",
+ "receiver_port": "current_input"
+ },
+ "syn_edge": {
+ "sender": "pre",
+ "receiver": "syn_post",
+ "sender_port": "spiking_output",
+ "receiver_port": "spike_input"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/examples/MDF/RNN/IAF_net3.yaml b/examples/MDF/RNN/IAF_net3.yaml
new file mode 100644
index 00000000..06758477
--- /dev/null
+++ b/examples/MDF/RNN/IAF_net3.yaml
@@ -0,0 +1,178 @@
+IAF_net3:
+ format: ModECI MDF v0.4
+ generating_application: Python modeci-mdf v0.4.12
+ graphs:
+ iaf_example:
+ nodes:
+ current_input_node:
+ parameters:
+ time:
+ default_initial_value: 0
+ time_derivative: '1'
+ start:
+ value: 20
+ duration:
+ value: 10
+ amplitude:
+ value: 10
+ level:
+ value: 0
+ conditions:
+ - id: 'on'
+ test: time > start
+ value: amplitude
+ - id: 'off'
+ test: time > start + duration
+ value: amplitude*0
+ output_ports:
+ current_output:
+ value: level
+ current_input_node2:
+ parameters:
+ time:
+ default_initial_value: 0
+ time_derivative: '1'
+ start:
+ value: 60
+ duration:
+ value: 10
+ amplitude:
+ value: 3
+ level:
+ value: 0
+ conditions:
+ - id: 'on'
+ test: time > start
+ value: amplitude
+ - id: 'off'
+ test: time > start + duration
+ value: amplitude*0
+ output_ports:
+ current_output:
+ value: level
+ pre:
+ input_ports:
+ current_input:
+ shape:
+ - 1
+ reduce: add
+ parameters:
+ v0:
+ value:
+ - -60
+ erev:
+ value:
+ - -70
+ tau:
+ value: 10.0
+ thresh:
+ value:
+ - -20
+ spiking:
+ default_initial_value: '0'
+ conditions:
+ - id: is_spiking
+ test: v >= thresh
+ value: '1'
+ - id: not_spiking
+ test: v < thresh
+ value: '0'
+ v:
+ default_initial_value: v0
+ time_derivative: -1 * (v-erev)/tau + current_input
+ conditions:
+ - id: reset
+ test: v > thresh
+ value: erev
+ output_ports:
+ v_output:
+ value: v
+ spiking_output:
+ value: spiking
+ post:
+ input_ports:
+ current_input:
+ shape:
+ - 1
+ reduce: add
+ parameters:
+ v0:
+ value:
+ - -60
+ erev:
+ value:
+ - -70
+ tau:
+ value: 10.0
+ thresh:
+ value:
+ - -20
+ spiking:
+ default_initial_value: '0'
+ conditions:
+ - id: is_spiking
+ test: v >= thresh
+ value: '1'
+ - id: not_spiking
+ test: v < thresh
+ value: '0'
+ v:
+ default_initial_value: v0
+ time_derivative: -1 * (v-erev)/tau + current_input
+ conditions:
+ - id: reset
+ test: v > thresh
+ value: erev
+ output_ports:
+ v_output:
+ value: v
+ spiking_output:
+ value: spiking
+ syn_post:
+ input_ports:
+ spike_input:
+ shape:
+ - 1
+ reduce: add
+ parameters:
+ syn_tau:
+ value: 10
+ spike_weights:
+ value:
+ - 40
+ weighted_spike:
+ function: MatMul
+ args:
+ A: spike_weights
+ B: spike_input
+ syn_i:
+ default_initial_value: '0'
+ time_derivative: -1 * syn_i
+ conditions:
+ - id: spike_detected
+ test: spike_input > 0
+ value: weighted_spike
+ output_ports:
+ current_output:
+ value: syn_i
+ edges:
+ input_edge:
+ sender: current_input_node
+ receiver: pre
+ sender_port: current_output
+ receiver_port: current_input
+ input_edge2:
+ sender: current_input_node2
+ receiver: pre
+ sender_port: current_output
+ receiver_port: current_input
+ post_internal_edge:
+ sender: syn_post
+ receiver: post
+ sender_port: current_output
+ receiver_port: current_input
+ syn_edge:
+ sender: pre
+ receiver: syn_post
+ sender_port: spiking_output
+ receiver_port: spike_input
diff --git a/examples/MDF/RNN/IAFs.json b/examples/MDF/RNN/IAFs.json
index efcc2a85..7e8ebd9f 100644
--- a/examples/MDF/RNN/IAFs.json
+++ b/examples/MDF/RNN/IAFs.json
@@ -47,7 +47,8 @@
"current_input": {
"shape": [
1
- ]
+ ],
+ "reduce": "add"
}
},
"parameters": {
@@ -110,7 +111,8 @@
"current_input": {
"shape": [
1
- ]
+ ],
+ "reduce": "add"
}
},
"parameters": {
@@ -173,7 +175,8 @@
"spike_input": {
"shape": [
1
- ]
+ ],
+ "reduce": "add"
}
},
"parameters": {
diff --git a/examples/MDF/RNN/IAFs.yaml b/examples/MDF/RNN/IAFs.yaml
index 7c479cc8..c14b276f 100644
--- a/examples/MDF/RNN/IAFs.yaml
+++ b/examples/MDF/RNN/IAFs.yaml
@@ -32,6 +32,7 @@ IAFs:
current_input:
shape:
- 1
+ reduce: add
parameters:
v0:
value:
@@ -70,6 +71,7 @@ IAFs:
current_input:
shape:
- 1
+ reduce: add
parameters:
v0:
value:
@@ -108,6 +110,7 @@ IAFs:
spike_input:
shape:
- 1
+ reduce: add
parameters:
syn_tau:
value: 10
diff --git a/examples/MDF/RNN/IaF.net3.run.png b/examples/MDF/RNN/IaF.net3.run.png
new file mode 100644
index 00000000..4ffbb7e9
Binary files /dev/null and b/examples/MDF/RNN/IaF.net3.run.png differ
diff --git a/examples/MDF/RNN/RNN.run.png b/examples/MDF/RNN/RNN.run.png
index f793e1da..fd86a095 100644
Binary files a/examples/MDF/RNN/RNN.run.png and b/examples/MDF/RNN/RNN.run.png differ
diff --git a/examples/MDF/RNN/RNNs.json b/examples/MDF/RNN/RNNs.json
index 67b6697b..9722517f 100644
--- a/examples/MDF/RNN/RNNs.json
+++ b/examples/MDF/RNN/RNNs.json
@@ -46,6 +46,7 @@
]
},
"fb_input": {
+ "default_value": 0,
"shape": [
5
]
@@ -102,7 +103,7 @@
0.10262953816578246,
0.43893793957112615
],
- "time_derivative": "-x + g*int_fb + ext_input"
+ "time_derivative": "-x + g*int_fb + ext_input + fb_input"
},
"r": {
"function": "tanh",
@@ -185,6 +186,15 @@
"parameters": {
"weight": 1
}
+ },
+ "feedback_edge": {
+ "sender": "readout_node",
+ "receiver": "rnn_node",
+ "sender_port": "z",
+ "receiver_port": "fb_input",
+ "parameters": {
+ "weight": 0.3
+ }
}
}
}
diff --git a/examples/MDF/RNN/RNNs.yaml b/examples/MDF/RNN/RNNs.yaml
index 3589bf9a..03b25311 100644
--- a/examples/MDF/RNN/RNNs.yaml
+++ b/examples/MDF/RNN/RNNs.yaml
@@ -31,6 +31,7 @@ RNNs:
shape:
- 5
fb_input:
+ default_value: 0
shape:
- 5
parameters:
@@ -70,7 +71,7 @@ RNNs:
- -0.5462970928715938
- 0.10262953816578246
- 0.43893793957112615
- time_derivative: -x + g*int_fb + ext_input
+ time_derivative: -x + g*int_fb + ext_input + fb_input
r:
function: tanh
args:
@@ -127,3 +128,10 @@ RNNs:
receiver_port: input
parameters:
weight: 1
+ feedback_edge:
+ sender: readout_node
+ receiver: rnn_node
+ sender_port: z
+ receiver_port: fb_input
+ parameters:
+ weight: 0.3
diff --git a/examples/MDF/RNN/generate_iaf.py b/examples/MDF/RNN/generate_iaf.py
index 42b4ec8f..cf4e3ef3 100644
--- a/examples/MDF/RNN/generate_iaf.py
+++ b/examples/MDF/RNN/generate_iaf.py
@@ -58,7 +58,7 @@ def create_iaf_syn_node(
syn_tau = Parameter(id="syn_tau", value=syn_tau)
syn_node.parameters.append(syn_tau)
- ip_spike = InputPort(id="spike_input", shape="(%i,)" % num_cells)
+ ip_spike = InputPort(id="spike_input", shape="(%i,)" % num_cells, reduce="add")
syn_node.input_ports.append(ip_spike)
spike_weights = Parameter(id="spike_weights", value=numpy.identity(num_cells))
@@ -91,7 +91,7 @@ def create_iaf_syn_node(
## IAF node...
iaf_node = Node(id)
- ip_current = InputPort(id="current_input", shape="(%i,)" % num_cells)
+ ip_current = InputPort(id="current_input", shape="(%i,)" % num_cells, reduce="add")
iaf_node.input_ports.append(ip_current)
v0 = Parameter(
@@ -293,7 +293,8 @@ def main():
recorded = {}
times = []
t = []
- i = []
+ input1 = []
+ input2 = []
s1 = []
sp1 = []
s2 = []
@@ -313,9 +314,15 @@ def main():
% eg.enodes["post"].evaluable_outputs["v_output"].curr_value
)
- i.append(
+ input1.append(
eg.enodes[input_node.id].evaluable_outputs["current_output"].curr_value
)
+ if net3:
+ input2.append(
+ eg.enodes[input_node2.id]
+ .evaluable_outputs["current_output"]
+ .curr_value
+ )
t.append(eg.enodes[input_node.id].evaluable_parameters["time"].curr_value)
s1.append(eg.enodes["pre"].evaluable_outputs["v_output"].curr_value)
sp1.append(eg.enodes["pre"].evaluable_parameters["spiking"].curr_value)
@@ -331,16 +338,18 @@ def main():
markersize = 2 if num_cells < 20 else 0.5
- if type(i[0]) == numpy.ndarray and i[0].size > 1:
- for ii in range(len(i[0])):
+ if type(input1[0]) == numpy.ndarray and input1[0].size > 1:
+ for ii in range(len(input1[0])):
iii = []
for ti in range(len(t)):
- iii.append(i[ti][ii])
+ iii.append(input1[ti][ii])
axis[0].plot(
times, iii, label="Input node %s current" % ii, linewidth="0.5"
)
else:
- axis[0].plot(times, i, label="Input node current", color="k")
+ axis[0].plot(times, input1, label="Input node current", color="k")
+ if net3:
+ axis[0].plot(times, input2, label="Input node current 2", color="g")
if not some_net:
axis[0].legend()
@@ -443,7 +452,7 @@ def main():
engine="dot",
output_format="png",
view_on_render=False,
- level=2,
+ level=1 if net3 else 2,
filename_root="iaf%s"
% (
".net"
diff --git a/examples/MDF/RNN/generate_rnn.py b/examples/MDF/RNN/generate_rnn.py
index 2ce00a50..29cdbfa4 100644
--- a/examples/MDF/RNN/generate_rnn.py
+++ b/examples/MDF/RNN/generate_rnn.py
@@ -87,16 +87,15 @@ def main():
)
mod_graph.edges.append(e2)
- """
e3 = Edge(
id="feedback_edge",
- parameters={"weight": 0.1},
+ parameters={"weight": 0.3},
sender=readout_node.id,
sender_port=readout_node.get_output_port("z").id,
receiver=rnn_node.id,
receiver_port=rnn_node.get_input_port("fb_input").id,
)
- mod_graph.edges.append(e3)"""
+ mod_graph.edges.append(e3)
if N < 100:
new_file = mod.to_json_file("%s.json" % mod.id)
diff --git a/examples/MDF/RNN/iaf.net.png b/examples/MDF/RNN/iaf.net.png
index 30db8f94..1a8a3bd1 100644
Binary files a/examples/MDF/RNN/iaf.net.png and b/examples/MDF/RNN/iaf.net.png differ
diff --git a/examples/MDF/RNN/iaf.net2.png b/examples/MDF/RNN/iaf.net2.png
index 22784bc0..ceec52d2 100644
Binary files a/examples/MDF/RNN/iaf.net2.png and b/examples/MDF/RNN/iaf.net2.png differ
diff --git a/examples/MDF/RNN/iaf.png b/examples/MDF/RNN/iaf.png
index acd54abb..e2785d20 100644
Binary files a/examples/MDF/RNN/iaf.png and b/examples/MDF/RNN/iaf.png differ
diff --git a/examples/MDF/RNN/regenerate.sh b/examples/MDF/RNN/regenerate.sh
index 6a6b140e..9911be35 100755
--- a/examples/MDF/RNN/regenerate.sh
+++ b/examples/MDF/RNN/regenerate.sh
@@ -10,6 +10,7 @@ python generate_iaf.py -net2 -graph
python generate_iaf.py -run -nogui
python generate_iaf.py -run -net -nogui
python generate_iaf.py -run -net2 -nogui
+python generate_iaf.py -run -net3 -nogui
#Fix dimensions!
#python generate_iaf.py -neuroml
#pynml LEMS_Simiaf_example.xml -lems-graph
diff --git a/examples/MDF/RNN/rnn.png b/examples/MDF/RNN/rnn.png
index ea726f10..963bdbc8 100644
Binary files a/examples/MDF/RNN/rnn.png and b/examples/MDF/RNN/rnn.png differ
diff --git a/examples/MDF/RNN/utils.py b/examples/MDF/RNN/utils.py
index 6688add9..1899769f 100644
--- a/examples/MDF/RNN/utils.py
+++ b/examples/MDF/RNN/utils.py
@@ -8,11 +8,11 @@ def create_rnn_node(id, N, g, seed=1234):
## RNN node...
rnn_node = Node(id=id)
- ipr1 = InputPort(id="ext_input", shape="(%i,)" % N)
- rnn_node.input_ports.append(ipr1)
+ ext_ip = InputPort(id="ext_input", shape="(%i,)" % N)
+ rnn_node.input_ports.append(ext_ip)
- ipr2 = InputPort(id="fb_input", shape="(%i,)" % N)
- rnn_node.input_ports.append(ipr2)
+ fb_ip = InputPort(id="fb_input", default_value=0, shape="(%i,)" % N)
+ rnn_node.input_ports.append(fb_ip)
default_initial_value = np.zeros(N)
default_initial_value = 2 * np.random.random(N) - 1
@@ -26,7 +26,7 @@ def create_rnn_node(id, N, g, seed=1234):
x = Parameter(
id="x",
default_initial_value=default_initial_value,
- time_derivative="-x + g*int_fb + %s" % ipr1.id,
+ time_derivative=f"-x + g*int_fb + {ext_ip.id} + {fb_ip.id}",
)
rnn_node.parameters.append(x)
diff --git a/examples/MDF/networks/Net_acyclical.json b/examples/MDF/networks/Net_acyclical.json
new file mode 100644
index 00000000..4cbac758
--- /dev/null
+++ b/examples/MDF/networks/Net_acyclical.json
@@ -0,0 +1,64 @@
+{
+ "Net_acyclical": {
+ "format": "ModECI MDF v0.4",
+ "generating_application": "Python modeci-mdf v0.4.12",
+ "graphs": {
+ "Net_acyclical": {
+ "nodes": {
+ "A": {
+ "input_ports": {
+ "input_port": {
+ "default_value": 0
+ }
+ },
+ "parameters": {
+ "passed_token": {
+ "value": "input_port + 1"
+ },
+ "internal_count": {
+ "value": "internal_count + 1"
+ }
+ },
+ "output_ports": {
+ "out_port": {
+ "value": "passed_token"
+ }
+ }
+ },
+ "B": {
+ "input_ports": {
+ "input_port": {}
+ },
+ "parameters": {
+ "passed_token": {
+ "value": "input_port + 1"
+ },
+ "internal_count": {
+ "value": "internal_count + 1"
+ }
+ },
+ "output_ports": {
+ "out_port": {
+ "value": "passed_token"
+ }
+ }
+ }
+ },
+ "edges": {
+ "edge_A_B": {
+ "sender": "A",
+ "receiver": "B",
+ "sender_port": "out_port",
+ "receiver_port": "input_port"
+ },
+ "edge_B_A": {
+ "sender": "B",
+ "receiver": "A",
+ "sender_port": "out_port",
+ "receiver_port": "input_port"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/examples/MDF/networks/Net_acyclical.png b/examples/MDF/networks/Net_acyclical.png
new file mode 100644
index 00000000..22386968
Binary files /dev/null and b/examples/MDF/networks/Net_acyclical.png differ
diff --git a/examples/MDF/networks/Net_acyclical.yaml b/examples/MDF/networks/Net_acyclical.yaml
new file mode 100644
index 00000000..f578ad33
--- /dev/null
+++ b/examples/MDF/networks/Net_acyclical.yaml
@@ -0,0 +1,40 @@
+Net_acyclical:
+ format: ModECI MDF v0.4
+ generating_application: Python modeci-mdf v0.4.12
+ graphs:
+ Net_acyclical:
+ nodes:
+ A:
+ input_ports:
+ input_port:
+ default_value: 0
+ parameters:
+ passed_token:
+ value: input_port + 1
+ internal_count:
+ value: internal_count + 1
+ output_ports:
+ out_port:
+ value: passed_token
+ B:
+ input_ports:
+ input_port: {}
+ parameters:
+ passed_token:
+ value: input_port + 1
+ internal_count:
+ value: internal_count + 1
+ output_ports:
+ out_port:
+ value: passed_token
+ edges:
+ edge_A_B:
+ sender: A
+ receiver: B
+ sender_port: out_port
+ receiver_port: input_port
+ edge_B_A:
+ sender: B
+ receiver: A
+ sender_port: out_port
+ receiver_port: input_port
diff --git a/examples/MDF/networks/network.py b/examples/MDF/networks/network.py
new file mode 100644
index 00000000..4f72f718
--- /dev/null
+++ b/examples/MDF/networks/network.py
@@ -0,0 +1,105 @@
+"""
+ Example of ModECI MDF - Testing networks
+"""
+
+from modeci_mdf.mdf import *
+import sys
+import os
+from modeci_mdf.utils import simple_connect
+
+
+def main(ref="acyclical"):
+
+ mod = Model(id="Net_%s" % ref)
+
+ mod_graph = Graph(id="Net_%s" % ref)
+ mod.graphs.append(mod_graph)
+
+ for id in ["A", "B"]:
+
+ node = Node(id=id)
+
+ if id == "A":
+ ip1 = InputPort(id="input_port", default_value=0)
+ node.input_ports.append(ip1)
+
+ if id == "B":
+ ip1 = InputPort(id="input_port")
+ node.input_ports.append(ip1)
+
+ p1 = Parameter(id="passed_token", value="input_port + 1")
+ node.parameters.append(p1)
+
+ p2 = Parameter(id="internal_count", value="internal_count + 1")
+ node.parameters.append(p2)
+
+ op1 = OutputPort(id="out_port", value=p1.id)
+ node.output_ports.append(op1)
+
+ mod_graph.nodes.append(node)
+
+ simple_connect(mod_graph.get_node("A"), mod_graph.get_node("B"), mod_graph)
+ simple_connect(mod_graph.get_node("B"), mod_graph.get_node("A"), mod_graph)
+
+ new_file = mod.to_json_file("%s.json" % mod.id)
+ new_file = mod.to_yaml_file("%s.yaml" % mod.id)
+
+ if "-run" in sys.argv:
+ verbose = True
+ # verbose = False
+ from modeci_mdf.utils import load_mdf, print_summary
+
+ from modeci_mdf.execution_engine import EvaluableGraph
+
+ eg = EvaluableGraph(mod_graph, verbose)
+ dt = 1
+
+ duration = 2
+ t = 0
+ recorded = {}
+ times = []
+ ai = []
+ bi = []
+ ao = []
+ bo = []
+ while t <= duration:
+ times.append(t)
+ print("====== Evaluating at t = %s ======" % (t))
+ if t == 0:
+ eg.evaluate() # replace with initialize?
+ else:
+ eg.evaluate(time_increment=dt)
+
+ ai.append(eg.enodes["A"].evaluable_parameters["internal_count"].curr_value)
+ bi.append(eg.enodes["B"].evaluable_parameters["internal_count"].curr_value)
+ ao.append(eg.enodes["A"].evaluable_outputs["out_port"].curr_value)
+ bo.append(eg.enodes["B"].evaluable_outputs["out_port"].curr_value)
+ t += dt
+
+ if "-nogui" not in sys.argv:
+ import matplotlib.pyplot as plt
+
+ plt.plot(times, ai, marker="x", label="A count")
+ plt.plot(times, bi, label="B count")
+ plt.plot(times, ao, marker="o", label="A token")
+ plt.plot(times, bo, label="B token")
+ plt.legend()
+ plt.show()
+
+ if "-graph" in sys.argv:
+ mod.to_graph_image(
+ engine="dot",
+ output_format="png",
+ view_on_render=False,
+ level=3,
+ filename_root=mod_graph.id,
+ only_warn_on_fail=(
+ os.name == "nt"
+ ), # Makes sure test of this doesn't fail on Windows on GitHub Actions
+ )
+
+ return mod_graph
+
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/MDF/networks/regenerate.sh b/examples/MDF/networks/regenerate.sh
new file mode 100755
index 00000000..acb22dd6
--- /dev/null
+++ b/examples/MDF/networks/regenerate.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+set -ex
+
+python network.py -run -graph -nogui
diff --git a/examples/NeuroML/Izh.png b/examples/NeuroML/Izh.png
index 0ee1a7b9..077a4381 100644
Binary files a/examples/NeuroML/Izh.png and b/examples/NeuroML/Izh.png differ
diff --git a/examples/NeuroML/IzhikevichTest.gv.png b/examples/NeuroML/IzhikevichTest.gv.png
index 120eef86..9334cdb1 100644
Binary files a/examples/NeuroML/IzhikevichTest.gv.png and b/examples/NeuroML/IzhikevichTest.gv.png differ
diff --git a/examples/NeuroML/PyNN/HH.mdf.png b/examples/NeuroML/PyNN/HH.mdf.png
index 7cb45a8b..42975df9 100644
Binary files a/examples/NeuroML/PyNN/HH.mdf.png and b/examples/NeuroML/PyNN/HH.mdf.png differ
diff --git a/examples/NeuroML/PyNN/InputWeights.mdf.png b/examples/NeuroML/PyNN/InputWeights.mdf.png
index a34f50a1..f91fa93c 100644
Binary files a/examples/NeuroML/PyNN/InputWeights.mdf.png and b/examples/NeuroML/PyNN/InputWeights.mdf.png differ
diff --git a/examples/NeuroML/PyNN/Net1.mdf.png b/examples/NeuroML/PyNN/Net1.mdf.png
index 8d21027b..3cbf34f6 100644
Binary files a/examples/NeuroML/PyNN/Net1.mdf.png and b/examples/NeuroML/PyNN/Net1.mdf.png differ
diff --git a/examples/NeuroML/PyNN/OneCell.mdf.png b/examples/NeuroML/PyNN/OneCell.mdf.png
index d67c5aff..74fb0b69 100644
Binary files a/examples/NeuroML/PyNN/OneCell.mdf.png and b/examples/NeuroML/PyNN/OneCell.mdf.png differ
diff --git a/examples/NeuroML/PyNN/SimpleNet.mdf.png b/examples/NeuroML/PyNN/SimpleNet.mdf.png
index 52d272aa..f94a631f 100644
Binary files a/examples/NeuroML/PyNN/SimpleNet.mdf.png and b/examples/NeuroML/PyNN/SimpleNet.mdf.png differ
diff --git a/examples/ONNX/abc.png b/examples/ONNX/abc.png
index e6cdc653..22c62cf2 100644
Binary files a/examples/ONNX/abc.png and b/examples/ONNX/abc.png differ
diff --git a/examples/TensorFlow/Keras/IRIS/model_on_iris_plot.png b/examples/TensorFlow/Keras/IRIS/model_on_iris_plot.png
index 9b0cde3d..5bfe195f 100644
Binary files a/examples/TensorFlow/Keras/IRIS/model_on_iris_plot.png and b/examples/TensorFlow/Keras/IRIS/model_on_iris_plot.png differ
diff --git a/examples/TensorFlow/Keras/MNIST/model_plot.png b/examples/TensorFlow/Keras/MNIST/model_plot.png
index f4142fab..445bbb4c 100644
Binary files a/examples/TensorFlow/Keras/MNIST/model_plot.png and b/examples/TensorFlow/Keras/MNIST/model_plot.png differ
diff --git a/src/modeci_mdf/execution_engine.py b/src/modeci_mdf/execution_engine.py
index 2922a23b..ff71dd86 100644
--- a/src/modeci_mdf/execution_engine.py
+++ b/src/modeci_mdf/execution_engine.py
@@ -692,10 +692,17 @@ class EvaluableInput:
def __init__(self, input_port: InputPort, verbose: Optional[bool] = False):
self.verbose = verbose
self.input_port = input_port
- default = 0
- if input_port.type and "float" in input_port.type:
- default = 0.0
- self.curr_value = np.full(input_port.shape, default)
+
+ if self.input_port.reduce == "overwrite":
+ if self.input_port.default_value is not None:
+ self.curr_value = self.input_port.default_value
+ else:
+ default = 0
+ if input_port.type and "float" in input_port.type:
+ default = 0.0
+ self.curr_value = np.full(input_port.shape, default)
+ else:
+ self.curr_value = None
def set_input_value(self, value: Union[str, int, np.ndarray]):
"""Set a new value at input port
@@ -703,9 +710,30 @@ def set_input_value(self, value: Union[str, int, np.ndarray]):
Args:
value: Value to be set at Input Port
"""
- if self.verbose:
- print(f" Input value in {self.input_port.id} set to {_val_info(value)}")
- self.curr_value = value
+
+ if self.curr_value is None:
+ ## No value set yet, so just set to value being input now
+ print(
+ f" Input value in {self.input_port.id} being set to {_val_info(value)}"
+ )
+ self.curr_value = value
+ else:
+ ## A previous value has been set during this time step. So reduce it...
+ if self.input_port.reduce == "overwrite":
+ print(
+ f" Input value in {self.input_port.id} being set to {_val_info(value)}"
+ )
+ self.curr_value = value
+ elif self.input_port.reduce == "add":
+ print(
+ f" Input value in {self.input_port.id} was {self.curr_value}, increasing by {_val_info(value)}"
+ )
+ self.curr_value += value
+ elif self.input_port.reduce == "multiply":
+ print(
+ f" Input value in {self.input_port.id} was {self.curr_value}, increasing by {_val_info(value)}"
+ )
+ self.curr_value *= value
def evaluate(
self, parameters: Dict[str, Any] = None, array_format: str = FORMAT_DEFAULT
@@ -719,16 +747,27 @@ def evaluate(
Returns:
value at Input port
"""
+
+ if self.curr_value is None:
+ if self.input_port.default_value is not None:
+ self.curr_value = self.input_port.default_value
+ else:
+ self.curr_value = np.full(self.input_port.shape, 0.0)
+
+ final_val = self.curr_value
+
if self.verbose:
print(
- " Evaluated %s with params %s =\t%s"
+ " Evaluated the %s with params %s \n =\t%s"
% (
self.input_port,
_params_info(parameters),
- _val_info(self.curr_value),
+ _val_info(final_val),
)
)
- return self.curr_value
+
+ self.curr_value = None
+ return final_val
class EvaluableNode:
@@ -1034,6 +1073,14 @@ def __init__(self, graph: Graph, verbose: Optional[bool] = False):
): # It could have been already removed...
self.root_nodes.remove(edge.receiver)
+ if len(self.root_nodes) == 0:
+ for node in graph.nodes:
+ for ip in node.input_ports:
+ if ip.default_value is not None:
+ self.root_nodes.append(node.id)
+
+ print("Root nodes evaluated as: %s" % (self.root_nodes))
+
self.ordered_edges = []
evaluated_nodes = []
for rn in self.root_nodes:
@@ -1049,6 +1096,8 @@ def __init__(self, graph: Graph, verbose: Optional[bool] = False):
self.ordered_edges.append(edge)
evaluated_nodes.append(edge.receiver)
+ print("Ordered_edges as: %s" % (self.ordered_edges))
+
if self.graph.conditions is not None:
if self.graph.conditions.node_specific is None:
conditions = {}
@@ -1076,6 +1125,7 @@ def __init__(self, graph: Graph, verbose: Optional[bool] = False):
else:
conditions = {}
termination_conds = {}
+
self.scheduler = graph_scheduler.Scheduler(
graph=self.graph.dependency_dict,
conditions=conditions,
@@ -1147,9 +1197,16 @@ def evaluate(
for node in ts:
self.order_of_execution.append(node.id)
for edge in incoming_edges[node]:
- self.evaluate_edge(
- edge, time_increment=time_increment, array_format=array_format
- )
+ if edge.sender in self.order_of_execution:
+ self.evaluate_edge(
+ edge,
+ time_increment=time_increment,
+ array_format=array_format,
+ )
+ else:
+ print(
+ "> Not evaluating edge: %s, as sender not run yet..." % edge
+ )
self.enodes[node.id].evaluate(
time_increment=time_increment, array_format=array_format
)
diff --git a/src/modeci_mdf/interfaces/graphviz/exporter.py b/src/modeci_mdf/interfaces/graphviz/exporter.py
index e34c32ac..833a9786 100644
--- a/src/modeci_mdf/interfaces/graphviz/exporter.py
+++ b/src/modeci_mdf/interfaces/graphviz/exporter.py
@@ -285,9 +285,9 @@ def mdf_to_graphviz(
additional = ""
if ip.shape is not None and ip.shape != ():
additional += "shape: %s, " % str(ip.shape)
- """if ip.reduce is not None:
+ if ip.reduce is not None:
if ip.reduce != "overwrite": # since this is the default...
- additional += "reduce: %s, " % str(ip.reduce)"""
+ additional += "reduce: %s, " % str(ip.reduce)
if ip.type is not None:
additional += "type: %s, " % str(ip.type)
diff --git a/src/modeci_mdf/mdf.py b/src/modeci_mdf/mdf.py
index 327240be..d6f908e6 100644
--- a/src/modeci_mdf/mdf.py
+++ b/src/modeci_mdf/mdf.py
@@ -99,19 +99,25 @@ class InputPort(MdfBase):
Attributes:
id: The unique (for this Node) id of the input port,
+ default_value: Value to set at this input port if no edge connected to it.
shape: The shape of the input port. This uses the same syntax as numpy ndarray shapes
(e.g., :code:`numpy.zeros(shape)` would produce an array with the correct shape
type: The data type of the input received at a port.
+ reduce: Specifies how to deal with multiple inputs to one port during a single timestep: add: add up all the values; multiply: multiply the values, overwrite: just use the last value supplied (default)
"""
id: str = field(validator=instance_of(str))
+ default_value: Optional[ValueExprType] = field(default=None)
shape: Optional[Tuple[int, ...]] = field(
validator=optional(instance_of(tuple)),
default=(),
converter=lambda x: make_tuple(x) if type(x) is str else x,
)
type: Optional[str] = field(validator=optional(instance_of(str)), default=None)
+ reduce: Optional[str] = field(
+ validator=optional(instance_of(str)), default="overwrite"
+ )
@modelspec.define(eq=False)
@@ -488,7 +494,10 @@ def dependency_dict(self) -> Dict[Node, Set[Node]]:
sender = self.get_node(edge.sender)
receiver = self.get_node(edge.receiver)
- dependencies[receiver].add(sender)
+ if receiver.get_input_port(edge.receiver_port).default_value is not None:
+ pass
+ else:
+ dependencies[receiver].add(sender)
return dependencies
diff --git a/test_all.sh b/test_all.sh
index 4d09a4d1..2d6c79cb 100755
--- a/test_all.sh
+++ b/test_all.sh
@@ -72,6 +72,11 @@ cd ..
cd RNN
./regenerate.sh
+## Test regenerating network example
+
+cd ../networks
+./regenerate.sh
+
## Test regenerating NeuroML
diff --git a/tests/test_scheduler.py b/tests/test_scheduler.py
index 45b5356a..f8f9984c 100644
--- a/tests/test_scheduler.py
+++ b/tests/test_scheduler.py
@@ -15,7 +15,7 @@ def test_execution_engine_main(tmpdir):
mdf_formats = ["json", "yaml"]
array_formats = [FORMAT_NUMPY, FORMAT_TENSORFLOW]
- # For now, don't make tensorflow a requiremnt...
+ # For now, don't make tensorflow a requirement...
try:
import tensorflow
except:
@@ -221,7 +221,7 @@ def test_threshold(create_model):
[
("A", "A_output"),
("A", "A_param_2"),
- ("B", "B_input"),
+ ("B", "B_output"),
],
)
@pytest.mark.parametrize(