1
2 '''
3 The dataset toolkit module helps in manipulating datasets and retrieving them
4 from simulation results.
5 '''
6
7 import cmath as _cmath
8 import math as _math
9 import numbers as _numbers
10 import operator as _operator
11
12 import empro.datasource as _datasource
13 from empro import _deprecation
14 from empro.datasource import _complexComponentArithmetic, _componentTrigonometric, _componentTrigonometric2, _extendedComponentArithmetic
15
16 try:
17 from collections.abc import Mapping as _Mapping
18 except ImportError:
19 from collections import Mapping as _Mapping
22 '''
23 Return sine of x, with x in radians.
24 x can either be a real or a complex dataset.
25 '''
26 try:
27 return _callBuiltin('sin', x)
28 except TypeError:
29 pass
30 x = _enforceDimensioned(x)
31 try:
32 re, im = x.real, x.imag
33 except AttributeError:
34 return _componentTrigonometric(x, 'Sine')
35 else:
36 return ComplexDataSet(sin(re) * cosh(im), cos(re) * sinh(im), name = 'sin( %s )' % x.name)
37
40 '''
41 Return cosine of x, with x in radians.
42 x can either be a real or a complex dataset.
43 '''
44 try:
45 return _callBuiltin('cos', x)
46 except TypeError:
47 pass
48 x = _enforceDimensioned(x)
49 try:
50 re, im = x.real, x.imag
51 except AttributeError:
52 return _componentTrigonometric(x, 'Cosine')
53 else:
54 return ComplexDataSet(cos(re) * cosh(im), -sin(re) * sinh(im), name = 'cos( %s )' % x.name)
55
58 '''
59 Return tangent of x, with x in radians.
60 x can either be a real or a complex dataset.
61 '''
62 try:
63 return _callBuiltin('tan', x)
64 except TypeError:
65 pass
66 x = _enforceDimensioned(x)
67 try:
68 re, im = x.real, x.imag
69 except AttributeError:
70 return _componentTrigonometric(x, 'Tangent')
71 else:
72 return _named(sin(x) / cos(x), 'tan( %s )' % x.name)
73
76 '''
77 Return arcsine of x, in radians
78 x must be a real dataset.
79 '''
80 try:
81 return _callBuiltin('asin', x)
82 except TypeError:
83 return _componentTrigonometric(_enforceDimensioned(x), 'ArcSine')
84
87 '''
88 Return arccosine of x, in radians
89 x must be a real dataset.
90 '''
91 try:
92 return _callBuiltin('acos', x)
93 except TypeError:
94 return _componentTrigonometric(_enforceDimensioned(x), 'ArcCosine')
95
98 '''
99 Return arctangent of x, in radians
100 x must be a real dataset.
101 '''
102 try:
103 return _callBuiltin('atan', x)
104 except TypeError:
105 return _componentTrigonometric(_enforceDimensioned(x), 'ArcTangent')
106
109 '''
110 Return arctangent of y/x in radians, and places the angle in the quadrant
111 of vector (x, y). Thus atan2(1, 1) == pi/4 but atan2(-1, -1) == -3*pi/4,
112 while atan(1 / 1) == atan(-1 / -1) == pi/4.
113 x and y must be a real datasets.
114 '''
115 try:
116 return _math.atan2(y, x)
117 except TypeError:
118 return _componentTrigonometric2(y, x, 'ArcTangent2')
119
122 '''
123 Return hyperbolic cosine of x.
124 x can either be a real or a complex dataset.
125 '''
126 try:
127 return _callBuiltin('cosh', x)
128 except TypeError:
129 x = _enforceDimensioned(x)
130 return _named(.5 * (exp(x) + exp(-x)), name='cosh( %s )' % x.name)
131
134 '''
135 Return hyperbolic sine of x.
136 x can either be a real or a complex dataset.
137 '''
138 try:
139 return _callBuiltin('sinh', x)
140 except TypeError:
141 x = _enforceDimensioned(x)
142 return _named(.5 * (exp(x) - exp(-x)), name='sinh( %s )' % x.name)
143
146 '''
147 Return hyperbolic tangent of x.
148 x can either be a real or a complex dataset.
149 '''
150 try:
151 return _callBuiltin('tanh', x)
152 except TypeError:
153 x = _enforceDimensioned(x)
154 a, b = exp(x), exp(-x)
155 return _named((a - b) / (a + b), name='tanh( %s )' % x.name)
156
159 '''
160 Return the smallest integer greater than or equal to x.
161 x must be a float or a real dataset.
162 '''
163 try:
164 return _math.ceil(x)
165 except TypeError:
166 return _extendedComponentArithmetic(_enforceDimensioned(x), 'Ceil')
167
170 '''
171 Return the greatest integer smaller than or equal to x.
172 x must be a float or a real dataset.
173 '''
174 try:
175 return _math.floor(x)
176 except TypeError:
177 return _extendedComponentArithmetic(_enforceDimensioned(x), 'Floor')
178
181 '''
182 Return e ** x.
183 x can either be a real or a complex dataset.
184 '''
185 try:
186 return _callBuiltin('exp', x)
187 except TypeError:
188 pass
189 x = _enforceDimensioned(x)
190 try:
191 re, im = x.real, x.imag
192 except AttributeError:
193 return _extendedComponentArithmetic(x, 'Exp')
194 else:
195 return exp(re) * ComplexDataSet(cos(re), sin(im))
196
197
198 -def log(x, base=None):
199 '''
200 Return logarithm of x.
201 If base is not specified, the natural logarithm is returned.
202 x can either be a real or a complex dataset.
203 If x is complex, the cut will extend from 0 along the negative x-axis.
204 '''
205 try:
206 return _callBuiltin('log', x)
207 except TypeError:
208 pass
209 x = _enforceDimensioned(x)
210 try:
211 re, im = x.real, x.imag
212 except AttributeError:
213 y = _extendedComponentArithmetic(x, 'Log')
214 else:
215 y = ComplexDataSet(log(abs(x), base), phase(x), name='log( %s )' % x.name)
216 if not base is None:
217 y /= _math.log(float(base))
218 y.name = 'log( %s, base=%s )' % (x.name, base)
219 return y
220
223 '''
224 Return base-10 logarithm of x.
225 x can either be a real or a complex dataset.
226 If x is complex, the cut will extend from 0 along the negative x-axis.
227 '''
228 try:
229 return _callBuiltin('log10', x)
230 except TypeError:
231 pass
232 x = _enforceDimensioned(x)
233 try:
234 re, im = x.real, x.imag
235 except AttributeError:
236 return _extendedComponentArithmetic(x, 'Log10')
237 else:
238 arg = _named(phase(x) / _math.log(10), 'Phase( %s ) / Log(10)' % x.name)
239 return ComplexDataSet(log10(abs(x)), arg, name='log10( %s )' % x.name)
240
243 '''
244 Return square root of x.
245 x must be a float or real dataset.
246 '''
247 try:
248 return _callBuiltin('sqrt', x)
249 except TypeError:
250 return _extendedComponentArithmetic(_enforceDimensioned(x), 'Sqrt')
251
254 '''
255 Return phase of x: atan2(x.imag, x.real)
256 x must be a complex number or a complex dataset.
257 The result will be between -pi and pi
258 '''
259 try:
260 return _math.atan2(x.imag, x.real)
261 except TypeError:
262 x = _enforceDimensioned(x)
263 return _named(atan2(_imag(x), _real(x)), 'Phase( %s )' % x.name)
264
267 '''
268 Return Euclidean norm: sqrt(x ** 2 + y ** 2)
269 x and y must be real datasets.
270 '''
271 if _isComplex(x) or _isComplex(y):
272 raise TypeError, "x and y must be real numbers or real datasets"
273 try:
274 return _math.hypot(x, y)
275 except TypeError:
276 x = _enforceDimensioned(x)
277 y = _enforceDimensioned(y)
278 return sqrt(x * x + y * y)
279
280
281 -def fft(x, size=None, dimension=None):
303 re, im = map(make, ("Real", "Imaginary"))
304 return ComplexDataSet(re, im, name="Fft( %s )" % x.name)
305
308 if isinstance(x, DataSetMatrix):
309 return DataSetMatrix({key: interpolate(ds, dimensions) for key, ds in x.items()}, name=x.name)
310 assert _isDataSet(x), x
311 try:
312 re, im = x.real, x.imag
313 except AttributeError:
314 pass
315 else:
316 return ComplexDataSet(interpolate(re, dimensions), interpolate(im, dimensions), x.name)
317 y = _datasource.InterpolatedDataSet(x.name, x.id + "'")
318 y.unitClass = x.unitClass
319 _addDimensions(y, dimensions)
320 y.checkCompatibility(x)
321 y.input("input").append(x)
322 return y
323
357
360 '''
361 A decorator to transforms a element-wise operation into a function on datasets.
362
363 Example:
364 @function
365 def myfunc(a, b):
366 if a < 0:
367 return 0
368 return b
369 A, B = getResult(...), getResult(...)
370 C = myfunc(A, b=B)
371 '''
372 def fun(*args, **kwargs):
373 return transform(operator, *args, **kwargs)
374 fun.__name__ = operator.__name__
375 fun.__doc__ = operator.__doc__
376 return fun
377
378
379 -def makeDataSet(sequence, id=None, name=None, dimensions=None, unitClass='SCALAR'):
380 '''
381 makeDataSet(sequence, id=None, name=None, dimensions=None, unitClass='SCALAR') -> DataSet
382 - sequence: a Python sequence (list, tuple, ...) of floats or complex values.
383 - id: optional, if not given a hashvalue is used as id.
384 - name: optional, if not given, the id is used.
385 - dimensions: a list of datasets to be used a dimensions
386 - unitClass: a string.
387 '''
388 from empro.datasource import ValueListDataSet
389 ys = tuple(sequence)
390 id = id or ('#%s' % hash(ys))
391 name = name or id
392 if any(_isComplex(y) for y in ys):
393 re = makeDataSet(map(_real, ys), id='Re( %s )' % id, name='Re( %s )' % name, dimensions=dimensions)
394 im = makeDataSet(map(_imag, ys), id='Im( %s )' % id, name='Im( %s )' % name, dimensions=dimensions)
395 return ComplexDataSet(re, im, name)
396 ds = ValueListDataSet(id, ys, name or id)
397 ds.unitClass = unitClass
398 _addDimensions(ds, dimensions or [])
399 return ds
400
401
402 -def makeRange(start, stop=None, step=1., id=None, name=None, unitClass='SCALAR'):
403 '''
404 makeRange([start, ] stop [, step]) -> DataSet
405
406 Similar to Python's built-in range() function, but creates a dataset.
407 In contrary to range(), here start, stop and step can be floats.
408 '''
409 if stop is None:
410 stop = start
411 start = 0
412 assert stop > start and step > 0
413 n = int(_math.ceil(float(stop - start) / float(step)))
414 Xs = [start + k * step for k in xrange(n)]
415 return makeDataSet(Xs, id=id, name=name, unitClass=unitClass)
416
419 '''
420 reduceDimensionsToIndex(x, a=1, b=2, ...) -> y
421
422 reduce the number of dimensions by fixing some to a single value.
423 - x is a DataSet or ComplexDataSet
424 - a, b, ... are the names of the dimensions you want to fixate, like Time, Frequency, Theta, ...
425 - 1, 2, ... are the indices within that dimension to fixate it to.
426 '''
427 assert _isDataSet(x), x
428 try:
429 re, im = x.real, x.imag
430 except AttributeError:
431 pass
432 else:
433 return ComplexDataSet(reduceDimensionsToIndex(re, **fixedDimensions), reduceDimensionsToIndex(im, **fixedDimensions), x.name)
434 y = _datasource.DimensionReducingDataSet(x.id, x.name)
435 y.unitClass = x.unitClass
436 y.input("input").append(x)
437 dimKs = {dim.name: k for k, dim in enumerate(x.dimensions())}
438 for name, index in fixedDimensions.items():
439 y.reduceDimensionToIndex(dimKs[name],index)
440 return y
441
442
443
444 -def getResult(context=None, **kwargs):
445 '''
446 getResult(context, sim=None, run=None, object=None, result=None,
447 timeDependence=None, fieldScatter=None, component=None, transform=None,
448 complexPart=None, interpolation=None)) -> dataset
449
450 Lookup and return a dataset from the tree of simulation results.
451
452 A dataset is found by narrowing down a query, starting from the context:
453 - If context is a string, it shall be a projectId: the project path.
454 - If context is empro.activeProject, its rootDir shall be the projectId.
455 - If context is a L{Simulation}, its path shall be used to preset the
456 projectId and simulationId.
457 - If context is a L{ResultQuery}, its fields shall be used as starting
458 point of the search, until the first override or ambiguity.
459 - If context is an existing dataset that has a query attribute, the
460 query shall be used as context.
461
462 The other arguments can be used to refine the query. If the context has
463 already set the same field (if context is a L{ResultQuery}), your explicit
464 arguments take precedence:
465 - sim -> query.simulationId: an integer or a string like '000001'
466 - run -> query.runId: an integer or a string like 'Run0001'
467 - object -> query.outputObjectId: name as string like 'Port1' or full id
468 as pair of strings like ('CircuitComponent', 'Port1')
469 - timeDependence -> query.timeDepencence: 'NoTimeDependence, 'Transient',
470 'SteadyState' or 'Broadband', ...
471 - result -> query.resultType: 'V', 'I', ...
472 - fieldScatter -> query.fieldScatter: string
473 - component -> query.resultComponent: 'Scalar', 'VectorMagnitude', ...
474 - transform -> query.dataTransform: 'NoTransform', 'Fft', 'Dft'
475 - complexPart -> query.complexPart: 'Complex', 'Real', 'Imag', ...
476 - interplation -> query.surfaceInterpolationResolution: string
477
478 If a field is not specified (neither in context, neither as additional
479 argument), and no good default value can be found, an AmbiguityError will
480 be raised.
481
482 Following keyword arguments are acccepted as additional options:
483 - name: the name of the returned dataset
484 - fftSize: an integer to specify the number of samples in the FFT
485 transformation. Only valid when transform='Fft' is used.
486 - indexRanges: a dictionary of dimension name and range pairs.
487 '''
488 from empro.output import ResultDataSet
489 query, followQuery = _startQuery(context)
490
491 followQuery = _setSim(query, followQuery, **kwargs)
492 followQuery = _setRun(query, followQuery, **kwargs)
493 followQuery = _setObject(query, followQuery, **kwargs)
494
495 availableTimeDependences = filter(lambda x: x != 'NoTimeDependence', query.availableTimeDependenceValues())
496 followQuery = _setKey(query, followQuery, "timeDependence", "timeDependence", \
497 availableTimeDependences, defaults=['Transient', 'Broadband'], **kwargs)
498
499 followQuery = _setKey(query, followQuery, "result", "resultType", query.availableResultTypeValues(), **kwargs)
500 followQuery = _setKey(query, followQuery, "fieldScatter", "fieldScatter", query.availableFieldScatterValues(), defaults=['TotalField'], **kwargs)
501 followQuery = _setKey(query, followQuery, "component", "resultComponent", query.availableResultComponentValues(), defaults=['Scalar', 'VectorMagnitude', 'Total'], **kwargs)
502 followQuery = _setKey(query, followQuery, "transform", "dataTransform", query.availableDataTransformValues(), defaults=['NoTransform', 'Fft'], **kwargs)
503
504 complexParts = list(query.availableComplexPartValues())
505 if 'RealPart' in complexParts and 'ImaginaryPart' in complexParts:
506 complexParts.append('Complex')
507 key, followQuery = _selectKey(query, followQuery, "complexPart", 'complexPart', complexParts, defaults=['NotComplex', 'Complex'], **kwargs)
508 if followQuery == _OVERRIDE_QUERY:
509 if key == 'Complex':
510 name = kwargs.get('name') or ''
511 kwargs['name'] = ''
512 kwargs['complexPart'] = 'RealPart'
513 real = getResult(context, **kwargs)
514 kwargs['complexPart'] = 'ImaginaryPart'
515 imag = getResult(context, **kwargs)
516 return ComplexDataSet(real, imag, name)
517 query.complexPart = key
518
519 _setKey(query, followQuery, "interpolation", "surfaceInterpolationResolution", query.availableSurfaceInterpolationResolutionValues(), defaults=['NoInterpolation'], **kwargs)
520
521 query.fftSize = kwargs.get('fftSize') or 0
522 indexRanges = kwargs.get('indexRanges', {})
523 for dim in query.availableDimensionIds():
524 query.setDimensionRange(dim, indexRanges.get(dim, (0, -1)))
525 return ResultDataSet(unicode(query), query, kwargs.get('name') or '')
526
529 '''
530 Exception raised by getResult if better specification of the desired dataset is required.
531 '''
532 - def __init__(self, attribute, candidates, msg=None):
533 self.attribute = attribute
534 self.candidates = candidates
535 self.__msg = msg
536
538 return unicode(self.__msg or "Ambiguous default choice for %r, specify one of %r" % (self.attribute, self.candidates))
539
542 '''
543 Wrapper around two DataSets to emulate a single dataset of complex numbers.
544
545 ComplexDataSet has two attributes real and imag which are instances of
546 L{empro.datasource.DataSet}. They should be of the same dataType and
547 share the same dimensions. ComplexDataSet emulates much of DataSet's
548 interface, but for complex numbers instead of floats.
549 '''
550 - def __init__(self, real, imag, name=None):
551 '''
552 ComplexDataSet(real, imag [, name=None])
553
554 construct a complex dataset from two real ones sharing the same
555 dataType and dimensions. If name is None, one will be derived
556 from real and imag.
557 '''
558 _assertCompatibleDatasets(real, imag)
559 def assertComplexPart(dataset, complexPart):
560 try:
561 q = dataset.query
562 except AttributeError:
563 return
564 assert q.complexPart == complexPart, "%r != %r" % (q.complexPart, complexPart)
565 assertComplexPart(real, 'RealPart')
566 assertComplexPart(imag, 'ImaginaryPart')
567 self._real, self._imag = real, imag
568 self.name = name
569
570 @property
573
574 @property
577
578 @property
580 try:
581 return self._real.query
582 except AttributeError:
583 return self._imag.query
584
586 if self._name:
587 return self._name
588 import re
589 r = re.match(r'^Re\((.*)\)$', self.real.name, re.I)
590 i = re.match(r'^Im\((.*)\)$', self.imag.name, re.I)
591 if r and i and r.group(1) == i.group(1):
592 return r.group(1).strip()
593 return '%s + j * %s' % (self.real.name, self.imag.name)
599 name = property(_getName, _setName)
600
601 - def flat(self, index):
603
604 - def at(self, *args):
605 return complex(self.real.at(*args), self.imag.at(*args))
606
609
612
613 @property
616
619
621 reMin, reMax = self.real.bounds()
622 imMin, imMax = self.imag.bounds()
623 return complex(reMin, imMin), complex(reMax, imMax)
624
627
630
633
635 return len(self.real)
636
639
641 return self.conjugate() / self.__absSquare()
642
645
648
651
654
660
662 return self._componentWiseOperator(other, _operator.__add__, '+')
663
665 return self._componentWiseOperator(other, _operator.__sub__, '-')
666
668 return self._complexComponentArithmetic(other, _operator.__mul__, '*', 'Multiply')
669
671 return self._complexComponentArithmetic(other, _operator.__truediv__, '/', 'Divide')
672
675
678
681
684
686 return _named(self.reciprocal() * other, "%s / %s" % (_name(other), self.name))
687
690
693
695 return not (self == other)
696
698 name = self._resultName(other, opName)
699
700 try:
701 return ComplexDataSet(op(self.real, other.real), op(self.imag, other.imag), name)
702 except AttributeError:
703 pass
704
705 try:
706 q = other.query
707 except AttributeError:
708 pass
709 else:
710 if q.complexPart == 'RealPart' or q.complexPart == 'NoComplex':
711 return ComplexDataSet(op(self.real, other), self.imag, name)
712 if q.complexPart == 'ImaginaryPart':
713 return ComplexDataSet(self.real, op(self.imag, other), name)
714 raise TypeError, "other has bad complexPart: %r" % q.complexPart
715
716 return ComplexDataSet(op(self.real, other), self.imag, name)
717
719 name = self._resultName(other, opName)
720
721 try:
722 return ComplexDataSet(op(self.real, other), op(self.imag, other), name)
723 except TypeError:
724 pass
725
726 if isinstance(other, complex):
727 if op == _operator.__truediv__:
728 other = 1 / other
729 else:
730 assert op == _operator.__mul__
731 return ComplexDataSet(self.real * other.real - self.imag * other.imag, self.real * other.imag + self.imag * other.real, name=name)
732
733 from empro.datasource import _complexComponentArithmetic
734 reA, imA, reB, imB = self.real, self.imag, other.real, other.imag
735 re, im = [_complexComponentArithmetic(reA, imA, reB, imB, opTag, part) for part in ("Real", "Imaginary")]
736 return ComplexDataSet(re, im, name)
737
740
742 return "%s %s %s" % (self.name, opName, _name(other))
743
747 '''
748 Groups a number of DataSets as a matrix
749 '''
750 - def __init__(self, datasets, name=None):
751 _assertCompatibleDatasets(*datasets.values())
752 rows, cols = _calculateMatrixShape(datasets)
753 self._rows, self._cols = rows, cols
754 self._datasets = datasets
755 self._name = name
756 self._zero = _constant(0, self._datasets.values()[0])
757
758 @property
761
762 @property
765
766 @property
769
771 return self._datasets.keys()
772
775
778
780 return self.isSquare() and all(row == col for row, col in self)
781
783 '''
784 self[row, col] -> DataSet
785
786 In case self is a column matrix, you can index it by just the row index: self[row] -> DataSet
787 '''
788 return self._datasets.get(self.__normalizeIndex(index), self._zero)
789
791 '''
792 self[row, col] = dataset
793
794 In case self is a column matrix, you can index it by just the row index: self[row] = dataset
795 '''
796 _assertCompatibleDatasets(self[index], dataset)
797 self._datasets[self.__normalizeIndex(index)] = dataset
798
800 try:
801 row, col = index
802 except (TypeError, ValueError):
803 assert len(self.cols) == 1, "You can only access items by single subscript in case of a column matrix"
804 row, col = index, self.cols[0]
805 if not row in self.rows and col in self.cols:
806 raise KeyError((row, col))
807 return row, col
808
810 return index in self._datasets
811
814
817
819 return len(self._datasets)
820
823
825 return self.__unaryOperation(_operator.__neg__, name="-%s" % self.name)
826
828 return self.__unaryOperation(_operator.__abs__, name="Abs(%s)" % self.name)
829
832
834 return self._componentWiseOperator(other, _operator.__add__, '+')
835
837 return self._componentWiseOperator(other, _operator.__sub__, '-')
838
841
844
853
855 if not isinstance(other, DataSetMatrix):
856 return DataSetMatrix({index: dataset * other for index, dataset in self.items()}, name="%s * %s" % (self.name, other))
857 assert self.cols == other.rows
858 if self.isDiagonal():
859 if other.isDiagonal():
860 product = {(k, k): self[k, k] * other[k, k] for k in self.rows}
861 else:
862 product = {(row, col): self[row, row] * other[row, col] for row in self.rows for col in other.cols}
863 else:
864 if other.isDiagonal():
865 product = {(row, col): self[row, col] * other[col, col] for row in self.rows for col in other.cols}
866 else:
867 dot = lambda a, b, row, col: sum(a[row, k] * b[k, col] for k in a.cols)
868 product = {(row, col): dot(self, other, row, col) for row in self.rows for col in other.cols}
869 return DataSetMatrix(product, name="%s * %s" % (self.name, other.name))
870
872 assert not isinstance(other, DataSetMatrix)
873 return self * other
874
876 '''
877 returns the transposed matrix
878 '''
879 items = dict([((col, row), value) for (row, col), value in self.items()])
880 return DataSetMatrix(items, "Trans( %s )" % self.name)
881
883 '''
884 returns the inversed matrix
885 '''
886 assert self.isSquare(), "Can only invert square matrices"
887 name, id = "Inv( %s )" % self.name, "Inv(%s)" % self.name
888 if self.isDiagonal():
889 return DataSetMatrix({index: dataset.reciprocal() for index, dataset in self.items()}, name)
890 indices = list(enumerate(self.rows))
891 if any(map(_isComplex, self._datasets.values())):
892 invOperator = _datasource.InverseComplexMatrixDataSet(name, len(self.rows), id)
893 _addDimensions(invOperator, self._datasets.values()[0].dimensions())
894 for rowIndex, rowKey in indices:
895 for colIndex, colKey in indices:
896 item = self[rowKey, colKey]
897 assert _isDataSet(item), item
898 try:
899 re, im = item.real, item.imag
900 except AttributeError:
901 re = item
902 im = _constant(0, item)
903 invOperator.input("Row-%s-Real" % rowIndex).append(re)
904 invOperator.input("Row-%s-Imag" % rowIndex).append(im)
905 invItems = {}
906 for rowIndex, rowKey in indices:
907 for colIndex, colKey in indices:
908 item_name = '%(name)s[%(rowKey)s, %(colKey)s]' % vars()
909 def complex_part(k, op):
910 part_name = '%s( %s )' % (op, item_name)
911 part = _datasource.DimensionReducingDataSet(part_name, part_name)
912 part.input("input").append(invOperator)
913 part.reduceDimensionToIndex(0, rowIndex)
914 part.reduceDimensionToIndex(1, colIndex)
915 part.reduceDimensionToIndex(2, k)
916 return part
917 re = complex_part(0, 'Re')
918 im = complex_part(1, 'Im')
919 invItems[rowKey, colKey] = ComplexDataSet(re, im, item_name)
920 else:
921 invOperator = _datasource.InverseRealMatrixDataSet(name, len(self.rows), id)
922 _addDimensions(invOperator, self._datasets.values()[0].dimensions())
923 for rowIndex, rowKey in indices:
924 for colIndex, colKey in indices:
925 invOperator.input("Row-%s" % rowIndex).append(self[rowKey, colKey])
926 invItems = {}
927 for rowIndex, rowKey in indices:
928 for colIndex, colKey in indices:
929 item_name = '%(name)s[%(rowKey)s, %(colKey)s]' % vars()
930 item = _datasource.DimensionReducingDataSet(item_name, item_name)
931 item.input("input").append(invOperator)
932 item.reduceDimensionToIndex(0, rowIndex)
933 item.reduceDimensionToIndex(1, colIndex)
934 invItems[rowKey, colKey] = item
935 return DataSetMatrix(invItems, name)
936
937 @_deprecation.deprecated(
938 390, "Use obj.inversed() for better naming consistency")
940 '''
941 deprecated, use inversed() for greater naming consistency.
942 '''
943 return self.inversed()
944
946 return "%s %s %s" % (self.name, opName, _name(other))
947
948
949
950
951
952
953 _FOLLOW_QUERY, _OVERRIDE_QUERY = range(2)
996
997
998 -def _setSim(query, followQuery, **kwargs):
999 if isinstance(kwargs.get('sim'), int):
1000 kwargs['sim'] = '%06d' % kwargs['sim']
1001 return _setKey(query, followQuery, "sim", "simulationId", query.availableSimulationIds(), **kwargs)
1002
1003
1004 -def _setRun(query, followQuery, **kwargs):
1005 if isinstance(kwargs.get('run'), int):
1006 kwargs['run'] = 'Run%04d' % kwargs['run']
1007 return _setKey(query, followQuery, "run", "runId", query.availableRunIds(), **kwargs)
1008
1009
1010 -def _setObject(query, followQuery, **kwargs):
1011 if isinstance(kwargs.get('object'), basestring):
1012 objName = kwargs['object']
1013 candidates = [objId for objId in query.availableOutputObjectIds() if objId[1] == objName]
1014 if len(candidates) == 0:
1015 goodNames = list(set(zip(*query.availableOutputObjectIds())[1]))
1016 raise KeyError, "No output object found with name %(objName)r. Try one of %(goodNames)r" % vars()
1017 if len(candidates) > 1:
1018 raise AmbiguityError("object", candidates, \
1019 "There's more than one output object with name %(objName)r. Try one of the following full IDs: %(candidates)r" % vars())
1020 kwargs['object'] = candidates[0]
1021 return _setKey(query, followQuery, "object", "outputObjectId", query.availableOutputObjectIds(), **kwargs)
1022
1023
1024 -def _setKey(query, followQuery, argument, attr, availableKeys, defaults=None, **kwargs):
1025 key, followQuery = _selectKey(query, followQuery, argument, attr, availableKeys, defaults, **kwargs)
1026 if followQuery == _OVERRIDE_QUERY:
1027 query.__setattr__(attr, key)
1028 assert query.__getattribute__(attr) == key
1029 return followQuery
1030
1031
1032 -def _selectKey(query, followQuery, argument, attr, availableKeys, defaults=None, **kwargs):
1033 '''
1034 the defaults should be in order: the first available default will be selected.\
1035 '''
1036 key = kwargs.get(argument)
1037 if key:
1038 if not key in availableKeys:
1039 raise KeyError, "%(key)r is not a valid value for %(argument)r. Use one of %(availableKeys)r" % vars()
1040 return key, _OVERRIDE_QUERY
1041 elif followQuery == _OVERRIDE_QUERY or not query.__getattribute__(attr) in availableKeys:
1042 assert len(availableKeys) > 0, query
1043 if len(availableKeys) > 1 and defaults:
1044 Xs = filter(lambda x: x in availableKeys, defaults)[:1]
1045 else:
1046 Xs = availableKeys
1047 if len(Xs) != 1:
1048 raise AmbiguityError(argument, availableKeys)
1049 return Xs[0], _OVERRIDE_QUERY
1050 return _OVERRIDE_QUERY
1051 return query.__getattribute__(attr), _FOLLOW_QUERY
1052
1055 try:
1056 return x.name
1057 except AttributeError:
1058 return unicode(x)
1059
1062 try:
1063 x.name = name
1064 except AttributeError:
1065 pass
1066 return x
1067
1070 return _buildId(template, *args), _buildName(template, *args)
1071
1074 return _buildTag(template, 'id', *args).replace(' ', '')
1075
1078 return _buildTag(template, 'name', *args)
1079
1082 args = tuple([getattr(x, attr, x) for x in args])
1083 return template % args
1084
1096
1105
1108
1109
1110 import operator
1111 assert operator.isNumberType(x), "%r is not a number." % x
1112 y = other * 0
1113 if x != 0:
1114 y += x
1115 return _named(y, name or unicode(x))
1116
1119 if isinstance(x, _numbers.Real):
1120 return False
1121 try:
1122 re, im = x.real, x.imag
1123 except AttributeError:
1124 return False
1125 return True
1126
1130
1133 try:
1134 return x.real
1135 except AttributeError:
1136 return x
1137
1140 try:
1141 return x.imag
1142 except AttributeError:
1143 return 0
1144
1147 try:
1148 return x.conjugate()
1149 except AttributeError:
1150 return x
1151
1154 try:
1155 return x.inversed()
1156 except AttributeError:
1157 pass
1158 try:
1159 return x.reciprocal()
1160 except AttributeError:
1161 return 1. / x
1162
1165 '''
1166 check that all datasets have the same datatypes and dimensions
1167 '''
1168 assert all(_isDataSet(ds) for ds in datasets), "Only (complex) datasets are accepted: %r" % list(map(type, datasets))
1169 if len(datasets) < 2:
1170 return
1171 reference = datasets[0]
1172 for other in datasets[1:]:
1173 assert other.dataType() == reference.dataType(), "All datasets must be of the same data type."
1174 assert len(other) == len(reference), "All datasets must be of the same length."
1175 assert other.numberOfDimensions() == reference.numberOfDimensions(), "All datasets must have the same number of dimensions."
1176 assert all(a == b for a, b in zip(other.dimensions(), reference.dimensions())), "All datasets must have compatible dimensions."
1177
1180 def sortedUnique(Xs):
1181 return tuple(sorted(set(Xs)))
1182 return map(sortedUnique, zip(*datasets.keys()))
1183
1186 return [(row, col) for row in rows for col in cols]
1187
1190 try:
1191 return getattr(_math, fun)(*args)
1192 except TypeError:
1193 return getattr(_cmath, fun)(*args)
1194
1195
1196 -def _apply(x, fun, name=None):
1197 try:
1198 return DataSetMatrix({k: fun(v) for k, v in x.items()}, name)
1199 except AttributeError:
1200 return fun(x)
1201
1204 from empro.toolkit import dataset
1205 try:
1206 assert other.isSquare(), "matrix must be square"
1207 except AttributeError:
1208 pass
1209 else:
1210 one = dataset._constant(1, other.values()[0])
1211 return dataset.DataSetMatrix({(k, k): one for k in other.rows}, "1")
1212 try:
1213 return dataset._constant(1, other)
1214 except AttributeError:
1215 return 1
1216