1 /***************************************************************************
2 matrix.cpp: 2D matrix type for kst
3 -------------------
4 begin : July 2004
5 ***************************************************************************/
6
7 /***************************************************************************
8 * *
9 * copyright : (C) 2007 The University of Toronto *
10 netterfield@astro.utoronto.ca
11 * copyright : (C) 2004 University of British Columbia *
12 * dscott@phas.ubc.ca *
13 * *
14 * This program is free software; you can redistribute it and/or modify *
15 * it under the terms of the GNU General Public License as published by *
16 * the Free Software Foundation; either version 2 of the License, or *
17 * (at your option) any later version. *
18 * *
19 ***************************************************************************/
20
21 #include "matrix.h"
22
23 #include <math.h>
24 #include <QDebug>
25 #include <QXmlStreamWriter>
26 #include <QList>
27
28 #include "debug.h"
29 #include "math_kst.h"
30 #include "datacollection.h"
31 #include "objectstore.h"
32
33
34 // used for resizing; set to 1 for loop zeroing, 2 to use memset
35 #define ZERO_MEMORY 2
36
37 using namespace std;
38
39 namespace Kst {
40
41 const QString Matrix::staticTypeString = "Matrix";
42
Matrix(ObjectStore * store)43 Matrix::Matrix(ObjectStore *store)
44 : Primitive(store, 0L), _NS(0), _NRealS(0), _nX(1), _nY(0), _minX(0), _minY(0), _stepX(1), _stepY(1),
45 _invertXHint(false), _invertYHint(false), _editable(false), _saveable(false), _z(0L), _zSize(0) {
46
47 _initializeShortName();
48
49 _scalars.clear();
50 _strings.clear();
51 _vectors.clear();
52
53 setFlag(true);
54
55 createScalars(store);
56
57 }
58
59
~Matrix()60 Matrix::~Matrix() {
61 if (_z) {
62 _vectors["z"]->setV(0L, 0);
63 free(_z);
64 _z = 0L;
65 }
66 }
67
_initializeShortName()68 void Matrix::_initializeShortName() {
69 _shortName = 'M'+QString::number(_matrixnum);
70 if (_matrixnum>max_matrixnum)
71 max_matrixnum = _matrixnum;
72 _matrixnum++;
73 }
74
75
deleteDependents()76 void Matrix::deleteDependents() {
77 for (QHash<QString, ScalarPtr>::Iterator it = _scalars.begin(); it != _scalars.end(); ++it) {
78 _store->removeObject(it.value());
79 }
80 for (QHash<QString, VectorPtr>::Iterator it = _vectors.begin(); it != _vectors.end(); ++it) {
81 _store->removeObject(it.value());
82 }
83 Object::deleteDependents();
84 }
85
86
typeString() const87 const QString& Matrix::typeString() const {
88 return staticTypeString;
89 }
90
91
sampleCount() const92 int Matrix::sampleCount() const {
93 return _nX*_nY;
94 }
95
96
value(double x,double y,bool * ok) const97 double Matrix::value(double x, double y, bool* ok) const {
98 int x_index = (int)((x - _minX) / (double)_stepX);
99 int y_index = (int)((y - _minY) / (double)_stepY);
100
101 int index = zIndex(x_index, y_index);
102 if ((index < 0) || !isfinite(_z[index]) || KST_ISNAN(_z[index])) {
103 if (ok) (*ok) = false;
104 return 0.0;
105 }
106 if (ok) (*ok) = true;
107 return _z[index];
108
109 }
110
value(double x,double y,QPointF & matchedPoint,bool * ok) const111 double Matrix::value(double x, double y, QPointF &matchedPoint, bool* ok) const {
112 int x_index = (int)((x - _minX) / (double)_stepX);
113 int y_index = (int)((y - _minY) / (double)_stepY);
114
115 matchedPoint.setX((x_index+0.5)*_stepX+_minX);
116 matchedPoint.setY((y_index+0.5)*_stepY+_minY);
117
118 int index = zIndex(x_index, y_index);
119 if ((index < 0) || !isfinite(_z[index]) || KST_ISNAN(_z[index])) {
120 if (ok) (*ok) = false;
121 return 0.0;
122 }
123 if (ok) (*ok) = true;
124 return _z[index];
125
126 }
127
128
valueRaw(int x,int y,bool * ok) const129 double Matrix::valueRaw(int x, int y, bool* ok) const {
130 int index = zIndex(x,y);
131 if ((index < 0) || !isfinite(_z[index]) || KST_ISNAN(_z[index])) {
132 if (ok) {
133 (*ok) = false;
134 }
135 return 0.0;
136 }
137 if (ok) {
138 (*ok) = true;
139 }
140 return _z[index];
141 }
142
143
zIndex(int x,int y) const144 int Matrix::zIndex(int x, int y) const {
145 if (x >= _nX || x < 0 || y >= _nY || y < 0) {
146 return -1;
147 }
148 int index = x * _nY + y;
149 if (index >= _zSize || index < 0 ) {
150 return -1;
151 }
152 return index;
153 }
154
155
setValue(double x,double y,double z)156 bool Matrix::setValue(double x, double y, double z) {
157 int x_index = (int)floor((x - _minX) / (double)_stepX);
158 int y_index = (int)floor((y - _minY) / (double)_stepY);
159 return setValueRaw(x_index, y_index, z);
160 }
161
162
setValueRaw(int x,int y,double z)163 bool Matrix::setValueRaw(int x, int y, double z) {
164 int index = zIndex(x,y);
165 if (index < 0) {
166 return false;
167 }
168 _z[index] = z;
169 return true;
170 }
171
minValue() const172 double Matrix::minValue() const {
173 return _scalars["min"]->value();
174 }
175
176
maxValue() const177 double Matrix::maxValue() const {
178 return _scalars["max"]->value();
179 }
180
minValueNoSpike() const181 double Matrix::minValueNoSpike() const {
182 // FIXME: it is expensive to calcNoSpikeRange
183 // so we have chosen here to only call it expicitly
184 // and no attempt is made to check if it is still up to date...
185 // It would be better to have these calls call
186 // calcNoSpikeRange iff the values were obsolete.
187 return _minNoSpike;
188 }
189
maxValueNoSpike() const190 double Matrix::maxValueNoSpike() const {
191 // FIXME: it is expensive to calcNoSpikeRange
192 // so we have chosen here to only call it expicitly
193 // and no attempt is made to check if it is still up to date...
194 // It would be better to have these calls call
195 // calcNoSpikeRange iff the values were obsolete.
196
197 return _maxNoSpike;
198 }
199
calcNoSpikeRange(double per)200 void Matrix::calcNoSpikeRange(double per) {
201 int n_check; // the number of (randomly selected) samples to check.
202 int n_checked;
203 double x=0;
204 int n_notnan;
205 QList<double> pixels;
206 double max = -1E300;
207 double min = 1E300;
208
209 int i;
210 unsigned long j;
211
212 if (per>0.5) per = 0.49;
213
214 // count number of points which aren't nans.
215 for (i=n_notnan=0; i<_NS; ++i) {
216 if (!KST_ISNAN(_z[i])) {
217 n_notnan++;
218 } else {
219 max = qMax(max, _z[i]);
220 min = qMin(min, _z[i]);
221 }
222 }
223
224 if (n_notnan==0) {
225 _minNoSpike = 0;
226 _maxNoSpike = 0;
227
228 return;
229 } else if (per<=0) {
230 _minNoSpike = min;
231 _maxNoSpike = max;
232 return;
233
234 }
235
236 n_check = 100000;
237
238 n_checked = 0;
239
240 pixels.clear();
241
242 while (n_checked < n_check) {
243 j = size_t(double(rand())*double(_NS-1)/(double(RAND_MAX)));
244 x = _z[j];
245 if (!KST_ISNAN(x)) {
246 pixels.append(x);
247 n_checked++;
248 }
249 }
250 qSort(pixels);
251
252 // FIXME: this needs a z spike insensitive algorithm...
253 _minNoSpike = pixels[size_t(n_check*per)];
254 _maxNoSpike = pixels[size_t(n_check*(1.0-per)-1)];
255
256 }
257
meanValue() const258 double Matrix::meanValue() const {
259 return _scalars["mean"]->value();
260 }
261
minValuePositive() const262 double Matrix::minValuePositive() const {
263 return _scalars["minpos"]->value();
264 }
265
numNew() const266 int Matrix::numNew() const {
267 return _numNew;
268 }
269
270
resetNumNew()271 void Matrix::resetNumNew() {
272 _numNew = 0;
273 }
274
275
zero()276 void Matrix::zero() {
277 for (int i = 0; i < _zSize; ++i) {
278 _z[i] = 0.0;
279 }
280 updateScalars();
281 }
282
283
blank()284 void Matrix::blank() {
285 for (int i = 0; i < _zSize; ++i) {
286 _z[i] = NOPOINT;
287 }
288 updateScalars();
289 }
290
291
getUsage() const292 int Matrix::getUsage() const {
293 int scalarUsage = 0;
294 for (QHash<QString, ScalarPtr>::ConstIterator it = _scalars.begin(); it != _scalars.end(); ++it) {
295 scalarUsage += it.value()->getUsage() - 1;
296 }
297 return Object::getUsage() + scalarUsage;
298 }
299
300
internalUpdate()301 void Matrix::internalUpdate() {
302 // calculate stats
303 _NS = _nX * _nY;
304
305 if (_zSize > 0) {
306 double min = NAN;
307 double max = NAN;
308 double minpos = NAN;
309 double sum = 0.0, sumsquared = 0.0;
310 bool initialized = false;
311
312 _NRealS = 0;
313
314 for (int i = 0; i < _zSize; ++i) {
315 if (isfinite(_z[i]) && !KST_ISNAN(_z[i])) {
316 if (!initialized) {
317 min = _z[i];
318 max = _z[i];
319 minpos = (_z[0] > 0) ? _z[0] : 1.0E300;
320 initialized = true;
321 _NRealS++;
322 } else {
323 if (min > _z[i]) {
324 min = _z[i];
325 }
326 if (max < _z[i]) {
327 max = _z[i];
328 }
329 if (minpos > _z[i] && _z[i] > 0) {
330 minpos = _z[i];
331 }
332 sum += _z[i];
333 sumsquared += _z[i] * _z[i];
334
335 _NRealS++;
336 }
337 }
338 }
339 _scalars["sum"]->setValue(sum);
340 _scalars["sumsquared"]->setValue(sumsquared);
341 _scalars["max"]->setValue(max);
342 _scalars["min"]->setValue(min);
343 _scalars["minpos"]->setValue(minpos);
344
345 updateScalars();
346 }
347 }
348
setXLabelInfo(const LabelInfo & label_info)349 void Matrix::setXLabelInfo(const LabelInfo &label_info) {
350 _xLabelInfo = label_info;
351 }
352
setYLabelInfo(const LabelInfo & label_info)353 void Matrix::setYLabelInfo(const LabelInfo &label_info) {
354 _yLabelInfo = label_info;
355 }
356
setTitleInfo(const LabelInfo & label_info)357 void Matrix::setTitleInfo(const LabelInfo &label_info) {
358 _titleInfo = label_info;
359 }
360
xLabelInfo() const361 LabelInfo Matrix::xLabelInfo() const {
362 return _xLabelInfo;
363 }
364
yLabelInfo() const365 LabelInfo Matrix::yLabelInfo() const {
366 return _yLabelInfo;
367 }
368
369
titleInfo() const370 LabelInfo Matrix::titleInfo() const {
371 return _titleInfo;
372 }
373
editable() const374 bool Matrix::editable() const {
375 return _editable;
376 }
377
378
setEditable(bool editable)379 void Matrix::setEditable(bool editable) {
380 _editable = editable;
381 }
382
383
createScalars(ObjectStore * store)384 void Matrix::createScalars(ObjectStore *store) {
385 Q_ASSERT(store);
386 ScalarPtr sp;
387 VectorPtr vp;
388
389 _scalars.insert("max", sp=store->createObject<Scalar>());
390 sp->setProvider(this);
391 sp->setSlaveName("Max");
392
393 _scalars.insert("min", sp=store->createObject<Scalar>());
394 sp->setProvider(this);
395 sp->setSlaveName("Min");
396
397 _scalars.insert("mean", sp=store->createObject<Scalar>());
398 sp->setProvider(this);
399 sp->setSlaveName("Mean");
400
401 _scalars.insert("sigma", sp=store->createObject<Scalar>());
402 sp->setProvider(this);
403 sp->setSlaveName("Sigma");
404
405 _scalars.insert("rms", sp=store->createObject<Scalar>());
406 sp->setProvider(this);
407 sp->setSlaveName("Rms");
408
409 _scalars.insert("ns", sp=store->createObject<Scalar>());
410 sp->setProvider(this);
411 sp->setSlaveName("NS");
412
413 _scalars.insert("sum", sp=store->createObject<Scalar>());
414 sp->setProvider(this);
415 sp->setSlaveName("Sum");
416
417 _scalars.insert("sumsquared", sp=store->createObject<Scalar>());
418 sp->setProvider(this);
419 sp->setSlaveName("SumSquared");
420
421 _scalars.insert("minpos", sp=store->createObject<Scalar>());
422 sp->setProvider(this);
423 sp->setSlaveName("MinPos");
424
425 _vectors.insert("z", vp = store->createObject<Vector>());
426 vp->setProvider(this);
427 vp->setSlaveName("Z");
428
429 }
430
431
updateScalars()432 void Matrix::updateScalars() {
433 _scalars["ns"]->setValue(_NS);
434 if (_NRealS >= 2) {
435 _scalars["mean"]->setValue(_scalars["sum"]->value()/double(_NRealS));
436 _scalars["sigma"]->setValue( sqrt(
437 (_scalars["sumsquared"]->value() - _scalars["sum"]->value()*_scalars["sum"]->value()/double(_NRealS))/ double(_NRealS-1) ) );
438 _scalars["rms"]->setValue(sqrt(_scalars["sumsquared"]->value()/double(_NRealS)));
439 } else {
440 _scalars["sigma"]->setValue(_scalars["max"]->value() - _scalars["min"]->value());
441 _scalars["rms"]->setValue(sqrt(_scalars["sumsquared"]->value()));
442 _scalars["mean"]->setValue(0);
443 }
444 }
445
446
resizeZ(int sz,bool reinit)447 bool Matrix::resizeZ(int sz, bool reinit) {
448 // qDebug() << "resizing to: " << sz << endl;
449 if (sz >= 1) {
450 if (!kstrealloc(_z, sz*sizeof(double))) {
451 qCritical() << "Matrix resize failed";
452 return false;
453 }
454 _vectors["z"]->setV(_z, sz);
455 #ifdef ZERO_MEMORY
456 if (reinit && _zSize < sz) {
457 #if ZERO_MEMORY == 2
458 memset(&_z[_zSize], 0, (sz - _zSize)*sizeof(double));
459
460 #else
461 for (int i = _zSize; i < sz; ++i) {
462 _z[i] = 0.0;
463 }
464 #endif
465 }
466 #else
467 // TODO: Is aborting all we can do?
468 fatalError("Not enough memory for matrix data");
469 return false;
470 #endif
471 _zSize = sz;
472 updateScalars();
473 }
474 return true;
475 }
476
477
478 #if 0
479 bool Matrix::resize(int xSize, int ySize, bool reinit) {
480 int oldNX = _nX;
481 int oldNY = _nY;
482 _nX = xSize;
483 _nY = ySize;
484 if (resizeZ(xSize*ySize, reinit)) {
485 return true;
486 } else {
487 _nX = oldNX;
488 _nY = oldNY;
489 return false;
490 }
491 }
492 #endif
493
494
495 // Resize the matrix to xSize x ySize, maintaining the values in the current
496 // positions. If reinit is set, new entries will be initialized to 0.
497 // Otherwise, they will not be set. The behavior in that case is undefined.
resize(int xSize,int ySize,bool reinit)498 bool Matrix::resize(int xSize, int ySize, bool reinit) {
499 if (xSize <= 0 || ySize <= 0) {
500 return false;
501 }
502
503 // NOTE: _zSize is assumed to correctly represent the state of _z, while _nX
504 // and _nY are the desired (but not necessarily actual) current size of the
505 // matrix
506 bool valid = (_zSize == _nX * _nY); // is the current matrix properly initialized?
507
508 int sz = xSize * ySize;
509 if (sz > _zSize) {
510 // array is getting bigger, so resize before moving
511 if (!kstrealloc(_z, sz*sizeof(double))) {
512 qCritical() << "Matrix resize failed";
513 return false;
514 }
515 _vectors["z"]->setV(_z, sz);
516 }
517
518 if (valid && ySize != _nY && _nY > 0) {
519 // move old values to new spots
520 int source = 0;
521 int target = 0;
522 for (int row=1; row < qMin(xSize, _nX); ++row) {
523 source += _nY;
524 target += ySize;
525 memmove(_z + target, _z + source, qMin(ySize, _nY)*sizeof(double));
526 if (reinit && ySize > _nY) {
527 // initialize memory in new column(s) of previous row vacated by memmove
528 memset(_z + source, 0, (ySize - _nY)*sizeof(double));
529 }
530 }
531 }
532
533 if (sz < _zSize) {
534 // array is getting smaller, so resize after moving
535 if (!kstrealloc(_z, sz*sizeof(double))) {
536 qCritical() << "Matrix resize failed";
537 return false;
538 }
539 _vectors["z"]->setV(_z, sz);
540 }
541
542 if (reinit && _zSize < sz) {
543 // initialize new memory after old values
544 for (int row=0; row < qMin(xSize, _nX); ++row) {
545 for (int col = qMin(ySize,_nY); col<ySize; ++col) {
546 _z[row*ySize+col] = 0;
547 }
548 }
549 for (int row = qMin(xSize, _nX); row < xSize; ++row) {
550 for (int col = 0; col < ySize; ++col) {
551 _z[row*ySize+col] = 0;
552 }
553 }
554 }
555
556 _nX = xSize;
557 _nY = ySize;
558 _NS = _nX * _nY;
559 _zSize = sz;
560
561 updateScalars();
562
563 return true;
564 }
565
566
save(QXmlStreamWriter & s)567 void Matrix::save(QXmlStreamWriter &s) {
568 Q_UNUSED(s)
569 // no saving
570 }
571
572
saveable() const573 bool Matrix::saveable() const {
574 return _saveable;
575 }
576
577
change(uint nX,uint nY,double minX,double minY,double stepX,double stepY)578 void Matrix::change(uint nX, uint nY, double minX, double minY, double stepX, double stepY) {
579 _nX = nX;
580 _nY = nY;
581 _stepX = stepX;
582 _stepY = stepY;
583 _minX = minX;
584 _minY = minY;
585 resizeZ(nX*nY, true);
586 }
587
588
change(QByteArray & data,uint nX,uint nY,double minX,double minY,double stepX,double stepY)589 void Matrix::change(QByteArray &data, uint nX, uint nY, double minX, double minY, double stepX, double stepY) {
590 _nX = nX;
591 _nY = nY;
592 _minX = minX;
593 _minY = minY;
594 _stepX = stepX;
595 _stepY = stepY;
596
597 _saveable = true;
598 resizeZ(nX*nY, true);
599
600 QDataStream qds(&data, QIODevice::ReadOnly);
601 uint i;
602 // fill in the raw array with the data
603 for (i = 0; i < nX*nY && !qds.atEnd(); ++i) {
604 qds >> _z[i]; // stored in the same order as it was saved
605 }
606 if (i < nX*nY) {
607 Debug::self()->log(tr("Saved matrix contains less data than it claims."), Debug::Warning);
608 resizeZ(i, false);
609 }
610 internalUpdate();
611 }
612
descriptionTip() const613 QString Matrix::descriptionTip() const {
614 return tr("Matrix: %1\n %2 x %3", "%1 is the matrix name. %2 and %3 are its dimensions.").arg(Name()).arg(_nX).arg(_nY);
615 }
616
sizeString() const617 QString Matrix::sizeString() const {
618 return QString("%1x%2").arg(_nX).arg(_nY);
619 }
620
outputPrimitives() const621 ObjectList<Primitive> Matrix::outputPrimitives() const {
622 PrimitiveList primitive_list;
623 int n;
624
625 n = _scalars.count();
626 for (int i = 0; i< n; ++i) {
627 primitive_list.append(kst_cast<Primitive>(_scalars.values().at(i)));
628 }
629
630 n = _strings.count();
631 for (int i = 0; i< n; ++i) {
632 primitive_list.append(kst_cast<Primitive>(_strings.values().at(i)));
633 }
634
635 n = _vectors.count();
636 for (int i = 0; i< n; ++i) {
637 VectorPtr V = _vectors.values().at(i);
638 primitive_list.append(kst_cast<Primitive>(V));
639 primitive_list.append(V->outputPrimitives());
640 }
641
642 return primitive_list;
643 }
644
metas() const645 PrimitiveMap Matrix::metas() const
646 {
647 PrimitiveMap meta;
648 for (QHash<QString, StringPtr>::ConstIterator it = _strings.begin(); it != _strings.end(); ++it) {
649 meta[it.key()] = it.value();
650 }
651 for (QHash<QString, ScalarPtr>::ConstIterator it = _scalars.begin(); it != _scalars.end(); ++it) {
652 meta[it.key()] = it.value();
653 }
654 for (QHash<QString, VectorPtr>::ConstIterator it = _vectors.begin(); it != _vectors.end(); ++it) {
655 meta[it.key()] = it.value();
656 }
657 return meta;
658 }
659
getBinaryArray() const660 QByteArray Matrix::getBinaryArray() const {
661 readLock();
662 QByteArray ret;
663 QDataStream ds(&ret, QIODevice::WriteOnly);
664 ds<<(qint32)_nX<<(qint32)_nY<<_minX<<_minY<<_stepX<<_stepY; //fixme: this makes it not compatible w/ change(...)
665
666 uint i;
667 uint n = _nX*_nY;
668 // fill in the raw array with the data
669 for (i = 0; i < n; ++i) {
670 ds << _z[i];
671 }
672 unlock();
673 return ret;
674 }
675
676 /* used for scripting IPC
677 accepts an open writable file.
678 returns false on failure */
saveToTmpFile(QFile & fp)679 bool Matrix::saveToTmpFile(QFile &fp) {
680 qint64 n_written;
681 qint64 n_write;
682
683 n_write = _nX*_nY*sizeof(double);
684
685 n_written = fp.write((char *)_z, n_write);
686
687 fp.flush();
688
689 return (n_write == n_written);
690 }
691
692
693 }
694 // vim: ts=2 sw=2 et
695