Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/tools/contributed/sumopy/agilepy/lib_base/arrayman.py
169689 views
1
# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
2
# Copyright (C) 2016-2025 German Aerospace Center (DLR) and others.
3
# SUMOPy module
4
# Copyright (C) 2012-2021 University of Bologna - DICAM
5
# This program and the accompanying materials are made available under the
6
# terms of the Eclipse Public License 2.0 which is available at
7
# https://www.eclipse.org/legal/epl-2.0/
8
# This Source Code may also be made available under the following Secondary
9
# Licenses when the conditions for such availability set forth in the Eclipse
10
# Public License 2.0 are satisfied: GNU General Public License, version 2
11
# or later which is available at
12
# https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
13
# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
14
15
# @file arrayman.py
16
# @author Joerg Schweizer
17
# @date 2012
18
19
from classman import *
20
import numpy as np
21
22
23
class ArrayConfMixin:
24
def __init__(self, attrname, default, dtype=None, is_index=False, **attrs):
25
self._is_index = is_index
26
self._dtype = dtype
27
AttrConf.__init__(self, attrname, default,
28
struct='array',
29
**attrs)
30
31
if is_index:
32
self._init_indexing()
33
34
def get_dtype(self):
35
return self._dtype
36
37
def convert_type(self, array):
38
return np.array(array, dtype=self._dtype)
39
40
def get_defaults(self, ids):
41
# create a list, should work for all types and dimensions
42
# default can be scalar or an array of any dimension
43
# print '\n\nget_defaults',self._name,ids
44
default = self.get_default()
45
46
if hasattr(default, '__iter__'):
47
default = np.asarray(default)
48
if self._dtype is not None:
49
dtype = self._dtype
50
else:
51
dtype = type(default.flatten()[0])
52
# print ' default=',default,len(default)
53
if len(ids) > 0:
54
defaults = np.array(len(ids)*[default], dtype)
55
# print ' size,type',len(ids)*[default], type(default.flatten()[0])
56
else:
57
#defaults = np.zeros( (0,len(default)),type(default.flatten()[0]) )
58
defaults = np.zeros((0,)+default.shape, dtype)
59
# print ' return',defaults,defaults.shape,defaults.dtype
60
return defaults
61
else:
62
if self._dtype is not None:
63
dtype = self._dtype
64
else:
65
dtype = type(default)
66
#defaults= np.array( len(ids)*[default], dtype )
67
# print ' return 1D',defaults,defaults.shape,defaults.dtype
68
return np.array(len(ids)*[default], dtype)
69
70
def get_init(self):
71
"""
72
Returns initialization of attribute.
73
Usually same as get_default for scalars.
74
Overridden by table configuration classes
75
"""
76
ids = self._manager.get_ids()
77
78
# print '\n\nget_init',self.attrname,ids,self._is_localvalue
79
values = self.get_defaults(ids)
80
81
# store locally if required
82
if self._is_localvalue:
83
self.value = values
84
# pass on to calling instance
85
# in this cas the data is stored under self._obj
86
return values
87
88
def get_ids_sorted(self):
89
inds = self._manager.get_inds()
90
return self._manager.get_ids(inds[np.argsort(self.get_value()[inds])])
91
92
#sortarray = np.concatenate((self.get_value()[inds],inds))
93
94
def _del_rows(self, ids_del, inds_remain):
95
"""
96
Multiple row delete method used by attrsman
97
"""
98
self.set_value(self.get_value()[inds_remain])
99
100
def delete_ind(self, i):
101
# called from del_rows
102
if self._is_index:
103
_id = self._manager._ids[i]
104
self.del_index(_id)
105
arr = self.get_value()
106
self.set_value(np.concatenate((arr[:i], arr[i+1:])))
107
108
def __delitem__(self, ids):
109
# print ' before=\n',self.__dict__[attr]
110
#attr = self.attrconf.get_attr()
111
if hasattr(ids, '__iter__'):
112
for i in self._manager._inds[ids]:
113
self.delete_ind[i]
114
else:
115
self.delete_ind(self._manager._inds[ids])
116
117
def __getitem__(self, ids):
118
# print '__getitem__',key
119
return self.get_value()[self._manager._inds[ids]]
120
121
def __setitem__(self, ids, values):
122
# print '__setitem__',ids,values,type(self.get_value()),self.get_value().dtype
123
124
if self._is_index:
125
if hasattr(ids, '__iter__'):
126
self.set_indices(ids, values)
127
128
else:
129
self.set_index(ids, values)
130
131
self.get_value()[self._manager._inds[ids]] = values
132
133
def set(self, ids, values):
134
if values is None:
135
return
136
137
if not hasattr(ids, '__iter__'):
138
_ids = [ids]
139
_values = np.array([values], self._dtype)
140
141
else:
142
_ids = ids
143
_values = np.array(values, self._dtype)
144
# print 'set', _ids ,_values
145
self[_ids] = _values
146
if self._is_index:
147
self.set_indices(_ids, _values)
148
self._is_modified = True
149
150
def set_plugin(self, ids, values):
151
if not hasattr(ids, '__iter__'):
152
_ids = [ids]
153
_values = np.array([values], self._dtype)
154
155
else:
156
_ids = ids
157
_values = np.array(values, self._dtype)
158
159
self[ids] = _values
160
if self._is_index:
161
self.set_indices(_ids, _values)
162
self._is_modified = True
163
self.plugin.exec_events_ids(EVTSETITEM, _ids)
164
165
def add(self, ids, values=None):
166
if not hasattr(ids, '__iter__'):
167
_ids = [ids]
168
if values is not None:
169
_values = np.array([values], self._dtype)
170
else:
171
_values = self.get_defaults(_ids)
172
173
else:
174
# if values is None:
175
# print 'WARNING:',self.attrname, ids,self._dtype
176
_ids = ids
177
if values is not None:
178
_values = np.array(values, self._dtype)
179
else:
180
_values = self.get_defaults(_ids)
181
# if values is None:
182
# _values = self.get_defaults(_ids)
183
184
# print 'add ids',self.attrname,ids,_ids,self._is_modified
185
# print ' values',values
186
# print ' _values',_values
187
# print ' self.get_value()',self.get_value()
188
# print ' type(_values),type(self.get_value())',type(_values),type(self.get_value())
189
# print ' _values.shape,self.get_value().shape',_values.shape,self.get_value().shape
190
191
#newvalue = np.concatenate((self.get_value(),_values))
192
# print ' ', type(newvalue),newvalue.dtype
193
self.set_value(np.concatenate((self.get_value(), _values)))
194
# print ' done:',self.attrname,self.get_value()
195
if self._is_index:
196
self.add_indices(_ids, _values)
197
self._is_modified = True
198
199
def add_plugin(self, ids, values=None):
200
# print 'add_plugin',self.attrname
201
if not hasattr(ids, '__iter__'):
202
_ids = [ids]
203
if values is not None:
204
_values = np.array([values], self._dtype)
205
206
else:
207
_ids = ids
208
if values is not None:
209
_values = np.array(values, self._dtype)
210
211
if values is None:
212
_values = self.get_defaults(_ids)
213
self._is_modified = True
214
# print 'add ids',self.attrname,ids,_ids,self._is_modified
215
# print ' values',values
216
# print ' _values',_values
217
# print ' self.get_value()',self.get_value()
218
# print ' type(_values),type(self.get_value())',type(_values),type(self.get_value())
219
# print ' _values.shape,self.get_value().shape',_values.shape,self.get_value().shape
220
221
#newvalue = np.concatenate((self.get_value(),_values))
222
# print ' ', type(newvalue),newvalue.dtype
223
self.set_value(np.concatenate((self.get_value(), _values)))
224
# print ' done:',self.attrname,self.get_value()
225
226
if self._is_index:
227
self.add_indices(_ids, _values)
228
229
self.plugin.exec_events_ids(EVTADDITEM, _ids)
230
231
# use original one from AttrConfig
232
# def _write_xml_value(self,val,fd):
233
# #print 'write_xml',self.xmltag,type(val),hasattr(val, '__iter__')
234
# if hasattr(val, '__iter__'):
235
# if len(val)>0:
236
# if hasattr(val[0], '__iter__'):
237
# # matrix
238
# fd.write(xm.mat(self.xmltag,val))
239
# else:
240
# if type(val)==np.ndarray:
241
# # vector
242
# fd.write(xm.arr(self.xmltag,val,sep=','))
243
# else:
244
# # list
245
# fd.write(xm.arr(self.xmltag,val))
246
# else:
247
# # empty list
248
# fd.write(xm.arr(self.xmltag,val))
249
# else:
250
# # scalar number or string
251
# fd.write(xm.num(self.xmltag,val))
252
253
def format_value(self, _id, show_unit=False, show_parentesis=False):
254
# TODO: handle also linked ids, etc...
255
if show_unit:
256
unit = ' '+self.format_unit(show_parentesis)
257
else:
258
unit = ''
259
# return repr(self[_id])+unit
260
261
#self.min = minval
262
#self.max = maxval
263
#self.digits_integer = digits_integer
264
#self.digits_fraction = digits_fraction
265
val = self[_id]
266
tt = type(val)
267
268
if tt in (np.int, np.int32, np.float64):
269
return str(val)+unit
270
271
elif tt in (np.float, np.float32, np.float64):
272
if hasattr(self, 'digits_fraction'):
273
digits_fraction = self.digits_fraction
274
else:
275
digits_fraction = 3
276
277
s = "%."+str(digits_fraction)+"f"
278
# print 'format_value df=',digits_fraction,'val=',val
279
280
return s % (val)+unit
281
282
else:
283
return str(val)+unit
284
285
286
class ArrayConf(ArrayConfMixin, ColConf):
287
"""
288
Column made of numeric array.
289
290
"""
291
pass
292
293
294
class ListArrayConf(ArrayConfMixin, ColConf):
295
"""
296
Column made of an array of lists.
297
298
"""
299
300
def __init__(self, attrname, dtype=None, **attrs):
301
ArrayConfMixin.__init__(self, attrname, None, dtype=np.object, **attrs)
302
303
def add(self, ids, values=None):
304
if not hasattr(ids, '__iter__'):
305
_ids = [ids]
306
if values is not None:
307
_values = np.zeros(1, self._dtype)
308
_values[0] = values
309
310
else:
311
312
_ids = ids
313
if values is not None:
314
_values = np.zeros(len(ids), self._dtype)
315
_values[:] = values
316
317
if values is None:
318
_values = self.get_defaults(_ids)
319
320
# print 'add ids, _values',self.attrname,ids
321
# print ' values',values
322
# print ' _values',_values
323
# print ' self.get_value()',self.get_value()
324
# print ' type(_values),type(self.get_value())',type(_values),type(self.get_value())
325
# print ' _values.shape,self.get_value().shape',_values.shape,self.get_value().shape
326
327
newvalue = np.concatenate((self.get_value(), _values))
328
# print ' ', type(newvalue),newvalue.dtype
329
self.set_value(np.concatenate((self.get_value(), _values)))
330
331
if self._is_index:
332
self.add_indices(_ids, _values)
333
334
def add_plugin(self, ids, values=None):
335
# print 'add_plugin',self.attrname,ids
336
if not hasattr(ids, '__iter__'):
337
_ids = [ids]
338
if values is not None:
339
_values = np.zeros(1, self._dtype)
340
_values[0] = values
341
342
else:
343
344
_ids = ids
345
if values is not None:
346
_values = np.zeros(len(ids), self._dtype)
347
_values[:] = values
348
349
if values is None:
350
_values = self.get_defaults(_ids)
351
352
# print 'add ids, _values',self.attrname,ids
353
# print ' values',values
354
# print ' _values',_values
355
# print ' self.get_value()',self.get_value()
356
# print ' type(_values),type(self.get_value())',type(_values),type(self.get_value())
357
# print ' _values.shape,self.get_value().shape',_values.shape,self.get_value().shape
358
359
newvalue = np.concatenate((self.get_value(), _values))
360
# print ' ', type(newvalue),newvalue.dtype
361
self.set_value(np.concatenate((self.get_value(), _values)))
362
363
if self._is_index:
364
self.add_indices(_ids, _values)
365
366
if self.plugin:
367
self.plugin.exec_events_ids(EVTADDITEM, _ids)
368
369
370
class NumArrayConf(ArrayConfMixin, ColConf):
371
"""
372
Column made of numeric array.
373
374
"""
375
# def __init__(self, **attrs):
376
# print 'ColConf',attrs
377
378
def __init__(self, attrname, default,
379
digits_integer=None, digits_fraction=None,
380
minval=None, maxval=None,
381
**attrs):
382
383
self.min = minval
384
self.max = maxval
385
self.digits_integer = digits_integer
386
self.digits_fraction = digits_fraction
387
388
ArrayConfMixin.__init__(self, attrname, default, metatype='number', **attrs)
389
390
def format_value(self, _id, show_unit=False, show_parentesis=False):
391
# TODO: handle also linked ids, etc...
392
if show_unit:
393
unit = ' '+self.format_unit(show_parentesis)
394
else:
395
unit = ''
396
# return repr(self[_id])+unit
397
398
#self.min = minval
399
#self.max = maxval
400
#self.digits_integer = digits_integer
401
#self.digits_fraction = digits_fraction
402
val = self[_id]
403
if self.digits_fraction is None:
404
digits_fraction = 3
405
else:
406
digits_fraction = self.digits_fraction
407
408
s = "%."+str(digits_fraction)+"f"
409
# print 'format_value df=',digits_fraction,'val=',val
410
411
return s % (val)+unit
412
413
414
class IdsArrayConf(ArrayConfMixin, ColConf):
415
"""
416
Column, where each entry is the id of a single Table.
417
"""
418
419
def __init__(self, attrname, tab, is_index=False, id_default=-1, perm='r', **kwargs):
420
self._tab = tab
421
ArrayConfMixin.__init__(self, attrname,
422
id_default, # default id
423
dtype=np.int32,
424
metatype='id',
425
perm=perm,
426
is_index=is_index,
427
**kwargs
428
)
429
self.init_xml()
430
# print 'IdsConf.__init__',attrname
431
# print ' ',self._tab.xmltag,self._attrconfig_id_tab
432
433
def get_defaults(self, ids):
434
# create a list, should work for all types and dimensions
435
# default can be scalar or an array of any dimension
436
# print '\n\nget_defaults',self.attrname,ids,self.get_default()
437
return self.get_default()*np.ones(len(ids), dtype=self._dtype)
438
439
440
# -------------------------------------------------------------------------------
441
# copied from IdsConf!!!
442
443
def set_linktab(self, tab):
444
self._tab = tab
445
446
def get_linktab(self):
447
return self._tab
448
449
def init_xml(self):
450
# print 'init_xml',self.attrname,self._tab
451
if self._tab.xmltag is not None:
452
xmltag_tab, xmltag_item_tab, attrname_id_tab = self._tab.xmltag
453
if (attrname_id_tab is None) | (attrname_id_tab is ''):
454
self._attrconfig_id_tab = None
455
else:
456
self._attrconfig_id_tab = getattr(self._tab, attrname_id_tab) # tab = tabman !
457
458
if not hasattr(self, 'is_xml_include_tab'):
459
# this means that entire table rows will be included
460
self.is_xml_include_tab = False
461
# print ' xmltag_tab, xmltag_item_tab, attrname_id_tab',xmltag_tab, xmltag_item_tab, attrname_id_tab,self.is_xml_include_tab
462
463
else:
464
self._attrconfig_id_tab = None
465
self.is_xml_include_tab = False
466
467
def write_xml(self, fd, _id, indent=0):
468
# print 'write_xml',self.attrname
469
if (self.xmltag is not None) & (np.all(self[_id] >= 0)):
470
# print 'write_xml',self.attrname, _id,'value',self[_id]
471
if self._attrconfig_id_tab is None:
472
self._write_xml_value(self[_id], fd)
473
elif self.is_xml_include_tab:
474
# print ' write table row(s)',self[_id]
475
self._tab.write_xml(fd, indent, ids=self[_id],
476
is_print_begin_end=False)
477
else:
478
# print ' write id(s)',self[_id]
479
self._write_xml_value(self._attrconfig_id_tab[self[_id]], fd)
480
481
def _write_xml_value(self, val, fd):
482
# print 'write_xml',self.xmltag,hasattr(val, '__iter__')
483
if hasattr(val, '__iter__'):
484
if len(val) > 0:
485
if hasattr(val[0], '__iter__'):
486
# matrix
487
fd.write(xm.mat(self.xmltag, val))
488
else:
489
# list
490
fd.write(xm.arr(self.xmltag, val, self.xmlsep))
491
else:
492
# empty list
493
# fd.write(xm.arr(self.xmltag,val))
494
# don't even write empty lists
495
pass
496
497
elif type(self._default) in (types.UnicodeType, types.StringType):
498
if len(val) > 0:
499
fd.write(xm.num(self.xmltag, val))
500
501
else:
502
# scalar number or string
503
fd.write(xm.num(self.xmltag, val))
504
505
def _getstate_specific(self, state):
506
"""
507
Called by __getstate__ to add/change specific states,
508
before returning states.
509
To be overridden.
510
"""
511
if self._is_save:
512
# if self._is_child:
513
# # OK self.value already set in
514
# pass
515
# else:
516
# # remove table reference and create ident
517
# print '_getstate_specific',self.attrname
518
# print ' self._tab',self._tab
519
# print '_getstate_specific',self._tab.ident, self._tab.get_ident_abs()
520
state['_tab'] = None
521
# try:
522
state['_ident_tab'] = self._tab.get_ident_abs()
523
# except:
524
# print 'WARNING:_getstate_specific',self._tab,self._tab.attrname
525
526
def init_postload_internal(self, man, obj):
527
# print 'IdsConf.init_postload_internal',self.attrname,hasattr(self,'value'),self._is_save,self._is_localvalue,'obj:',obj.ident
528
529
AttrConf.init_postload_internal(self, man, obj)
530
# print 'IdsConf.init_postload_internal',self.attrname,self.get_value().dtype,self.get_value().dtype == np.int64
531
if self.get_value().dtype == np.int64:
532
print 'WARNING in init_postload_internal: convert ids array to 32 bit'
533
self.set_value(np.array(self.get_value(), dtype=np.int32))
534
# if self._is_child:
535
# print ' make sure children get initialized'
536
# print ' call init_postload_internal of',self._tab.ident
537
# self._tab.init_postload_internal(obj)
538
539
def init_postload_external(self):
540
# if self._is_child:
541
# # restore normally
542
# AttrConf.init_postload_external(self)
543
# self._tab.init_postload_external()
544
# else:
545
546
# Substitute absolute ident with link object.
547
# Called from init_postload_external of attrsman during load_obj
548
#
549
ident_abs = self._ident_tab
550
# print 'init_postload_external',self.attrname,ident_abs
551
obj = self.get_obj()
552
rootobj = obj.get_root()
553
# print ' obj,rootobj',obj,rootobj
554
linkobj = rootobj.get_obj_from_ident(ident_abs)
555
# print ' linkobj',linkobj.ident
556
self._tab = linkobj
557
self.init_xml()
558
559
def is_modified(self):
560
return False
561
562
def set_modified(self, is_modified):
563
pass
564
565
566
class IdlistsArrayConf(IdsArrayConf):
567
"""
568
Column, where each entry is a list of ids of a single Table.
569
"""
570
571
def __init__(self, attrname, tab, metatype=None, perm='r', **kwargs):
572
self._is_index = False
573
self._tab = tab
574
ArrayConfMixin.__init__(self, attrname,
575
None, # default, will be substituted by id list
576
dtype='object',
577
metatype='ids',
578
perm=perm,
579
**kwargs
580
)
581
self.init_xml()
582
583
def get_defaults(self, ids):
584
# here we initialize with None for reach element
585
return np.array(len(ids)*[None, ], self._dtype)
586
587
def add(self, ids, values=None):
588
if not hasattr(ids, '__iter__'):
589
_ids = [ids]
590
if values is not None:
591
_values = np.zeros(1, self._dtype)
592
_values[0] = values
593
594
else:
595
596
_ids = ids
597
_values = np.zeros(len(ids), self._dtype)
598
_values[:] = values
599
600
if values is None:
601
_values = self.get_defaults(_ids)
602
603
# print 'add ids, _values',self.attrname,ids
604
# print ' values',values
605
# print ' _values',_values
606
# print ' self.get_value()',self.get_value()
607
# print ' type(_values),type(self.get_value())',type(_values),type(self.get_value())
608
# print ' _values.shape,self.get_value().shape',_values.shape,self.get_value().shape
609
610
newvalue = np.concatenate((self.get_value(), _values))
611
# print ' ', type(newvalue),newvalue.dtype
612
self.set_value(np.concatenate((self.get_value(), _values)))
613
614
if self._is_index:
615
self.add_indices(_ids, _values)
616
617
618
class TabIdListArrayConf(ArrayConfMixin, ColConf):
619
"""
620
Column made of an array of lists with (table,id) tupels.
621
The tables are linked, and will not be saved.
622
"""
623
624
def __init__(self, attrname, dtype=None, perm='r', **attrs):
625
ArrayConfMixin.__init__(self, attrname, None, # default, will be substituted by (table,id) list
626
dtype='object',
627
metatype='tabidlist',
628
perm=perm, **attrs)
629
630
def add(self, ids, values=None):
631
if not hasattr(ids, '__iter__'):
632
_ids = [ids]
633
if values is not None:
634
_values = np.zeros(1, self._dtype)
635
_values[0] = values
636
637
else:
638
639
_ids = ids
640
_values = np.zeros(len(ids), self._dtype)
641
_values[:] = values
642
643
if values is None:
644
_values = self.get_defaults(_ids)
645
646
# print 'add ids, _values',self.attrname,ids
647
# print ' values',values
648
# print ' _values',_values
649
# print ' self.get_value()',self.get_value()
650
# print ' type(_values),type(self.get_value())',type(_values),type(self.get_value())
651
# print ' _values.shape,self.get_value().shape',_values.shape,self.get_value().shape
652
653
newvalue = np.concatenate((self.get_value(), _values))
654
# print ' ', type(newvalue),newvalue.dtype
655
self.set_value(np.concatenate((self.get_value(), _values)))
656
657
if self._is_index:
658
self.add_indices(_ids, _values)
659
660
def add_plugin(self, ids, values=None):
661
if not hasattr(ids, '__iter__'):
662
_ids = [ids]
663
if values is not None:
664
_values = np.zeros(1, self._dtype)
665
_values[0] = values
666
667
else:
668
669
_ids = ids
670
_values = np.zeros(len(ids), self._dtype)
671
_values[:] = values
672
673
if values is None:
674
_values = self.get_defaults(_ids)
675
676
###
677
678
def format_value(self, _id, show_unit=False, show_parentesis=False):
679
s = ''
680
rowlist = self[_id]
681
if rowlist is None:
682
return s
683
# elif (type(rowlist)in STRINGTYPES):
684
# return rowlist
685
elif len(rowlist) == 0:
686
return s
687
elif len(rowlist) == 1:
688
tab, ids = rowlist[0]
689
return str(tab)+'['+str(ids)+']'
690
elif len(rowlist) > 1:
691
tab, ids = rowlist[0]
692
s = str(tab)+'['+str(ids)+']'
693
for tab, ids in rowlist[1:]:
694
s += ','+str(tab)+'['+str(ids)+']'
695
return s
696
697
def _getstate_specific(self, state):
698
"""
699
Called by __getstate__ to add/change specific states,
700
before returning states.
701
To be overridden.
702
"""
703
# print '_getstate_specific',self.attrname, self._is_save
704
# print ' self.get_value',self.get_value()
705
# print len(self.get_value())
706
if self._is_save:
707
n = len(state['value'])
708
state['value'] = None
709
_tabidlists_save = n*[None]
710
i = 0
711
for rowlist in self.get_value():
712
if rowlist is not None:
713
rowlist_save = []
714
for tab, ids in rowlist:
715
rowlist_save.append([tab.get_ident_abs(), ids])
716
# print ' tab.get_ident'.get_ident()
717
# print ' appended',[tab.get_ident_abs(), ids]
718
_tabidlists_save[i] = rowlist_save
719
# print ' ',i,rowlist_save
720
i += 1
721
state['_tabidlists_save'] = _tabidlists_save
722
723
def init_postload_external(self):
724
# Substitute absolute ident with link object.
725
# Called from init_postload_external of attrsman during load_obj
726
#
727
# print 'init_postload_external',self.attrname, len(self._tabidlists_save)
728
#obj = self.get_obj()
729
#rootobj = obj.get_root()
730
# print ' rootobj',rootobj.ident
731
#linkobj = rootobj.get_obj_from_ident(ident_abs)
732
# print ' linkobj',linkobj.ident
733
#self._tab = linkobj
734
735
# Substitute absolute ident with link object.
736
# Called from init_postload_external of attrsman during load_obj
737
#
738
_tabidlists_save = self._tabidlists_save
739
#ident_abs = self._ident_value
740
# print 'init_postload_external',self.attrname,_tabids_save
741
obj = self.get_obj()
742
rootobj = obj.get_root()
743
# print ' rootobj',rootobj.ident
744
tabidlists = np.zeros(len(_tabidlists_save), dtype=self._dtype)
745
746
i = 0
747
for rowlist_save in _tabidlists_save:
748
rowlist = []
749
# print ' rowlist_save',rowlist_save
750
if rowlist_save is not None:
751
for tabident, ids in rowlist_save:
752
tab = rootobj.get_obj_from_ident(tabident)
753
# print ' recovered tab',tab.get_ident_abs(), ids
754
rowlist.append([tab, ids])
755
tabidlists[i] = rowlist
756
else:
757
tabidlists[i] = None
758
i += 1
759
# print ' tabidlists', tabidlists
760
self.set_value(tabidlists)
761
762
def is_modified(self):
763
return False
764
765
def set_modified(self, is_modified):
766
pass
767
768
769
class TabIdsArrayConf(ArrayConfMixin, ColConf):
770
"""
771
Column, where each entry contains a tuple with table object and id.
772
The tables are linked, and will not be saved.
773
"""
774
775
def __init__(self, attrname, is_index=False, perm='r', **kwargs):
776
self._is_index = is_index
777
ArrayConfMixin.__init__(self, attrname,
778
(None, -1), # default id
779
dtype=[('ob', object), ('id', np.int)],
780
metatype='tabid',
781
perm=perm,
782
**kwargs
783
)
784
785
def get_defaults(self, ids):
786
# create a list, should work for all types and dimensions
787
# default can be scalar or an array of any dimension
788
# print '\n\nget_defaults',self.attrname,ids,self.get_default()
789
return np.zeros(len(ids), dtype=self._dtype)
790
791
def _getstate_specific(self, state):
792
"""
793
Called by __getstate__ to add/change specific states,
794
before returning states.
795
To be overridden.
796
"""
797
if self._is_save:
798
n = len(state['value'])
799
state['value'] = None
800
_tabids_save = n*[None]
801
i = 0
802
for tab, ids in self.get_value():
803
_tabids_save[i] = [tab.get_ident_abs(), ids]
804
i += 1
805
state['_tabids_save'] = _tabids_save
806
807
def init_postload_external(self):
808
# if self._is_child:
809
# # restore normally
810
# AttrConf.init_postload_external(self)
811
# self._tab.init_postload_external()
812
# else:
813
814
# Substitute absolute ident with link object.
815
# Called from init_postload_external of attrsman during load_obj
816
#
817
#ident_abs = self._ident_tab
818
# print 'reset_linkobj',self.attrname,ident_abs
819
#obj = self.get_obj()
820
#rootobj = obj.get_root()
821
# print ' rootobj',rootobj.ident
822
#linkobj = rootobj.get_obj_from_ident(ident_abs)
823
# print ' linkobj',linkobj.ident
824
#self._tab = linkobj
825
826
# Substitute absolute ident with link object.
827
# Called from init_postload_external of attrsman during load_obj
828
#
829
_tabids_save = self._tabids_save
830
#ident_abs = self._ident_value
831
# print 'init_postload_external',self.attrname,_tabids_save
832
obj = self.get_obj()
833
rootobj = obj.get_root()
834
# print ' rootobj',rootobj.ident
835
tabids = np.zeros(len(self._tabids_save), dtype=self._dtype)
836
837
i = 0
838
for tabident, ids in self._tabids_save:
839
tab = rootobj.get_obj_from_ident(tabident)
840
# print ' ',tab.get_ident_abs(), ids
841
tabids[i] = (tab, ids)
842
i += 1
843
844
self.set_value(tabids)
845
846
def is_modified(self):
847
return False
848
849
def set_modified(self, is_modified):
850
pass
851
852
853
class Arrayman(Tabman):
854
"""
855
Manages all table attributes of an object.
856
857
if argument obj is specified with an instance
858
then column attributes are stored under this instance.
859
The values of attrname is then directly accessible with
860
861
obj.attrname
862
863
If nothing is specified, then column attribute will be stored under
864
the respective config instance of this tab man (self).
865
The values of attrname is then directly accessible with
866
867
self.attrname.value
868
869
"""
870
871
def __init__(self, obj=None, **kwargs):
872
873
Attrsman.__init__(self, obj, **kwargs)
874
self._colconfigs = []
875
self._inds = np.zeros((0,), dtype=np.int32)
876
self._ids = np.zeros((0,), dtype=np.int32)
877
878
def get_inds(self, ids=None):
879
if ids is not None:
880
return self._inds[ids]
881
else:
882
return self._inds[self._ids]
883
884
def get_ids(self, inds=None):
885
if inds is not None:
886
return self._ids[inds]
887
else:
888
return self._ids
889
890
return
891
892
def __getitem__(self, args):
893
if type(args) == types.TupleType:
894
attrnames = args[0]
895
ids = args[1]
896
else:
897
attrnames = args
898
ids = self._ids
899
900
if hasattr(attrnames, '__iter__'):
901
n_rows = len(ids)
902
n_cols = len(attrnames)
903
# print ' n_rows',n_rows,'n_cols',n_cols,attrnames
904
if (n_rows == 0) | (n_cols == 0):
905
return np.array([[]])
906
else:
907
#attrconf = getattr(self,attrnames[0])
908
# TODO: detect dtype by adding numbers or use fields!!!
909
dtype = np.float32
910
out = np.zeros((n_rows, n_cols), dtype=dtype)
911
for i, attrname in zip(xrange(n_cols), attrnames):
912
out[:, i] = getattr(self, attrname)[ids]
913
return out
914
else:
915
return getattr(self, attrnames)[ids]
916
917
def get_ind(self, id):
918
return self._inds[id]
919
920
def select_ids(self, mask):
921
# print 'select_ids'
922
# print ' mask\n=',mask
923
# print ' self._ids\n=',self._ids
924
# if len(self)>0:
925
return np.take(self._ids, np.flatnonzero(mask))
926
# else:
927
# return np.zeros(0,int)
928
929
def suggest_id(self, is_zeroid=False):
930
"""
931
Returns a an availlable id.
932
933
Options:
934
is_zeroid=True allows id to be zero.
935
936
"""
937
return self.suggest_ids(1, is_zeroid)[0]
938
939
def format_id(self, id):
940
return self.format_ids([id])
941
942
def format_ids(self, ids):
943
return ', '.join(np.array(ids, dtype='|S24'))
944
945
def get_id_from_formatted(self, idstr):
946
return int(idstr)
947
948
def get_ids_from_formatted(self, idstrs):
949
idstrs_arr = idstrs.split(',')
950
ids = np.zeros(len(idstrs_arr), dtype=np.float32)
951
i = 0
952
for idstr in idstrs_arr:
953
ids[i] = int[idstr]
954
955
return ids
956
957
def suggest_ids(self, n, is_zeroid=False):
958
"""
959
Returns a list of n availlable ids.
960
It returns even a list for n=1.
961
962
Options:
963
is_zeroid=True allows id to be zero.
964
"""
965
# TODO: does always return 1 if is_index is True ?????
966
# print 'suggest_ids',n,is_zeroid,self._inds,len(self._inds),self._inds.dtype
967
ids_unused_orig = np.flatnonzero(np.less(self._inds, 0))
968
969
if not is_zeroid:
970
if len(self._inds) == 0:
971
ids_unused = np.zeros(0, dtype=np.int32)
972
else:
973
# avoid 0 as id:
974
# ids_unused=take(ids_unused,flatnonzero(greater(ids_unused,0)))
975
# print ' ids_unused_orig',ids_unused_orig,type(ids_unused_orig)
976
# print ' len(ids_unused_orig)',len(ids_unused_orig),ids_unused_orig.shape
977
# print ' greater(ids_unused_orig,0)',greater(ids_unused_orig,0)
978
# print ' len(greater(ids_unused_orig,0))',len(greater(ids_unused_orig,0))
979
# print ' flatnonzero(greater(ids_unused_orig,0))',flatnonzero(greater(ids_unused_orig,0))
980
# print ' len(flatnonzero(greater(ids_unused_orig,0)))=',len(flatnonzero(greater(ids_unused_orig,0)) )
981
ids_unused = ids_unused_orig[np.flatnonzero(np.greater(ids_unused_orig, 0))]
982
zid = 1
983
else:
984
if len(self._inds) == 0:
985
ids_unused = np.zeros(0, dtype=np.int32)
986
else:
987
ids_unused = ids_unused_orig.copy()
988
989
zid = 0
990
991
n_unused = len(ids_unused)
992
n_max = len(self._inds)-1
993
# print ' ids_unused',ids_unused
994
# print ' ids_unused.shape',ids_unused.shape
995
# print ' len(ids_unused)',len(ids_unused)
996
# print ' n_unused,n_max,zid=',n_unused,n_max,zid
997
998
if n_max < zid:
999
# first id generation
1000
ids = np.arange(zid, n+zid, dtype=np.int32)
1001
1002
elif n_unused > 0:
1003
if n_unused >= n:
1004
ids = ids_unused[:n]
1005
else:
1006
# print ' ids_unused',ids_unused
1007
# print ' from to',n_max+1,n_max+1+n-n_unused
1008
# print ' arange=',arange(n_max+1,n_max+1+n-n_unused)
1009
# print ' type(ids_unused)',type(ids_unused)
1010
# print ' dtype(ids_unused)',ids_unused.dtype
1011
ids = np.concatenate((ids_unused, np.arange(n_max+1, n_max+1+n-n_unused)))
1012
1013
else:
1014
ids = np.arange(n_max+1, n_max+1+n, dtype=np.int32)
1015
1016
return ids
1017
1018
def _add_ids(self, ids):
1019
n = len(ids)
1020
if n == 0:
1021
return
1022
1023
id_max = max(ids)
1024
id_max_old = len(self._inds)-1
1025
n_array_old = len(self)
1026
1027
ids_existing = np.take(ids, np.flatnonzero(np.less(ids, id_max_old)))
1028
# print ' ids',ids,'id_max_old',id_max_old,'ids_existing',ids_existing
1029
1030
# check here if ids are still available
1031
# if np.sometrue( np.not_equal( np.take(self._inds, ids_existing), -1) ):
1032
# print 'WARNING in create_ids: some ids already in use',ids_existing
1033
# return np.zeros(0,int)
1034
1035
# extend index map with -1 as necessary
1036
if id_max > id_max_old:
1037
# print 'ext',-1*ones(id_max-id_max_old)
1038
self._inds = np.concatenate((self._inds, -1*np.ones(id_max-id_max_old, int)))
1039
1040
# assign n new indexes to new ids
1041
ind_new = np.arange(n_array_old, n_array_old+n, dtype=np.int32)
1042
1043
# print 'ind_new',ind_new
1044
np.put(self._inds, ids, ind_new)
1045
1046
# print ' concat ids..',self._ids,ids
1047
self._ids = np.concatenate((self._ids, ids))
1048
1049
def add_rows(self, n=None, ids=[], **attrs):
1050
1051
if n is not None:
1052
ids = self.suggest_ids(n)
1053
elif (len(ids) == 0) & (len(attrs) > 0):
1054
# get number of rows from any value vector provided
1055
ids = self.suggest_ids(len(attrs.values()[0]))
1056
elif (n is None) & (len(ids) == 0) & (len(attrs) > 0):
1057
# nothing given really-> do nothing
1058
return np.zeros((0), np.int)
1059
1060
else:
1061
# ids already given , no ids to create
1062
pass
1063
1064
# print 'add_rows ids', ids, type(ids)
1065
self._add_ids(ids)
1066
1067
for colconfig in self._colconfigs:
1068
colconfig.add(ids, values=attrs.get(colconfig.attrname, None))
1069
if self.plugin:
1070
self.plugin.exec_events_ids(EVTADDITEM, ids)
1071
return ids
1072
1073
def copy_cols(self, attrman2, ids=None):
1074
# WARNING: this method is unsafe for indexed colums as values are
1075
# copied and are no longer unique
1076
# print 'copy_cols'
1077
if ids is None:
1078
ids2 = attrman2.get_ids()
1079
else:
1080
ids2 = ids
1081
#ids_new = self.suggest_ids(ids2)
1082
ids_new = self.add_rows(n=len(ids2))
1083
1084
for colconfig2 in attrman2._colconfigs:
1085
if hasattr(self, colconfig2.attrname):
1086
colconfig = getattr(self, colconfig2.attrname)
1087
colconfig.set(ids_new, values=colconfig2[ids2].copy())
1088
1089
return ids_new
1090
1091
def set_rows(self, ids, **attrs):
1092
1093
for colconfig in self._colconfigs:
1094
colconfig.set(ids, values=attrs.get(colconfig.attrname, None))
1095
if self.plugin:
1096
self.plugin.exec_events_ids(EVTSETITEM, ids)
1097
1098
def add_row(self, _id=None, **attrs):
1099
if _id is None:
1100
_id = self.suggest_id()
1101
1102
self._add_ids([_id])
1103
#ids = self.add_rows(1, **attrs)
1104
for colconfig in self._colconfigs:
1105
# print ' add_row',colconfig.attrname,attrs.get(colconfig.attrname, None )
1106
colconfig.add(_id, values=attrs.get(colconfig.attrname, None))
1107
1108
if self.plugin:
1109
self.plugin.exec_events_ids(EVTADDITEM, [id])
1110
return _id
1111
1112
def set_row(self, _id, **attrs):
1113
# if _id is None:
1114
# print ' set_row ',self.get_ident(),attrs
1115
for colconfig in self._colconfigs: # TODO: run through keys!!!!
1116
# print ' add_row',_id,colconfig.attrname,attrs.get(colconfig.attrname, None )
1117
# if attrs.has_key(colconfig.attrname):
1118
#colconfig.set(_id, values = attrs[colconfig.attrname])
1119
colconfig.set(_id, values=attrs.get(colconfig.attrname, None))
1120
1121
if self.plugin:
1122
self.plugin.exec_events_ids(EVTSETITEM, [id])
1123
1124
def del_rows(self, ids_del):
1125
# print '\n\ndel_rows',self.ident,ids_del
1126
# remaining index
1127
if self.plugin:
1128
self.plugin.exec_events_ids(EVTDELITEM, ids_del)
1129
1130
inds_remain = np.ones(len(self), dtype=np.bool_)
1131
inds_remain[self._inds[ids_del]] = False
1132
inds_remain = np.flatnonzero(inds_remain)
1133
for colconfig in self._colconfigs:
1134
colconfig._del_rows(ids_del, inds_remain)
1135
self._ids = self._ids[inds_remain]
1136
if len(self._ids) == 0:
1137
n = 0
1138
else:
1139
n = np.max(self._ids)
1140
self._inds = np.zeros(n+1, dtype=np.int32)
1141
self._inds[self._ids] = np.arange(len(self._ids))
1142
1143
def del_row(self, _id):
1144
# print 'del_row',id
1145
self.del_rows_simple([_id])
1146
1147
def del_rows_simple(self, ids):
1148
# print '\n\ndel_rows',self.ident,ids
1149
# print ' self._ids',self._ids
1150
# print ' self._inds',self._inds
1151
# TODO: this could be done in with array methods
1152
1153
if self.plugin:
1154
self.plugin.exec_events_ids(EVTDELITEM, ids)
1155
for _id in ids:
1156
i = self._inds[_id]
1157
# print ' id to eliminate _id=',_id
1158
# print ' index to eliminate i=',i
1159
for colconfig in self._colconfigs:
1160
# print ' colconfig',colconfig.attrname,i
1161
# colconfig.delete_ind(i)
1162
del colconfig[_id] # this is universal, also for cm.ColConfigs
1163
1164
# print ' del from id lookup'
1165
self._ids = np.concatenate((self._ids[:i], self._ids[i+1:]))
1166
# print ' ids after cut',self._ids
1167
1168
# print ' free index',id
1169
if _id == len(self._inds)-1:
1170
# id is highest, let's shrink index array by 1
1171
self._inds = self._inds[:-1]
1172
else:
1173
self._inds[_id] = -1
1174
1175
# get ids of all indexes which are above i
1176
ids_above = np.flatnonzero(self._inds > i)
1177
1178
# decrease index from those wich are above the deleted one
1179
#put(self._inds, ids_above,take(self._inds,ids_above)-1)
1180
self._inds[ids_above] -= 1
1181
1182
# print ' inds after cut',self._inds
1183
1184
# print ' self._inds',self._inds
1185
1186
# print ' del',ids,' done.'
1187
1188
def clear_rows(self):
1189
# attention: overridden by ArrayObjman
1190
# print 'clear_rows',self.ident
1191
1192
if self.plugin:
1193
self.plugin.exec_events_ids(EVTDELITEM, self.get_ids())
1194
self._ids = []
1195
self._inds = np.zeros((0,), int)
1196
self._ids = np.zeros((0,), int)
1197
1198
for colconfig in self._colconfigs:
1199
# print 'clear_rows',colconfig.attrname,len(colconfig.get_value())
1200
colconfig.clear()
1201
# print ' done',len(colconfig.get_value())
1202
1203
1204
class ArrayObjman(Arrayman, TableMixin):
1205
"""
1206
Array Object management manages objects with numeric Python arrays
1207
based columns. Can also handle list and dict based columns.
1208
"""
1209
1210
def __init__(self, ident, **kwargs):
1211
self._init_objman(ident, **kwargs)
1212
1213
def _init_objman(self, ident, is_plugin=False, **kwargs):
1214
BaseObjman._init_objman(self, ident, managertype='table', **kwargs)
1215
Arrayman.__init__(self, is_plugin=is_plugin)
1216
self.set_attrsman(self)
1217
1218
def init_postload_internal(self, parent):
1219
"""
1220
Called after set state.
1221
Link internal states.
1222
"""
1223
TableMixin.init_postload_internal(self, parent)
1224
attrman = self.get_attrsman()
1225
1226
# this should no longer happen in the future as ind and ids
1227
# have been formatted propperly
1228
if attrman._inds.dtype != np.int32:
1229
print 'WARNING: 64 bit ids and inds...will adjust to 32 bit'
1230
attrman._inds = np.array(attrman._inds, dtype=np.int32)
1231
attrman._ids = np.array(attrman._ids, dtype=np.int32)
1232
1233
def init_postload_external(self):
1234
"""
1235
Called after set state.
1236
Link internal states.
1237
"""
1238
TableMixin.init_postload_external(self)
1239
self._init_attributes()
1240
self._init_constants()
1241
1242
def clear_rows(self):
1243
if self.plugin:
1244
self.plugin.exec_events_ids(EVTDELITEM, self.get_ids())
1245
self._inds = np.zeros((0,), dtype=np.int32)
1246
self._ids = np.zeros((0,), dtype=np.int32)
1247
for colconfig in self.get_attrsman()._colconfigs:
1248
# print 'ArrayObjman.clear_rows',colconfig.attrname,len(colconfig.get_value())
1249
colconfig.clear()
1250
# print ' done',len(colconfig.get_value())
1251
1252
def clear(self):
1253
# print 'ArrayObjman.clear',self.ident
1254
# clear/reset scalars
1255
for attrconfig in self.get_attrsman().get_configs(structs=STRUCTS_SCALAR):
1256
attrconfig.clear()
1257
self.clear_rows()
1258
self._init_constants()
1259
self.set_modified()
1260
1261
# print ' check: suggest_ids',self.suggest_ids(5)
1262
1263