-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathports.py
More file actions
265 lines (193 loc) · 8.14 KB
/
Copy pathports.py
File metadata and controls
265 lines (193 loc) · 8.14 KB
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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
"""
This file defines the Port classes used in NineML
:copyright: Copyright 2010-2017 by the NineML Python team, see AUTHORS.
:license: BSD-3, see LICENSE for details.
"""
from builtins import object
from abc import ABCMeta
import sympy
from . import BaseALObject
from operator import add
from nineml.units import dimensionless
from nineml.utils import validate_identifier
from nineml.exceptions import NineMLUsageError
from .expressions import ExpressionSymbol
from nineml.base import SendPortBase # A work around to avoid circular imports
from nineml.units import Dimension
from future.utils import with_metaclass
class Port(with_metaclass(ABCMeta, BaseALObject)):
"""
Base class for |AnalogSendPorts|, |AnalogReceivePorts|,
|EventSendPorts|, |EventReceivePorts| and |AnalogReducePorts|.
In general, a port has a ``name``, which can be used to reference it,
and a ``mode``, which specifies whether it sends or receives information.
Generally, a send port can be connected to receive port to allow different
components to communicate.
|AnalogSendPorts| and |EventSendPorts| can be connected to any number of
|AnalogReceivePorts| and |EventReceivePorts| respectively, but each
|AnalogReceivePort| and |EventReceivePort| can only be connected to a
single |AnalogSendPort| and |EventSendPort| respectively.
"""
nineml_attr = ('name',)
def __init__(self, name):
""" Port Constructor.
`name` -- The name of the port, as a `string`
"""
super(Port, self).__init__()
self._name = validate_identifier(name)
@property
def name(self):
"""The name of the port, local to the current component"""
return self._name
def __repr__(self):
classstring = self.__class__.__name__
return "{}('{}')".format(classstring, self.name)
def serialize_node(self, node, **options): # @UnusedVariable
node.attr('name', self.name, **options)
@classmethod
def unserialize_node(cls, node, **options): # @UnusedVariable
return cls(name=node.attr('name', **options))
class DimensionedPort(
with_metaclass(ABCMeta,
type('NewBase', (Port, ExpressionSymbol), {}))):
"""DimensionedPort
A |DimensionedPort| is the base class for ports with dimensions (e.g.
Analog and Property ports).
"""
nineml_attr = ('name',)
nineml_child = {'dimension': Dimension}
def __init__(self, name, dimension=None):
super(DimensionedPort, self).__init__(name)
self._dimension = (dimension if dimension is not None
else dimensionless)
@property
def dimension(self):
"""The dimension of the port"""
return self._dimension
def set_dimension(self, dimension):
assert self.dimension == dimension,\
"Dimensions should not change, only change of names is permitted"
self._dimension = dimension
def __repr__(self):
classstring = self.__class__.__name__
try:
dim_name = self.dimension.name
except NineMLUsageError:
dim_name = '<unknown>'
return "{}('{}', dimension='{}')".format(classstring, self.name,
dim_name)
def serialize_node(self, node, **options): # @UnusedVariable
super(DimensionedPort, self).serialize_node(node, **options)
node.attr('dimension', self.dimension.name, **options)
@classmethod
def unserialize_node(cls, node, **options): # @UnusedVariable
return cls(
name=node.attr('name', **options),
dimension=node.visitor.document[node.attr('dimension', **options)])
class SendPort(SendPortBase):
"""SendPort
Base class for sending ports
"""
mode = "send"
def is_incoming(self):
return False
def can_connect_to(self, port):
return isinstance(port, ReceivePort) and self.type_matches(port)
class ReceivePort(object):
"""ReceivePort
Base class for receiving ports
"""
mode = "receive"
def is_incoming(self):
return True
def can_connect_to(self, port):
return isinstance(port, SendPort) and self.type_matches(port)
class AnalogPort(DimensionedPort):
"""AnalogPort
An |AnalogPort| represents a continuous input or output to/from a
Component. For example, this could be the membrane-voltage into a synapse
component, or the current provided by a ion-channel.
"""
communicates = 'analog'
def type_matches(self, port):
return isinstance(port, AnalogPort)
class EventPort(Port):
"""EventPort
An |EventPort| is a port that can transmit and receive discrete events at
points in time. For example, an integrate-and-fire could 'send' events to
notify other components that it had fired; or synapses could receive events
to notify them to provide current to a post-synaptic neuron.
"""
communicates = 'event'
def type_matches(self, port):
return isinstance(port, AnalogPort)
class AnalogSendPort(AnalogPort, SendPort):
"""AnalogSendPort
An |AnalogSendPort| represents a continuous output from a
Component. For example, this could be the membrane-voltage into a synapse
component, or the current provided by a ion-channel.
"""
nineml_type = 'AnalogSendPort'
class AnalogReceivePort(AnalogPort, ReceivePort):
"""AnalogReceivePort
An |AnalogReceivePort| represents a continuous input to a
Component. For example, this could be the membrane-voltage into a synapse
component, or the current provided by a ion-channel.
"""
nineml_type = 'AnalogReceivePort'
class EventSendPort(EventPort, SendPort):
"""EventSendPort
An |EventSendPort| is a port that can transmit discrete events at
points in time. For example, an integrate-and-fire could 'send' events to
notify other components that it had fired.
"""
nineml_type = 'EventSendPort'
class EventReceivePort(EventPort, ReceivePort):
"""EventReceivePort
An |EventReceivePort| is a port that can receive discrete events at
points in time. For example, synapses could receive events
to notify them to provide current to a post-synaptic neuron.
"""
nineml_type = 'EventReceivePort'
class AnalogReducePort(AnalogPort, ReceivePort):
"""AnalogReducePort
An |AnalogReducePort| represents a collection of continuous inputs to a
Component from a common type of input that can be reduced into a single
input. For example, or the currents provided by a collection of
ion-channels. NB: The only currently supported operators are: ``+``.
"""
nineml_type = 'AnalogReducePort'
mode = "reduce"
nineml_attr = ('name', 'operator')
nineml_child = {'dimension': Dimension}
_operator_map = {'add': '+', '+': '+', }
_to_python_operator = {'+': add}
def __init__(self, name, dimension=None, operator='+'):
if operator not in list(self._operator_map.keys()):
err = ("%s('%s')" + "specified undefined operator: '%s'") %\
(self.__class__.__name__, name, str(operator))
raise NineMLUsageError(err)
super(AnalogReducePort, self).__init__(name, dimension)
self._operator = str(operator)
@property
def operator(self):
return self._operator
@property
def python_op(self):
return self._to_python_operator[self.operator]
def __repr__(self):
classstring = self.__class__.__name__
return ("{}('{}', dimension='{}', op='{}')"
.format(classstring, self.name, self.dimension,
self.operator))
def serialize_node(self, node, **options): # @UnusedVariable
super(AnalogReducePort, self).serialize_node(node, **options)
node.attr('operator', self.operator, **options)
def combine_symbols(self, *syms):
return reduce(add, (sympy.Symbol(s) for s in syms))
@classmethod
def unserialize_node(cls, node, **options): # @UnusedVariable
return cls(
name=node.attr('name', **options),
dimension=node.visitor.document[node.attr('dimension', **options)],
operator=node.attr('operator', **options))