-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbase.py
More file actions
234 lines (187 loc) · 7.03 KB
/
Copy pathbase.py
File metadata and controls
234 lines (187 loc) · 7.03 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
from inspect import signature, Parameter
from pprint import PrettyPrinter
from .config import read_config
from .format import Formatter
from .monitor import Monitor
config = read_config()
time_format = config.get("log", "time_format")
pprint_option = config.get_section("pprint")
class Pipe:
"""
Base pipe class for all pipes.
In FPEG, processors, transformers and encoders are pipes which can be filled into a pipeline to create brand new compress and decompress algorithms.
References
----------
[1] Lars Buitinck, Gilles Louppe, Mathieu Blondel et al. "API design for machine learning software: experiences from the scikit-learn project" in European Conference on Machine Learning and Principles and Practices of Knowledge Discovery in Databases (2013).
"""
def __init__(self):
"""
Init basic implicit attributes.
Implicit Attributes
-------------------
logs: list of str
Log messages of recieving and sending data. Each element in list is a log of recieving and sending data.
formatter: fpeg.log.Formatter
Formatter for generating log messages.
pprinter: fpeg.printer.Pprinter
Pretty printer for printing pipes.
"""
self.logs = []
self.formatter = Formatter(fmt=time_format)
self.pprinter = PrettyPrinter(**pprint_option)
self.monitor = Monitor()
def recv_send(self, X, **params):
"""
Recieve and send data.
"""
return self.recv(X, **params).send()
def recv(self, X, **params):
"""
Just let subclass rewrite this method.
Data received by the pipe are processed and stored when this recv method is called.
"""
return self
def send(self):
"""
Send the received and processed data, add a log record and send the monitor a message.
"""
self.logs[-1] += self.formatter.message("Sending received data.")
self.sended = True
self.monitor.wake()
self.monitor.gather(*self.respond())
self._clear_record()
return self.sended_
def respond(self):
"""
Respond to the monitor.
Pipe should support a monitor to trace its history of
recieving and sending data.
"""
if not self.sended:
msg = "Send method hasn't been called yet, do not send message to the monitor."
self.logs[-1] += self.formatter.error(msg)
raise RuntimeError(msg)
self.logs[-1] += self.formatter.message("Responding to monitor.")
self.sended = False
print(self.logs[-1])
return self.name, (self.received_, self.sended_), self.logs[-1], self.get_params()
def accelerate(self, **params):
"""
Set self.accelerated as True when number of tasks exceeds the setted minimun task number.
If self.accelerated is true, pipe will open a subprocess pool for parallel computation when receiving and processing data.
The function mapped to the pool must not be method of pipe.
"""
self.logs[-1] += self.formatter.message("Trying to accelerate process.")
try:
self.task_number = params["task_number"]
except KeyError as err:
msg = "\"task_number\" should be passed to the accelerate method."
self.logs[-1] += self.formatter.warning(msg)
self.task_number = len(self.received_)
try:
self.accelerated = params["accelerated"]
except KeyError:
try:
self.max_pool_size = params["max_pool_size"]
except KeyError:
pass
self.accelerated = self.accelerated or bool(self.task_number < self.min_task_number)
def set_params(self, **params):
if not params:
return self
valid_params = self.get_params()
for key, value in params.items():
if key not in valid_params:
pass
else:
setattr(self, key, value)
def get_params(self):
out = {}
for key in self._get_param_names():
try:
value = getattr(self, key)
except AttributeError:
value = None
out[key] = value
return out
def _clear_record(self):
self.logs[-1] += self.formatter.message("Cleaning former record.")
init = self.__init__
if init is object.__init__:
return
params = {}
# make self, name, mode, flag, monitor, formatter, pprinter unchanged every time receive and send data.
excluded_names = ["self", "name", "mode", "flag", "monitor", "formatter", "pprinter"]
init_signature = signature(init)
for key, val in init_signature.parameters.items():
if key not in excluded_names and val.default is not Parameter.empty:
params[key] = val.default
self.set_params(**params)
@classmethod
def _get_param_names(cls):
init = cls.__init__
if init is object.__init__:
return []
init_signature = signature(init)
parameters = [p for p in init_signature.parameters.values()
if p.name != 'self' and p.kind != p.VAR_KEYWORD]
# Make monitor, formatter, pprinter visible to pipeline.
# These attributes are initialized by the Pipe base class,
# and is invisible to pipeline in subclasses if not do so.
included_names = ["monitor", "formatter", "pprinter"]
names = [p.name for p in parameters]
names.extend(included_names)
names = sorted(list(set(names)))
return names
def __repr__(self):
"""
Pretty print the pipe.
"""
excluded_names = ["name", "monitor", "formatter", "pprinter"]
params = self.get_params()
new_params = {}
for key in params:
if key not in excluded_names:
new_params[key] = params[key]
return self.__class__.__name__ + " '" + params["name"] + "' with attributes: " + self.pprinter.pformat(new_params)
class Codec(Pipe):
"""
Base class of encoders and decoders.
In FPEG, coding and decoding methods like Huffman coding, Shannon coding and Entropy coding are implemented as codecs.
"""
def recv(self, X, **params):
"""
Recieve stream of data.
"""
self.logs.append("")
self.logs[-1] += self.formatter.message("Receiving data.")
self.received_ = X
self.accelerate(**params)
if self.mode == "encode":
self.sended_ = self.encode(X, **params)
elif self.mode == "decode":
self.sended_ = self.decode(X, **params)
else:
msg = "Invalid attribute %s for codec %s. Codec.mode should be set to \"encode\" or \"decode\"." % (self.mode, self)
self.logs[-1] += self.formatter.error(msg)
raise AttributeError(msg)
return self
class Transformer(Pipe):
"""
Base class of transformers and inverse-transformers.
In FPEG, transforms and inverse transforms like fft, ifft, dwt and idwt are implemented as transformers.
"""
def recv(self, X, **params):
self.logs.append("")
self.logs[-1] += self.formatter.message("Receiving data.")
self.received_ = X
self.accelerate(**params)
if self.mode == "forward":
self.sended_ = self.forward(X, **params)
elif self.mode == "backward":
self.sended_ = self.backward(X, **params)
else:
msg = "Invalid attribute %s for transformer %s. Transformer.mode should be set to \"forward\" or \"backward\"." % (self.mode, self)
self.logs[-1] += self.formatter.error(msg)
raise AttributeError(msg)
return self