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