/[escript]/trunk/escript/py_src/timeseries.py
ViewVC logotype

Annotation of /trunk/escript/py_src/timeseries.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 637 - (hide annotations)
Thu Mar 23 10:55:31 2006 UTC (13 years, 8 months ago) by gross
File MIME type: text/x-python
File size: 42703 byte(s)
more copyright statements added
1 jgs 110 # $Id$
2    
3 gross 637 """
4     Time serieas analysis
5    
6     @var __author__: name of author
7     @var __copyright__: copyrights
8     @var __license__: licence agreement
9     @var __url__: url entry point on documentation
10     @var __version__: version
11     @var __date__: date of the version
12     """
13    
14    
15     __author__="Lutz Gross, l.gross@uq.edu.au"
16 elspeth 609 __copyright__=""" Copyright (c) 2006 by ACcESS MNRF
17     http://www.access.edu.au
18     Primary Business: Queensland, Australia"""
19 elspeth 614 __license__="""Licensed under the Open Software License version 3.0
20     http://www.opensource.org/licenses/osl-3.0.php"""
21 gross 637 __url__="http://www.iservo.edu.au/esys/escript"
22     __version__="$Revision$"
23     __date__="$Date$"
24 elspeth 609
25 gross 637
26 jgs 110 import numarray
27 jgs 117 from types import SliceType
28 jgs 119 DEFAULT_BUFFER_SIZE=1000
29 jgs 117 DEFAULT_FLOAT_TYPE=numarray.Float64
30 jgs 110
31     class TimeSeriesBase:
32 jgs 117 """The TimeSeriesBase class is the base class for all class of the TimeSeries module."""
33 jgs 110
34 jgs 119 def __init__(self,debug=False,description="TimeSeriesBase"):
35 jgs 117 self.__debug=debug
36 jgs 119 self.setDescription(description)
37 jgs 110
38     def __str__(self):
39 jgs 117 return self.__description
40 jgs 119
41     def setDescription(self,text):
42     self.__description=text
43 jgs 110
44     def setDebugOn(self):
45     """switch on degugging mode"""
46     self.__debug=True
47    
48     def setDebugOff(self):
49     """switch off degugging mode"""
50     self.__debug=False
51    
52     def setDebug(self,flag=False):
53     """sets debug mode to flag"""
54     if flag:
55     self.setDebugOn()
56     else:
57     self.setDebugOff()
58    
59     def debug(self):
60     """returns true if debug mode is on"""
61     return self.__debug
62    
63 jgs 117 #============================================================================================================
64 jgs 119 class TimeSeriesBaseDataset(TimeSeriesBase):
65 jgs 117 """provides an interface for accessing a set of linearly ordered data."""
66 jgs 119 def __init__(self,buffer,offset=0,debug=False,description="TimeSeriesDataset"):
67 jgs 117 TimeSeriesBase.__init__(self,debug,description)
68     self.__buffer=buffer
69     self.__offset=offset
70     if self.debug(): print "Debug: %s: offset %d to buffer"%(self,self.getOffset())
71 jgs 110
72 jgs 117 def __len__(self):
73     """needed to handle negative indexing in slicing"""
74     return 0
75 jgs 110
76 jgs 117 def getNumComponents(self):
77     """returns the number of components of the data (may be overwritten by subclass)"""
78 jgs 119 return self.getBaseBuffer().getNumComponents()
79 jgs 110
80 jgs 117 def getIdOfLastDatum(self):
81     """returns the identification number of the last datum in the data set (may be overwritten by subclass)"""
82 jgs 119 return self.getBaseBuffer().getIdOfLastDatum()-self.getOffset()
83 jgs 110
84 jgs 117 def getIdOfFirstDatum(self):
85     """returns the identification number of the first datum (may be overwritten by subclass)"""
86 jgs 119 return self.getBaseBuffer().getIdOfFirstDatum()-self.getOffset()
87 jgs 110
88 jgs 119 def getIdOfFirstAvailableDatum(self):
89     """returns the identification number of the first avaiable datum (may be overwritten by subclass)"""
90     return self.getBaseBuffer().getIdOfFirstAvailableDatum()-self.getOffset()
91    
92     def getOffsetInBaseBuffer(self):
93     """returns the offset to access elements in getBaseBuffer() (may be overwritten by subclass)"""
94 jgs 117 return self.getOffset()
95    
96 jgs 119 def getIdOfLastUnreferencedDatum(self):
97     """returns the identification number of the last datum which has been unused by all TimeSeries refering to the TimeSeriesBaseDataset (may be overwritten by subclass)"""
98     return self.getBaseBuffer().getIdOfLastUnreferencedDatum()-self.getOffset()
99 jgs 117
100 jgs 119 def updateIdOfLastUnreferencedDatum(self,last_unreferenced_datum):
101 jgs 117 """updates the identification number of the last unused datum (to be overwritten by subclass)"""
102 jgs 119 self.getBaseBuffer().updateIdOfLastUnreferencedDatum(last_unreferenced_datum+self.getOffset())
103 jgs 117
104     def append(self,values):
105     """appends data to the buffer. If the buffer would be full the buffer is rearranged before the data are appended (to be overwritten by subclass)"""
106 jgs 119 self.getBaseBuffer().append(values)
107 jgs 117
108 jgs 119 def getBaseBufferSize(self):
109 jgs 117 """returns the size of the buffer (to be overwritten by subclass)"""
110 jgs 119 return self.getBaseBuffer().getBaseBufferSize()
111 jgs 117
112     def needsRearrangement(self,num_new_data=0):
113     """returns True if the buffer will be full after num_new_data have been appended (to be overwritten by subclass)"""
114 jgs 119 return self.getBaseBuffer().needsRearrangement(num_new_data)
115 jgs 117
116     def isEmpty(self):
117     """returns true if no data are appeneded to buffer"""
118     return self.getNumData()<=0
119    
120     def getNumData(self):
121     """returns the number of data (not all of them are accessible)"""
122     return self.getIdOfLastDatum()-self.getIdOfFirstDatum()+1
123    
124 jgs 119 def getBaseBuffer(self):
125     """return the buffer referenced by the TimeSeriesBaseDataset"""
126 jgs 117 return self.__buffer
127    
128     def getOffset(self):
129     """return the offset when referring to dataset elements"""
130     return self.__offset
131    
132     def __getitem__(self,index):
133     """returns the datum index"""
134     if type(index)==SliceType:
135     start=index.start
136     end=index.stop
137     if start==end:
138     return self[start]
139     else:
140     if start<self.getIdOfFirstDatum() or start>self.getIdOfLastDatum() or \
141     end-1<self.getIdOfFirstDatum() or end-1>self.getIdOfLastDatum(): raise IndexError,"%s: Index [%d:%d] out of range"%(self,start,end)
142 jgs 119 return self.getBaseBuffer()[start+self.getOffsetInBaseBuffer():end+self.getOffsetInBaseBuffer()]
143 jgs 117 else:
144     if index<self.getIdOfFirstDatum() or index>self.getIdOfLastDatum(): raise IndexError,"%s: Index %d out of range"%(self,index)
145 jgs 119 return self.getBaseBuffer()[index+self.getOffsetInBaseBuffer()]
146 jgs 117
147 jgs 119 class TimeSeriesBaseBuffer(TimeSeriesBaseDataset):
148     """An inplementation of TimeSeriesBaseDataset which actually is storing data into a numarray buffer"""
149     def __init__(self,buffer_size=DEFAULT_BUFFER_SIZE,numComponents=1,type=DEFAULT_FLOAT_TYPE,id_of_first_datum=0,debug=False,description="TimeSeriesBaseBuffer"):
150 jgs 110 if numComponents<2:
151 jgs 117 buffer=numarray.zeros((buffer_size,),type)
152 jgs 110 else:
153 jgs 117 buffer=numarray.zeros((buffer_size,numComponents),type)
154 jgs 119 TimeSeriesBaseDataset.__init__(self,buffer,id_of_first_datum-1,debug,description)
155 jgs 117 self.__num_data_in_buffer=0
156 jgs 119 self.__id_last_unreferenced_datum=id_of_first_datum-1
157 jgs 117 self.__id_last_datum=id_of_first_datum-1
158     self.__id_first_datum=id_of_first_datum
159     if self.debug(): print "Debug: %s : buffer of size %d with %d components allocated (first datum is %d)."% \
160 jgs 119 (self,self.getBaseBufferSize(),self.getNumComponents(),id_of_first_datum)
161 jgs 110
162    
163 jgs 119 def getBaseBufferSize(self):
164 jgs 117 """returns the size of the buffer"""
165 jgs 119 return self.getBaseBuffer().shape[0]
166 jgs 117
167 jgs 110 def getNumComponents(self):
168 jgs 119 """returns the number of components of the data (overwrites TimeSeriesBaseDataset method)"""
169     if self.getBaseBuffer().rank==1:
170 jgs 110 return 1
171     else:
172 jgs 119 self.getBaseBuffer().shape[1]
173 jgs 110
174 jgs 119 def getNumDataInBaseBuffer(self):
175 jgs 117 """returns the number of data currently in the buffer"""
176     return self.__num_data_in_buffer
177 jgs 110
178 jgs 117 def getIdOfLastDatum(self):
179 jgs 119 """returns the identification number of the last datum in the data set (overwrites method from TimeSeriesBaseDataset)"""
180 jgs 117 return self.__id_last_datum
181 jgs 110
182 jgs 117 def getIdOfFirstDatum(self):
183 jgs 119 """returns the identification number of the first datum (overwrites method from TimeSeriesBaseDataset)"""
184 jgs 117 return self.__id_first_datum
185 jgs 110
186 jgs 119 def getOffsetInBaseBuffer(self):
187     """returns the offset to access elements in the buffer (overwrites method from TimeSeriesBaseDataset)"""
188     return -self.getIdOfLastDatum()+self.getNumDataInBaseBuffer()-1
189 jgs 110
190 jgs 119 def getIdOfLastUnreferencedDatum(self):
191     """returns the identification number of the last datum which has been unused by all TimeSeries refering to the TimeSeriesBaseDataset (overwrites method from TimeSeriesBaseDataset)"""
192     return self.__id_last_unreferenced_datum
193 jgs 110
194 jgs 119 def updateIdOfLastUnreferencedDatum(self,last_unreferenced_datum):
195 jgs 117 """updates the identification number of the last unused datum (to be overwritten by subclass)"""
196 jgs 119 self.getBaseBuffer().updateIdOfLastUnreferencedDatum(last_unreferenced_datum-self.getOffset())
197 jgs 110
198 jgs 119 def updateIdOfLastUnreferencedDatum(self,last_unreferenced_datum):
199     """updates the identification number of the last unused datum (overwrites TimeSeriesBaseDataset method)"""
200     if self.__id_last_unreferenced_datum>last_unreferenced_datum:
201     self.__id_last_unreferenced_datum=last_unreferenced_datum
202     if self.debug(): print "Debug: %s: last unused datum is now %s"%(self,last_unreferenced_datum)
203 jgs 110
204 jgs 117 def needsRearrangement(self,num_new_data=0):
205     """returns True if the buffer will be full after num_new_data have been appended"""
206 jgs 119 return self.getNumDataInBaseBuffer()+num_new_data>self.getBaseBufferSize()
207 jgs 117
208 jgs 119 def getIdOfFirstAvailableDatum(self):
209     """returns the identification number of the first avaiable datum (overwrites TimeSeriesBaseDataset method)"""
210     return self.getIdOfLastDatum()-self.__num_data_in_buffer+1
211    
212 jgs 117 def append(self,data):
213 jgs 119 """appends data to the buffer. If the buffer would be full the buffer is rearranged before the data are appended (overwrites TimeSeriesBaseDataset method)"""
214 jgs 117 data=numarray.array(data)
215     nc=self.getNumComponents()
216     if data.rank==0:
217     if nc==1:
218     num_new_data=1
219     else:
220     raise ValueError,"%s: illegal data shape"%self
221     elif data.rank==1:
222     if nc==1:
223     num_new_data=data.shape[0]
224     else:
225     num_new_data=1
226     elif data.rank==2:
227     if not nc==data.shape[1]: raise ValueError,"%s: illegal data shape"%self
228     num_new_data=data.shape[0]
229     else:
230     raise ValueError,"%s: illegal rank"%self
231 jgs 110
232 jgs 117 # check is buffer will be overflown when data are appended:
233     if self.needsRearrangement(num_new_data):
234 jgs 119 nn=self.getNumDataInBaseBuffer()
235     num_protected_data=self.getIdOfLastDatum()-self.getIdOfLastUnreferencedDatum()
236     if num_protected_data+num_new_data>self.getBaseBufferSize():
237 jgs 117 raise ValueError,"%s: buffer overflow: buffer size has to be bigger than %d"%(self,num_protected_data+num_new_data)
238 jgs 119 if num_protected_data>0: self.getBaseBuffer()[0:num_protected_data]=self.getBaseBuffer()[nn-num_protected_data:nn]
239 jgs 117 self.__num_data_in_buffer=num_protected_data
240 jgs 119 self.__id_last_unreferenced_datum=self.__id_last_datum
241 jgs 117 if self.debug():
242 jgs 119 print "Debug: %s: rearrangement: first data in buffer is %d."%(self,self.getIdOfLastDatum()-self.getNumDataInBaseBuffer()+1)
243 jgs 117 # copy data over:
244 jgs 119 nn=self.getNumDataInBaseBuffer()
245     self.getBaseBuffer()[nn:nn+num_new_data]=data
246 jgs 117 self.__num_data_in_buffer+=num_new_data
247     self.__id_last_datum+=num_new_data
248 jgs 119 self.__id_last_unreferenced_datum+=num_new_data
249     if self.debug(): print "Debug: %s: %d data appended. Last unreferenced datum is now %d."%(self,num_new_data,self.__id_last_unreferenced_datum)
250 jgs 110
251 jgs 117 # ======================================
252 jgs 119 class TimeSeriesControlerView(TimeSeriesBase):
253     """A TimeSeriesControlerView is attached to a Controler and moves forward in time by increasing the id of the last processed datum.
254     Any implementation of a TimeSeriesControlerView must provide the getControler method which returns the controler"""
255     def __init__(self,id_first_datum=0,debug=False,description="TimeSeries"):
256     TimeSeriesBase.__init__(self,debug,description)
257     self.__id_last_processed_datum=id_first_datum-1
258     if self.debug(): print "Debug: %s created with first datum %d"%(str(self),id_first_datum)
259 jgs 110
260 jgs 117 def getIdOfLastProcessedDatum(self):
261     return self.__id_last_processed_datum
262 jgs 110
263 jgs 117 def updateIdOfLastProcessedDatum(self,id_last_processed_datum):
264     self.__id_last_processed_datum=id_last_processed_datum
265 jgs 110
266 jgs 119 # def getControler(self):
267     # """returns the Controler of the time series (to be overwritten by subclass)"""
268     # pass
269    
270     class TimeSeries(TimeSeriesBaseDataset,TimeSeriesControlerView):
271     """makes TimeSeriesBaseDataset look like a TimeSeries and introduces operations
272     Any implementation of a TimeSeriesControlerView must provide the getControler method which returns the controler"""
273     def __init__(self,dataset,debug=False,description="TimeSeries"):
274     TimeSeriesControlerView.__init__(self,dataset.getIdOfFirstDatum(),debug,description)
275     TimeSeriesBaseDataset.__init__(self,dataset,0,debug,description)
276    
277     def getDataset(self):
278     """returns the TimeSeriesBaseDataset of the time series"""
279     return self.getBaseBuffer()
280    
281     # def getControler(self):
282     # """returns the Controler of the time series (to be overwritten by subclass)"""
283     # pass
284    
285 jgs 117 def __add__(self,arg):
286 jgs 119 if isinstance(arg,TimeSeriesBaseDataset):
287     return TimeSeriesAdd(self,arg)
288 jgs 117 else:
289     return TimeSeriesAddScalar(self,arg)
290 jgs 110
291 jgs 117 def __sub__(self,arg):
292     return self+(-1.)*arg
293    
294     def __mul__(self,arg):
295 jgs 119 if isinstance(arg,TimeSeriesBaseDataset):
296 jgs 117 return TimeSeriesMult(self,arg)
297     else:
298     return TimeSeriesMultScalar(self,arg)
299    
300     def __div__(self,arg):
301 jgs 119 if isinstance(arg,TimeSeriesBaseDataset):
302 jgs 117 return TimeSeriesDiv(self,arg)
303     else:
304     return TimeSeriesMultScalar(self,1./arg)
305    
306     def __pow__(self,arg):
307 jgs 119 if isinstance(arg,TimeSeriesBaseDataset):
308 jgs 117 return TimeSeriesPower(self,arg)
309     else:
310     return TimeSeriesPowerScalar(self,arg)
311 jgs 110
312 jgs 117 def __radd__(self,arg):
313     return self.__add__(arg)
314    
315     def __rsub__(self,arg):
316     return arg+(-1.)*self
317    
318     def __rmul__(self,arg):
319     return self.__mul__(arg)
320    
321     def __rdiv__(self,arg):
322 jgs 119 if isinstance(arg,TimeSeriesBaseDataset):
323 jgs 117 return TimeSeriesDiv(arg,self)
324     else:
325     return TimeSeriesDivScalar(self,arg)
326    
327     def __rpow__(self,arg):
328 jgs 119 if isinstance(arg,TimeSeriesBaseDataset):
329 jgs 117 return TimeSeriesPower(arg,self)
330     else:
331     return Exp(numarray.log(arg)*self)
332    
333     def __lshift__(self,arg):
334     return TimeSeriesShift(self,-arg)
335    
336     def __rshift__(self,arg):
337     return TimeSeriesShift(self,arg)
338    
339     def __neg__(self):
340     return (-1.0)*self
341    
342     def __pos__(self):
343     return (1.0)*self
344    
345 jgs 119 class TimeSeriesOperator(TimeSeriesControlerView):
346     """a TimeSeriesOperator decribes an operation acting on list of TimeSeries time_series_args. It allows to update its output (if there is any)
347     through the update method which is overwritten by a particular implementation of the class. The update method is called to process the data [start:end] using
348     [start-left_wing_size:end+right_wing_size] of its arguments"""
349     def __init__(self,controler,time_series_args=[],left_wing_size=0,right_wing_size=0,debug=False,description="TimeSeriesOperator"):
350     id_first_datum=controler.getIdOfFirstDatum()
351     for i in time_series_args: id_first_datum=max(id_first_datum,i.getIdOfFirstDatum())
352     TimeSeriesControlerView.__init__(self,id_first_datum+left_wing_size,debug,description)
353 jgs 117 self.__left_wing_size=left_wing_size
354     self.__right_wing_size=right_wing_size
355 jgs 119 self.__time_series_args=time_series_args
356 jgs 117 self.__controler=controler
357 jgs 119 controler.appendOperatorToUpdateList(self)
358     if self.debug(): print "Debug: %s: with left/right wing size %d/%d and %d arguments."%(str(self),left_wing_size,right_wing_size,len(time_series_args))
359 jgs 117
360 jgs 119 def __del__(self):
361     self.getControler().removeOperatorFromUpdateList(self)
362    
363 jgs 117 def getControler(self):
364 jgs 119 """returns the Controler updating the TimeSeriesOperator"""
365 jgs 117 return self.__controler
366    
367     def getLeftWingSize(self):
368     """returns the left wing size"""
369     return self.__left_wing_size
370    
371     def getRightWingSize(self):
372     """returns the right wing size"""
373     return self.__right_wing_size
374    
375     def getArguments(self,index=None):
376     """returns the list of arguments or, index is present, the argument with index index. In the latter case None is returned if no arguments are present"""
377     if index==None:
378 jgs 119 return self.__time_series_args
379 jgs 117 else:
380 jgs 119 if len(self.__time_series_args)>0:
381     return self.__time_series_args[index]
382 jgs 117 else:
383     return None
384    
385     def getArgumentDataset(self,index):
386     """returns the dataset of in the argument with index index"""
387     arg=self.getArguments(index)
388     if arg==None:
389     return None
390     else:
391     return self.getArguments(index).getDataset()
392    
393     def flush(self):
394     """calls the update method with all the maximum processable range. It also updates the id of unused datum for all arguments"""
395     start=self.getIdOfLastProcessedDatum()+1
396 jgs 119 end=self.getControler().getIdOfLastDatum()
397     for i in self.getArguments(): end=min(end,i.getIdOfLastDatum())
398     if start<=end-self.getRightWingSize():
399     if self.debug(): print "Debug: %s: range [%d:%d] is updated."%(self,start,end-self.getRightWingSize())
400     self.update(start,end-self.getRightWingSize()+1)
401     for i in self.getArguments(): i.updateIdOfLastUnreferencedDatum(end-self.getLeftWingSize())
402     self.updateIdOfLastProcessedDatum(end)
403 jgs 117
404 jgs 119 def update(self,start,end):
405     """updates the the data [start:end] using [start-left_wing_size:end+right_wing_size] of its arguments (is overwritten by a particular TimeSeriesOperator)"""
406     pass
407    
408    
409     class TimeSeriesFilter(TimeSeries,TimeSeriesOperator):
410     """a TimeSeriesFilter is a TimeSeries taht is created trough a TimeSeriesOperator"""
411     def __init__(self,controler,dataset,time_series_args=[],left_wing_size=0,right_wing_size=0,debug=False,description="TimeSeriesFilter"):
412     TimeSeriesOperator.__init__(self,controler,time_series_args,left_wing_size,right_wing_size,debug,description)
413     TimeSeries.__init__(self,dataset,debug,description)
414    
415     def update(self,start,end):
416     """appends zeros to the dataset. This method should be overwritten by a particular TimeSeriesFilter"""
417     nc=self.getNumComponents()
418     if nc>1:
419     self.getDataset().append(numarray.zeros([nc,end-start]))
420     else:
421     self.getDataset().append(numarray.zeros(end-start))
422    
423 jgs 117 class Controler(TimeSeries):
424     """controls a set of TimeSeries"""
425 jgs 119 def __init__(self,buffer_size=DEFAULT_BUFFER_SIZE,debug=False,description="TimeSeriesControler"):
426     TimeSeries.__init__(self,TimeSeriesBaseBuffer(buffer_size,1,DEFAULT_FLOAT_TYPE,0,debug,"node buffer of "+description),debug,"nodes of "+description)
427 jgs 117 self.setFlushRate()
428     self.__update_time_series=list()
429    
430     def __del__(self):
431     self.flush()
432    
433     def getControler(self):
434     """returns the Controler of the time series (overwrites method of by TimeSeries)"""
435     return self
436    
437     def setFlushRate(self,rate=50):
438     """set the flush rate, i.e. after rate new time nodes have been checked in the flush method is called."""
439     self.__flush_rate=rate
440     if self.debug(): print "Debug: %s: flush rate is set to %d"%(self,rate)
441 jgs 110
442 jgs 117 def needsFlushing(self):
443     """returns true if the depending TimeSeriesFilters needs to be flushed becuase the time nodes buffer is full or because of the set flush rate"""
444     return self.needsRearrangement(1) or (self.getNumData()+1)%self.__flush_rate==0
445 jgs 110
446     def flush(self):
447 jgs 117 """flushes all dependend TimeSeriesFilters by processing their flush method"""
448     if self.debug(): print "Debug: %s: start flushing"%self
449     for time_serie in self.__update_time_series: time_serie.flush()
450 jgs 110
451 jgs 119 def appendOperatorToUpdateList(self,time_serie):
452     if not time_serie.getControler()==self: raise ValueError,"%s: TimeSeries %s is not defined on this controler."%(self,time_serie)
453 jgs 117 if not self.isEmpty(): raise ValueError,"%s: you can only check in a time series time_serie is controler is empty."%self
454     self.__update_time_series.append(time_serie)
455     if self.debug(): print "Debug: %s: %s has been added to update list."%(self,time_serie)
456 jgs 110
457 jgs 119 def removeOperatorFromUpdateList(self,time_serie):
458     self.__update_time_series.remove(time_serie)
459     if self.debug(): print "Debug: %s: %s has been removed from update list."%(self,time_serie)
460    
461     def nextTime(self,value):
462 jgs 117 if self.needsFlushing(): self.flush()
463     self.getDataset().append(value)
464     if self.debug(): print "Debug: %s: new time node %e has been added."%(self,value)
465 jgs 110
466 jgs 117 class TimeSeriesShift(TimeSeries):
467     """creates a shift of the time series, i.e. if d[n] is the datum at time t[n], the value at t[n] becomes v[n+shift] on the output"""
468     def __init__(self,time_serie,shift=1):
469     if shift<0:
470     dsc="(%s)<<%d"%(time_serie,-shift)
471     else:
472     dsc="(%s)>>%d"%(time_serie,shift)
473     self.__controler=time_serie.getControler()
474 jgs 119 TimeSeries.__init__(self,TimeSeriesBaseDataset(time_serie.getDataset(),-shift,time_serie.debug(),"buffer view to "+dsc),time_serie.debug(),dsc)
475    
476 jgs 117 def getControler(self):
477     return self.__controler
478 jgs 110
479 jgs 119 class TimeSeriesAdd(TimeSeriesFilter):
480 jgs 117 """adds two TimeSeries"""
481     def __init__(self,time_serie_1,time_serie_2):
482     dsc="(%s)+(%s)"%(time_serie_1,time_serie_2)
483     dbg=time_serie_1.debug() or time_serie_2.debug()
484     cntrl=time_serie_1.getControler()
485     if not cntrl==time_serie_2.getControler():
486 jgs 119 raise ValueError("TimeSeriesAdd: %s and %s have different controler."%(time_serie_1,time_serie_2))
487 jgs 117 id_first_datum=max(time_serie_1.getIdOfFirstDatum(),time_serie_2.getIdOfFirstDatum())
488     TimeSeriesFilter.__init__(self,cntrl, \
489 jgs 119 TimeSeriesBaseBuffer(cntrl.getBaseBufferSize(),time_serie_1.getNumComponents(),DEFAULT_FLOAT_TYPE,id_first_datum,dbg,"buffer for "+dsc), \
490 jgs 117 [time_serie_1,time_serie_2],0,0,dbg,dsc)
491    
492     def update(self,start,end):
493     self.append(self.getArgumentDataset(0)[start:end]+self.getArgumentDataset(1)[start:end])
494    
495     class TimeSeriesAddScalar(TimeSeriesFilter):
496     """adds a single value to a TimeSeries"""
497     def __init__(self,time_serie,scalar):
498     dsc="(%s)+(%s)"%(time_serie,scalar)
499     dbg=time_serie.debug()
500     cntrl=time_serie.getControler()
501     id_first_datum=time_serie.getIdOfFirstDatum()
502     TimeSeriesFilter.__init__(self,cntrl, \
503 jgs 119 TimeSeriesBaseBuffer(cntrl.getBaseBufferSize(),time_serie.getNumComponents(),DEFAULT_FLOAT_TYPE,id_first_datum,dbg,"buffer for "+dsc), \
504 jgs 117 [time_serie],0,0,dbg,dsc)
505     self.__scalar=scalar
506    
507     def update(self,start,end):
508     self.append(self.getArgumentDataset(0)[start:end]+self.__scalar)
509    
510     class TimeSeriesMult(TimeSeriesFilter):
511     """multiplies two TimeSeries"""
512     def __init__(self,time_serie_1,time_serie_2):
513     dsc="(%s)*(%s)"%(time_serie_1,time_serie_2)
514     dbg=time_serie_1.debug() or time_serie_2.debug()
515     cntrl=time_serie_1.getControler()
516     if not cntrl==time_serie_2.getControler():
517     raise ValueError("TimeSeriesMult: %s and %s have different controler."%(time_serie_1,time_serie_2))
518     id_first_datum=max(time_serie_1.getIdOfFirstDatum(),time_serie_2.getIdOfFirstDatum())
519     TimeSeriesFilter.__init__(self,cntrl, \
520 jgs 119 TimeSeriesBaseBuffer(cntrl.getBaseBufferSize(),time_serie_1.getNumComponents(),DEFAULT_FLOAT_TYPE,id_first_datum,dbg,"buffer for "+dsc), \
521 jgs 117 [time_serie_1,time_serie_2],0,0,dbg,dsc)
522    
523     def update(self,start,end):
524     self.append(self.getArgumentDataset(0)[start:end]*self.getArgumentDataset(1)[start:end])
525    
526     class TimeSeriesMultScalar(TimeSeriesFilter):
527     """multiplies a TimeSeries with a single value"""
528     def __init__(self,time_serie,scalar):
529     dsc="(%s)*%s"%(time_serie,scalar)
530     dbg=time_serie.debug()
531     cntrl=time_serie.getControler()
532     id_first_datum=time_serie.getIdOfFirstDatum()
533     TimeSeriesFilter.__init__(self,cntrl, \
534 jgs 119 TimeSeriesBaseBuffer(cntrl.getBaseBufferSize(),time_serie.getNumComponents(),DEFAULT_FLOAT_TYPE,id_first_datum,dbg,"buffer for "+dsc), \
535 jgs 117 [time_serie],0,0,dbg,dsc)
536     self.__scalar=scalar
537    
538     def update(self,start,end):
539     self.append(self.getArgumentDataset(0)[start:end]*self.__scalar)
540    
541     class TimeSeriesDiv(TimeSeriesFilter):
542     """divides two TimeSeries"""
543     def __init__(self,time_serie_1,time_serie_2):
544     dsc="(%s)/(%s)"%(time_serie_1,time_serie_2)
545     dbg=time_serie_1.debug() or time_serie_2.debug()
546     cntrl=time_serie_1.getControler()
547     if not cntrl==time_serie_2.getControler():
548     raise ValueError("TimeSeriesDiv: %s and %s have different controler."%(time_serie_1,time_serie_2))
549     id_first_datum=max(time_serie_1.getIdOfFirstDatum(),time_serie_2.getIdOfFirstDatum())
550     TimeSeriesFilter.__init__(self,cntrl, \
551 jgs 119 TimeSeriesBaseBuffer(cntrl.getBaseBufferSize(),time_serie_1.getNumComponents(),DEFAULT_FLOAT_TYPE,id_first_datum,dbg,"buffer for "+dsc), \
552 jgs 117 [time_serie_1,time_serie_2],0,0,dbg,dsc)
553    
554     def update(self,start,end):
555     self.append(self.getArgumentDataset(0)[start:end]/self.getArgumentDataset(1)[start:end])
556    
557     class TimeSeriesDivScalar(TimeSeriesFilter):
558     """divides a scalar be a TimeSerie"""
559     def __init__(self,time_serie,scalar):
560     dsc="(%s)/(%s)"%(scalar,time_serie)
561     dbg=time_serie.debug()
562     cntrl=time_serie.getControler()
563     id_first_datum=time_serie.getIdOfFirstDatum()
564     TimeSeriesFilter.__init__(self,cntrl, \
565 jgs 119 TimeSeriesBaseBuffer(cntrl.getBaseBufferSize(),time_serie.getNumComponents(),DEFAULT_FLOAT_TYPE,id_first_datum,dbg,"buffer for "+dsc), \
566 jgs 117 [time_serie],0,0,dbg,dsc)
567     self.__scalar=scalar
568    
569     def update(self,start,end):
570     self.append(self.__scalar/self.getArgumentDataset(0)[start:end])
571    
572     class TimeSeriesPower(TimeSeriesFilter):
573     """raise one TimeSeries to the power of an other TimeSeries"""
574     def __init__(self,time_serie_1,time_serie_2):
575     dsc="(%s)**(%s)"%(time_serie_1,time_serie_2)
576     dbg=time_serie_1.debug() or time_serie_2.debug()
577     cntrl=time_serie_1.getControler()
578     if not cntrl==time_serie_2.getControler():
579     raise ValueError("TimeSeriesPower: %s and %s have different controler."%(time_serie_1,time_serie_2))
580     id_first_datum=max(time_serie_1.getIdOfFirstDatum(),time_serie_2.getIdOfFirstDatum())
581     TimeSeriesFilter.__init__(self,cntrl, \
582 jgs 119 TimeSeriesBaseBuffer(cntrl.getBaseBufferSize(),time_serie_1.getNumComponents(),DEFAULT_FLOAT_TYPE,id_first_datum,dbg,"buffer for "+dsc), \
583 jgs 117 [time_serie_1,time_serie_2],0,0,dbg,dsc)
584    
585     def update(self,start,end):
586     self.append(self.getArgumentDataset(0)[start:end]**self.getArgumentDataset(1)[start:end])
587    
588     class TimeSeriesPowerScalar(TimeSeriesFilter):
589     """raises a TimeSerie to the power of a scalar"""
590     def __init__(self,time_serie,scalar):
591     dsc="(%s)**(%s)"%(time_serie,scalar)
592     dbg=time_serie.debug()
593     cntrl=time_serie.getControler()
594     id_first_datum=time_serie.getIdOfFirstDatum()
595     TimeSeriesFilter.__init__(self,cntrl, \
596 jgs 119 TimeSeriesBaseBuffer(cntrl.getBaseBufferSize(),time_serie.getNumComponents(),DEFAULT_FLOAT_TYPE,id_first_datum,dbg,"buffer for "+dsc), \
597 jgs 117 [time_serie],0,0,dbg,dsc)
598     self.__scalar=scalar
599    
600     def update(self,start,end):
601     self.append(self.getArgumentDataset(0)[start:end]**self.__scalar)
602    
603     class Exp(TimeSeriesFilter):
604     """"""
605     def __init__(self,time_serie):
606     dsc="exp(%s)"%(time_serie)
607     dbg=time_serie.debug()
608     cntrl=time_serie.getControler()
609     id_first_datum=time_serie.getIdOfFirstDatum()
610     TimeSeriesFilter.__init__(self,cntrl, \
611 jgs 119 TimeSeriesBaseBuffer(cntrl.getBaseBufferSize(),time_serie.getNumComponents(),DEFAULT_FLOAT_TYPE,id_first_datum,dbg,"buffer for "+dsc), \
612 jgs 117 [time_serie],0,0,dbg,dsc)
613    
614     def update(self,start,end):
615     self.append(numarray.exp(self.getArgumentDataset(0)[start:end]))
616    
617 jgs 119 class Writer(TimeSeriesOperator):
618     """writes the time series into an output strim ostream which mast have the writeline method. The values are seperated by the string seperator."""
619     def __init__(self,time_serie,ostream,seperator=",",commend_tag="#"):
620     dsc="write %s to %s"%(time_serie,ostream)
621     dbg=time_serie.debug()
622     cntrl=time_serie.getControler()
623     self.__ostream=ostream
624     self.__seperator=seperator
625     TimeSeriesOperator.__init__(self,cntrl,[time_serie],0,0,dbg,dsc)
626     ostream.writelines("%s time series %s\n"%(commend_tag,str(self)))
627    
628     def update(self,start,end):
629     cntrl=self.getControler()
630     arg=self.getArguments(0)
631     n=arg.getNumComponents()
632     if n<2:
633     for i in range(start,end): self.__ostream.writelines("%s%s%s\n"%(cntrl[i],self.__seperator,arg[i]))
634     else:
635     for i in range(start,end):
636     l="%s"%cntrl[i]
637     for j in range(n): l=l+"%s%s"(self.__seperator,arg[i][j])
638     self.__ostream.writelines("%s\n"%l)
639    
640     class DataCatcher(TimeSeries):
641     """collects data into a time series."""
642     def __init__(self,controler,numComponents=1,description="DataCatcher"):
643     self.__controler=controler
644     dbg=controler.debug()
645     TimeSeries.__init__(self,TimeSeriesBaseBuffer(controler.getBaseBufferSize(),numComponents,DEFAULT_FLOAT_TYPE,controler.getIdOfFirstDatum(),dbg,"buffer for "+description),dbg,description)
646    
647     def getControler(self):
648     return self.__controler
649    
650     def nextValue(self,value):
651     """append a value to the time series"""
652     id_last=self.getIdOfLastDatum()
653     id_current=self.getControler().getIdOfLastDatum()
654     if id_last+1==id_current:
655     self.getDataset().append(value)
656     elif id_last+1<id_current:
657     if self.isEmpty():
658     self.getDataset().append(value)
659     id_last+=1
660     t_last=self.getControler()[id_last]
661     t_current=self.getControler()[id_current]
662     value_last=self[id_last]
663     out=(value_last-value)/(t_last-t_current)*(self.getControler()[id_last+1:id_current+1]-t_current)+value
664     self.getDataset().append(out)
665     else :
666     raise ValueError,"%s: a new time node must be introduced before a new value can be added."
667     self.updateIdOfLastUnreferencedDatum(id_last)
668    
669    
670 jgs 117 class TimeSeriesCumulativeSum(TimeSeriesFilter):
671 jgs 119 """cummulative sum of the time series values"""
672     def __init__(self,time_serie):
673     dsc="cumsum(%s)"%(time_serie)
674     dbg=time_serie.debug()
675     cntrl=time_serie.getControler()
676     id_first_datum=time_serie.getIdOfFirstDatum()
677     TimeSeriesFilter.__init__(self,cntrl, \
678     TimeSeriesBaseBuffer(cntrl.getBaseBufferSize(),time_serie.getNumComponents(),DEFAULT_FLOAT_TYPE,id_first_datum,dbg,"buffer for "+dsc), \
679     [time_serie],0,0,dbg,dsc)
680     self.__last_value=0
681 jgs 117
682 jgs 119 def update(self,start,end):
683     out=numarray.cumsum(self.getArgumentDataset(0)[start:end])+self.__last_value
684     self.__last_value=out[end-start-1]
685     self.append(out)
686 jgs 117
687    
688 jgs 119 class Reader(TimeSeriesBase):
689     """reads a list of input streams and creates a time series for each input stream but on the same Controler where the first column
690     is used to create the time nodes"""
691     def __init__(self,list_of_istreams,buffer_size=DEFAULT_BUFFER_SIZE,seperator=",",commend_tag="#",debug=False):
692     TimeSeriesBase.__init__(self,debug=debug,description="reader")
693     if not isinstance(list_of_istreams,list):
694     self.__list_of_istreams=[list_of_istreams]
695     else:
696     self.__list_of_istreams=list_of_istreams
697     self.__cntrl=Controler(buffer_size,debug,"reader controler")
698     self.__seperator=seperator
699     self.__commend_tag=commend_tag
700     self.__time_series={}
701     self.__t={}
702     self.__v={}
703     # set up the time series:
704     for i in self.__list_of_istreams:
705     line=self.__commend_tag
706     while not line=="" and line[0]==self.__commend_tag:
707     line=i.readline().strip()
708     if line=="":
709     list_of_istreams.remove(i)
710     else:
711     d=line.split(self.__seperator)
712     self.__t[i]=float(d[0])
713     tmp=[]
714     for j in d[1:]: tmp.append(float(j))
715     self.__v[i]=numarray.array(tmp)
716     self.__time_series[i]=DataCatcher(self.__cntrl,len(d)-1,str(i))
717 jgs 110
718 jgs 119 #
719     def run(self):
720     while len(self.__list_of_istreams)>0:
721     if len(self.__time_series)>0:
722     # find list all times with minumum time node:
723     tminargs=[]
724     for i in self.__time_series:
725     if len(tminargs)==0:
726     tminargs.append(i)
727     elif abs(t[tminargs[0]]-self.__t[i])<1.e-8*abs(self.__t[i]):
728     tminargs.append(i)
729     elif self.__t[i]<t[tminargs[0]]:
730     tminargs=[i]
731     # find list all times with minumum time node:
732     self.__cntrl.nextTime(self.__t[tminargs[0]])
733     for i in tminargs:
734     self.__time_series[i].nextValue(self.__v[i])
735     # find next line without leading "#"
736     line="#"
737     while not line=="" and line[0]==self.__commend_tag:
738     line=i.readline().strip()
739     # if eof reached iostream is removed for searching
740     if line=="":
741     self.__list_of_istreams.remove(i)
742     else:
743     d=line.split(self.__seperator)
744     self.__t[i]=float(d[0])
745     tmp=[]
746     for j in d[1:]: tmp.append(float(j))
747     self.__v[i]=numarray.array(tmp)
748 jgs 110
749 jgs 119 def getControler(self):
750     """returns the controler shared by all time series created through the input streams"""
751     return self.__cntrl
752 jgs 110
753 jgs 119 def getTimeSeries(self,istream=None):
754     """returns the time series as a tuple. If istream is present its time series is returned"""
755     if istream==None:
756     out=self.__time_series.values()
757     if len(out)>1:
758     return tuple(out)
759     elif len(out)>0:
760     return out[0]
761     else:
762     return None
763     else:
764     return self.__time_series[istream]
765 jgs 110
766 jgs 119
767     class Plotter(TimeSeriesOperator):
768     def __init__(self,time_series,window_size=DEFAULT_BUFFER_SIZE/4,file_name=None,format=None):
769     if isinstance(time_series,list):
770     dbg=time_series[0].getControler().debug()
771     text=""
772     for i in time_series:
773     if len(text)==0:
774     text=str(i)
775     else:
776     text=text+","+str(i)
777     TimeSeriesOperator.__init__(self,time_series[0].getControler(),time_series,window_size,0,dbg,"plot(%s)"%text)
778     else:
779     dbg=time_series.getControler().debug()
780     text=str(time_series)
781     TimeSeriesOperator.__init__(self,time_series.getControler(),[time_series],window_size,0,dbg,"plot(%s)"%text)
782     from pyvisi.renderers.gnuplot import LinePlot,Scene,PsImage
783     self.__renderer=Scene()
784     self.__line_plot=LinePlot(self.__renderer)
785     self.__line_plot.setTitle(text)
786     self.__line_plot.setLineStyle("lines")
787     self.__line_plot.setXLabel("time")
788     self.__line_plot.setYLabel("values")
789     self.__file_name=file_name
790     if format==None:
791     self.__format=PsImage()
792     else:
793     self.__format=format
794     self.__window_size=window_size
795    
796     def update(self,start,end):
797     s=max(end-self.__window_size,self.getControler().getIdOfFirstAvailableDatum())
798     args=[self.getControler()[s:end]]
799     for arg in self.getArguments(): args.append(arg[s:end])
800     self.__line_plot.setData(*args)
801     self.__line_plot.render()
802     if self.__file_name==None:
803     raise SystemError,"Online viewing is not avilabel yet!"
804     else:
805     self.__renderer.save(fname=self.__file_name, format=self.__format)
806    
807    
808     def viewer(time_serie,seperator=","):
809     """creates a viewer for a time series"""
810     import sys
811     return Writer(time_serie,sys.stdout,seperator)
812    
813     def differential(time_serie):
814 jgs 117 """calculates the derivative Dv of the time series v:
815    
816     Dv[n]=(v[n]-v[n-1])/(t[n]-t[n-1])
817 jgs 110
818 jgs 117 """
819 jgs 119 out=(((time_serie<<1)-time_serie)/((time_serie.getControler()<<1)-time_serie.getControler())+ \
820     ((time_serie>>1)-time_serie)/((time_serie.getControler()>>1)-time_serie.getControler()))/2.
821     out.setDescription("d(%s)/dt"%str(time_serie))
822     out.setDebug(time_serie.debug())
823     return out
824 jgs 110
825 jgs 119 def integral(time_serie):
826 jgs 117 """calculates the intagral Iv of the time series v using the trapozidal rule:
827 jgs 110
828 jgs 119 Iv[n]=int_{t_0}^{t_n} v ~ sum_{0<i<=n} n (v[i]+v[i-1])/2*(t[i]-t[i-1])
829 jgs 110
830 jgs 117 """
831 jgs 119 out=TimeSeriesCumulativeSum(((time_serie>>1)+time_serie)/2.*(time_serie.getControler()-(time_serie.getControler()>>1)))
832     out.setDescription("I (%s) dt"%str(time_serie))
833     out.setDebug(time_serie.debug())
834     return out
835 jgs 110
836 jgs 119 def smooth(time_serie,range=5):
837     """smoothes a time series using the at each time the previous and next range values"""
838     i=integral(time_serie)
839     out=((i>>range)-(i<<range))/((time_serie.getControler()>>range)-(time_serie.getControler()<<range))
840     out.setDescription("smooth(%s,-%d:%d) dt"%(str(time_serie),range,range))
841     out.setDebug(time_serie.debug())
842     return out
843 jgs 110
844 jgs 119 def leakySmooth(time_serie,l=0.99):
845     """leaky smoother: s(t)=int_{t_0}^{t} v(r) l^{t-r} dr/ int_{t_0}^{t} l^{t-r} dr """
846     w=l**(-time_serie.getControler())
847     out=integrate(time_serie*w)/integrate(w)
848     out.setDescription("leaky smoother(%s)"%str(time_serie))
849     return out
850 jgs 110
851     # test
852    
853     if __name__=="__main__":
854 jgs 117 # tests the interfaces to data sets:
855     print "Test of Datasets:"
856     print "================="
857 jgs 119 bf=TimeSeriesBaseBuffer(buffer_size=5,numComponents=1,debug=True,description="TestBaseBuffer")
858     bfv_l=TimeSeriesBaseDataset(bf,offset=1,debug=True,description="offset 1")
859     bfv_r=TimeSeriesBaseDataset(bf,offset=-1,debug=True,description="offset -1")
860 jgs 117 bf.append([1.,2.,3.,4.])
861     print "should be all 2. :",bfv_l[0]
862     print bf[1]
863     print bfv_r[2]
864     bf.append([5.,6.,7.])
865     print "should be all 5. :",bfv_l[3],bf[4],bfv_r[5]
866     print "should be all 6. :",bfv_l[4],bf[5],bfv_r[6]
867     print "should be all 7. :",bfv_l[5],bf[6],bfv_r[7]
868     print "should be all [6., 7.] :",bfv_l[4:6],bf[5:7],bfv_r[6:8]
869 jgs 110
870 jgs 117 print "Test of Controler"
871     print "================="
872     b=Controler(buffer_size=15,debug=True)
873     s3=b>>3
874     s1=b>>1
875     s_3=b<<3
876 jgs 119 print s_3
877     print b
878     print b+s3
879 jgs 117 sum=(s_3+b)+(b+s3)
880    
881     for i in range(30):
882 jgs 119 b.nextTime(i*1.)
883 jgs 117 b.flush()
884     print "should be all 28. :",s_3.getDataset()[25],b.getDataset()[28],s3.getDataset()[31]
885     print "should be all 29. :",s_3.getDataset()[26],b.getDataset()[29],s3.getDataset()[32]
886     print "should be all 96. :",sum.getDataset()[24]
887    
888     print "Test of operators"
889     print "================="
890     b=Controler(buffer_size=15,debug=True)
891 jgs 119 b.setFlushRate(2)
892     q=DataCatcher(b)
893 jgs 117 b1=b<<1
894     a=b+b1
895     a_s=b1+1.
896     s_a=1.+b1
897     d=b-b1
898     d_s=b1-1.
899     s_d=1.-b1
900     m=b*b1
901     m_s=b1*2.
902     s_m=2.*b1
903     dv=b/b1
904     dv_s=b1/2.
905     s_dv=2./b1
906     p=b**b1
907     p_s=b1**2.
908     s_p=2.**b1
909     pb=+b
910     mb=-b
911 jgs 119 sum=TimeSeriesCumulativeSum(b)
912     diff=differential(b)
913     smt=smooth(b,2)
914     int=integral(b*2)
915     fl=file("/tmp/test.csv","w")
916     w=Writer(q,fl)
917     v=viewer(q)
918     plo=Plotter([a,a_s],window_size=4,file_name="s.ps")
919 jgs 117 for i in range(30):
920 jgs 119 b.nextTime(i*1.)
921     if i%2==1: q.nextValue(i*28.)
922 jgs 117 b.flush()
923     print "a[28] should be %e: %e"%(28.+29.,a[28])
924     print "a_s[28] should be %e: %e"%(29.+1.,a_s[28])
925     print "s_a[28] should be %e: %e"%(29.+1.,s_a[28])
926     print "d[28] should be %e: %e"%(28.-29.,d[28])
927     print "d_s[28] should %e: %e"%(29.-1.,d_s[28])
928     print "s_d[28] should %e: %e"%(1.-29.,s_d[28])
929     print "m[28] should be %e: %e"%(28.*29.,m[28])
930     print "m_s[28] should be %e: %e"%(29.*2.,m_s[28])
931     print "s_m[28] should be %e: %e"%(29.*2.,s_m[28])
932     print "dv[28] should be %e: %e"%(28./29.,dv[28])
933     print "dv_s[28] should be %e: %e"%(29./2.,dv_s[28])
934     print "s_dv[28] should be %e: %e"%(2./29.,s_dv[28])
935     print "p[28] should be %e: %e"%(28.**29.,p[28])
936     print "p_s[28] should be %e: %e"%(29.**2,p_s[28])
937     print "s_p[28] should be %e: %e"%(2.**29.,s_p[28])
938     print "pb[28] should be %e: %e"%(28.,pb[28])
939     print "mb[28] should be %e: %e"%(-28.,mb[28])
940 jgs 119 print "sum[28] should be %e: %e"%(28*29./2,sum[28])
941     print "diff[28] should be %e: %e"%(1.,diff[28])
942     print "smt[27] should be %e: %e"%(27.,smt[27])
943     print "int[28] should be %e: %e"%(28.**2,int[28])
944     print "q[27] should be %e: %e"%(27*28.,q[27])
945     print "q[28] should be %e: %e"%(28*28.,q[28])
946     print "q[29] should be %e: %e"%(29*28.,q[29])
947     fl.flush()
948    
949     rin=Reader(file("/tmp/test.csv","r+"),buffer_size=15,debug=True)
950     rin.run()
951     inp=rin.getTimeSeries()
952     print "inp[27] should be %e: %e"%(27*28.,inp[27])
953     print "inp[28] should be %e: %e"%(28*28.,inp[28])
954     print "inp[29] should be %e: %e"%(29*28.,inp[29])
955 jgs 117

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

  ViewVC Help
Powered by ViewVC 1.1.26