1 /* _______________________________________________________________________
2
3 Surfpack: A Software Library of Multidimensional Surface Fitting Methods
4 Copyright (c) 2006, Sandia National Laboratories.
5 This software is distributed under the GNU Lesser General Public License.
6 For more information, see the README file in the top Surfpack directory.
7 _______________________________________________________________________ */
8
9 #include "surfpack.h"
10 #include "SurfData.h"
11
12 using std::cerr;
13 using std::cout;
14 using std::endl;
15 using std::ifstream;
16 using std::istream;
17 using std::istringstream;
18 using std::ios;
19 using std::list;
20 using std::ofstream;
21 using std::ostream;
22 using std::ostream_iterator;
23 using std::ostringstream;
24 using std::range_error;
25 using std::set;
26 using std::setw;
27 using std::string;
28 using std::vector;
29
30
31 #ifdef SURFPACK_HAVE_BOOST_SERIALIZATION
BOOST_CLASS_EXPORT(SurfData)32 BOOST_CLASS_EXPORT(SurfData)
33 #endif
34
35
36 // ____________________________________________________________________________
37 // Creation, Destruction, Initialization
38 // ____________________________________________________________________________
39
40 /// Vector of points will be copied and checked for duplicates
41 SurfData::SurfData(const vector<SurfPoint>& points_)
42 {
43 if (points_.empty()) {
44 this->xsize = 0;
45 this->fsize = 0;
46 this->gradsize = 0;
47 this->hesssize = 0;
48 } else {
49 this->xsize = points_[0].xSize();
50 this->fsize = points_[0].fSize();
51 this->gradsize = points_[0].fGradientsSize();
52 this->hesssize = points_[0].fHessiansSize();
53 defaultLabels();
54 for (unsigned i = 0; i < points_.size(); i++) {
55 this->addPoint(points_[i]);
56 }
57 }
58 init();
59 // Check to make sure data points all have the same number of dimensions
60 // and response values. An exception will be thrown otherwise.
61 sanityCheck();
62 }
63
64 /// Read a set of SurfPoints from a file
SurfData(const string filename)65 SurfData::SurfData(const string filename):
66 xsize(0), fsize(0), gradsize(0), hesssize(0)
67 {
68 init();
69 read(filename);
70 }
71
72 /// Read a set of SurfPoints from a istream. The stream does not
73 /// contain the normal header information (#points, #vars, #responses).
74 /// The #vars and #responses are explicitly specified in the constructor;
75 /// The stream reader processes data until eof, assuming one point per line.
SurfData(const string filename,unsigned n_vars,unsigned n_responses,unsigned n_cols_to_skip)76 SurfData::SurfData(const string filename, unsigned n_vars,
77 unsigned n_responses, unsigned n_cols_to_skip):
78 xsize(n_vars), fsize(n_responses), gradsize(0), hesssize(0)
79 {
80 init();
81
82 if (!surfpack::hasExtension(filename,".dat")
83 && !surfpack::hasExtension(filename,".spd")) {
84 cerr << "Bad filename: " << filename << endl;
85 throw surfpack::io_exception(
86 "Expected .dat extension for filename"
87 );
88 }
89 ifstream infile(filename.c_str(),ios::in);
90 if (!infile) {
91 throw surfpack::file_open_failure(filename);
92 }
93 bool read_header = false;
94 readText(infile, read_header, n_cols_to_skip);
95 }
96
97 /// Read a set of SurfPoints from a istream
SurfData(istream & is,bool binary)98 SurfData::SurfData(istream& is, bool binary):
99 xsize(0), fsize(0), gradsize(0), hesssize(0)
100 {
101 init();
102 if (binary) {
103 readBinary(is);
104 } else {
105 readText(is);
106 }
107 }
108
109 /// Makes a deep copy of the object
SurfData(const SurfData & other)110 SurfData::SurfData(const SurfData& other):
111 xsize(other.xsize), fsize(other.fsize),
112 gradsize(other.gradsize), hesssize(other.hesssize),
113 excludedPoints(other.excludedPoints), defaultIndex(other.defaultIndex),
114 xLabels(other.xLabels), fLabels(other.fLabels)
115 {
116 for (unsigned i = 0; i < other.points.size(); i++) {
117 this->addPoint(*other.points[i]);
118 }
119 mapping = other.mapping;
120 buildOrderedPoints();
121 }
122
123 /// First SurfPoint added will determine the dimensions of the data set
SurfData()124 SurfData::SurfData(): xsize(0), fsize(0), gradsize(0), hesssize(0)
125 {
126 init();
127 }
128
129 /// STL data members' resources automatically deallocated
~SurfData()130 SurfData::~SurfData()
131 {
132 cleanup();
133 }
134
135 /// Data member initialization that is common to all constructors
init()136 void SurfData::init()
137 {
138 defaultIndex = 0;
139 defaultMapping();
140 }
141
142 /// Copy only the points which have not been marked for exclusion
copyActive()143 SurfData SurfData::copyActive()
144 {
145 // This is not a very efficient method. It is not expected that it will
146 // be called often. The active points are copied into the local
147 // activePoints vector. Then, a new SurfData object is created, which
148 // requires that all the points be recopied. Then, at the end of the method
149 // the new SurfData object is returned by value, which means all of the data
150 // are most likely copied again. If this is a bottleneck, it would not be
151 // terribly difficult to eliminate at least two of the three copies.
152 vector<SurfPoint> activePoints;
153 for (unsigned i = 0; i < mapping.size(); i++) {
154 activePoints.push_back(*points[mapping[i]]);
155 }
156 SurfData newSD(activePoints);
157 if (!activePoints.empty()) {
158 newSD.setDefaultIndex(defaultIndex);
159 }
160 return newSD;
161 }
162
163 /// Call delete on the SurfPoint* in the data set.
cleanup()164 void SurfData::cleanup()
165 {
166 mapping.clear();
167 orderedPoints.clear();
168 for (unsigned j = 0; j < points.size(); j++) {
169 delete points[j];
170 points[j] =0;
171 }
172 points.clear();
173 excludedPoints.clear();
174 }
175
176 // ____________________________________________________________________________
177 // Overloaded operators
178 // ____________________________________________________________________________
179
180 /// Makes deep copy of other
operator =(const SurfData & other)181 SurfData& SurfData::operator=(const SurfData& other)
182 {
183 if (*this != other) {
184 xLabels = other.xLabels;
185 fLabels = other.fLabels;
186 cleanup();
187 this->xsize = other.xsize;
188 this->fsize = other.fsize;
189 this->gradsize = other.gradsize;
190 this->hesssize = other.hesssize;
191 for (unsigned i = 0; i < other.points.size(); i++) {
192 this->addPoint(*other.points[i]);
193 }
194 this->excludedPoints = other.excludedPoints;
195 this->mapping = other.mapping;
196 this->defaultIndex = other.defaultIndex;
197 }
198 buildOrderedPoints();
199 return (*this);
200 }
201
202 /// Makes deep comparison
operator ==(const SurfData & other) const203 bool SurfData::operator==(const SurfData& other) const
204 {
205 if (this->xsize == other.xsize &&
206 this->fsize == other.fsize &&
207 this->gradsize == other.gradsize &&
208 this->hesssize == other.hesssize &&
209 this->size() == other.size()) {
210 for (unsigned i = 0; i < points.size(); i++) {
211 if (*this->points[i] != *other.points[i]) {
212 return false;
213 }
214 }
215 return true;
216 } else {
217 return false;
218 }
219 }
220
221 /// Makes deep comparison
operator !=(const SurfData & other) const222 bool SurfData::operator!=(const SurfData& other) const
223 {
224 return !(*this == other);
225 }
226
227 /// Return a const reference to SurfPoint at given index
operator [](unsigned index) const228 const SurfPoint& SurfData::operator[](unsigned index) const
229 {
230 static string header("Indexing error in SurfData::operator[] const.");
231 checkRangeNumPoints(header, index);
232 return *points[mapping[index]];
233 }
234
235 /// Return the x-value for point pt along dimension dim
operator ()(unsigned pt,unsigned dim) const236 double SurfData::operator()(unsigned pt, unsigned dim) const
237 {
238 assert(pt < size());
239 assert(dim < xSize());
240 return points[mapping[pt]]->X()[dim];
241 }
242
243 /// Return the vector of predictor vars for point index
operator ()(unsigned pt) const244 const vector<double>& SurfData::operator()(unsigned pt) const
245 {
246 if (pt >= size()) {
247 cout << "Assertion failure. Pt: " << pt << " size: " << size() << endl;
248 }
249 assert(pt < size());
250 return points[mapping[pt]]->X();
251 }
252
253 // ____________________________________________________________________________
254 // Queries
255 // ____________________________________________________________________________
256
257 /// Return the number of SurfPoints in the data set
size() const258 unsigned SurfData::size() const
259 {
260 return mapping.size();
261 }
262
263 /// True if there are no points
empty() const264 bool SurfData::empty() const
265 {
266 return mapping.empty();
267 }
268
269 /// Return the dimensionality of the SurfPoints
xSize() const270 unsigned SurfData::xSize() const
271 {
272 return xsize;
273 }
274
275 /// Return the number of response variables in the data set
fSize() const276 unsigned SurfData::fSize() const
277 {
278 return fsize;
279 }
280
281 /// Return the set of excluded points (the indices)
getExcludedPoints() const282 const set<unsigned>& SurfData::getExcludedPoints() const
283 {
284 return excludedPoints;
285 }
286
287 /// Get the response value of the (index)th point that corresponds to this
288 /// surface
getResponse(unsigned index) const289 double SurfData::getResponse(unsigned index) const
290 {
291 static string header("Indexing error in SurfData::getResponse.");
292 checkRangeNumPoints(header, index);
293 return points[mapping[index]]->F(defaultIndex);
294 }
295
296 // const std::vector<double>& SurfData::getGradient(unsigned index) const
297 // {
298 // static string header("Indexing error in SurfData::getResponse.");
299 // checkRangeNumPoints(header, index);
300 // return points[mapping[index]]->fGradient(defaultIndex);
301 // }
302
303 // const SurfpackMatrix<double>& SurfData::getHessian(unsigned index) const
304 // {
305 // static string header("Indexing error in SurfData::getResponse.");
306 // checkRangeNumPoints(header, index);
307 // return points[mapping[index]]->fHessian(defaultIndex);
308 // }
309
310 /// Get the default response for all of the points as a vector
getResponses() const311 std::vector< double > SurfData::getResponses() const
312 {
313 vector< double > result(mapping.size());
314 for (unsigned i = 0; i < mapping.size(); i++) {
315 result[i] = points[mapping[i]]->F(defaultIndex);
316 }
317 return result;
318 }
319
320 /// Get the predictor (index-th x-value) for all the active points as a vector
getPredictor(unsigned index) const321 std::vector< double > SurfData::getPredictor(unsigned index) const
322 {
323 assert(index < xSize());
324 vector< double > result(mapping.size());
325 for (unsigned i = 0; i < mapping.size(); i++) {
326 result[i] = (*this)(i,index);
327 }
328 return result;
329 }
330
getConstraintPoint() const331 const SurfPoint& SurfData::getConstraintPoint() const
332 {
333 return constraintPoint;
334 }
335
336 /** Calculate the number of constraint data present in functions,
337 gradients, and Hessians in the constraintPoint. */
numConstraints() const338 unsigned SurfData::numConstraints() const
339 {
340 unsigned num_constraints = 0;
341 if (constraintPoint.fSize() > 0)
342 num_constraints += 1; // value at a particular point
343 if (constraintPoint.fGradientsSize() > 0)
344 num_constraints += xsize; // gradient at a point
345 if (constraintPoint.fHessiansSize() > 0)
346 num_constraints += (xsize*xsize+xsize)/2; // hessian at a point
347 return num_constraints;
348 }
349
350
351 /// Get default index
getDefaultIndex() const352 unsigned SurfData::getDefaultIndex() const
353 {
354 return defaultIndex;
355 }
356
357 /// Retrieve the label for one of the predictor variables
getXLabel(unsigned index) const358 const string& SurfData::getXLabel(unsigned index) const
359 {
360 return xLabels[index];
361 }
362
363 /// Retrieve the label for one of the response variables
getFLabel(unsigned index) const364 const string& SurfData::getFLabel(unsigned index) const
365 {
366 return fLabels[index];
367 }
368
369 /// Retrieve the index and variable type (predictor/response) for a given
370 /// name. Return false if not found
varIndex(const string & name,unsigned & index,bool & isResponse) const371 bool SurfData::varIndex(const string& name, unsigned& index,
372 bool& isResponse) const
373 {
374 //\todo check for apostrophes rather than just assuming they're there
375 // Strip off the apostrophes at the beginning and end
376 string unquoted_name = name;
377 if (name.find('\'') != string::npos) {
378 unquoted_name = string(name,1,name.size()-2);
379 }
380 vector< string>::const_iterator iter =
381 find(xLabels.begin(),xLabels.end(),unquoted_name);
382 if (iter != xLabels.end()) {
383 index = iter - xLabels.begin();
384 isResponse = false;
385 return true;
386 } else {
387 iter = find(fLabels.begin(),fLabels.end(),unquoted_name);
388 if (iter != fLabels.end()) {
389 index = iter - fLabels.begin();
390 isResponse = true;
391 return true;
392 } else {
393 cout << "Name sought: " << unquoted_name << endl;
394 cout << "Predictors: " << endl;
395 copy(xLabels.begin(),xLabels.end(),ostream_iterator<string>(cout,"\n"));
396 cout << "Responses: " << endl;
397 copy(fLabels.begin(),fLabels.end(),ostream_iterator<string>(cout,"\n"));
398 return false;
399 }
400 }
401 }
402
403 // ____________________________________________________________________________
404 // Commands
405 // ____________________________________________________________________________
406
407 /// Specify which response value getResponse will return. When a Surface
408 /// object that is associated with the SurfData object operates on the data,
409 /// it sets this value so that the response value lookup function will return
410 /// the value for the response variable that that particular Surface object
411 /// is interested in.
setDefaultIndex(unsigned index) const412 void SurfData::setDefaultIndex(unsigned index) const
413 {
414 static string header("Indexing error in SurfData::setDefaultIndex.");
415 checkRangeNumResponses(header, index);
416 defaultIndex = index;
417 }
418
419 /// Set the response value of the (index)th point that corresponds to this
420 /// surface
setResponse(unsigned index,double value)421 void SurfData::setResponse(unsigned index, double value)
422 {
423 static string header("Indexing error in SurfData::setResponse.");
424 checkRangeNumPoints(header, index);
425 points[mapping[index]]->F(defaultIndex, value);
426 }
427
428 /// Add a point to the data set. The parameter point will be copied.
addPoint(const SurfPoint & sp)429 void SurfData::addPoint(const SurfPoint& sp)
430 {
431 if (points.empty()) {
432 xsize = sp.xSize();
433 fsize = sp.fSize();
434 gradsize = sp.fGradientsSize();
435 hesssize = sp.fHessiansSize();
436 if (xLabels.empty()) {
437 defaultLabels();
438 }
439 } else {
440 if (sp.xSize() != xsize || sp.fSize() != fsize ||
441 sp.fGradientsSize() != gradsize || sp.fHessiansSize() != hesssize) {
442 ostringstream errormsg;
443 errormsg << "Error in SurfData::addPoint. Points in this data set "
444 << "have " << xsize << " dimensions and " << fsize
445 << " response values; point to be added has "
446 << sp.xSize() << " dimensions and " << sp.fSize()
447 << " response values. (Or gradient and Hessian sizes don't "
448 << "match.)" << endl;
449 throw bad_surf_data(errormsg.str());
450 }
451 }
452 SurfPointSet::iterator iter;
453 // This should be a safe const cast. All that's happening is a check
454 // to see if another data point at the same location in the space has already
455 // been added.
456 iter = orderedPoints.find(const_cast<SurfPoint*>(&sp));
457 if (iter == orderedPoints.end()) {
458 // This SurfPoint is not already in the data set. Add it.
459 points.push_back(new SurfPoint(sp));
460 orderedPoints.insert(points[points.size()-1]);
461 mapping.push_back(points.size()-1);
462 } else {
463 // Another SurfPoint in this SurfData object has the same location and
464 // may have different response value(s). Replace the old point with
465 // this new one.
466 SurfPoint* spPtr = *iter;
467 *spPtr = sp;
468 }
469 }
470
471 /// Add a new response variable to each point. Return the index of the new
472 /// variable.
addResponse(const vector<double> & newValues,string label)473 unsigned SurfData::addResponse(const vector<double>& newValues,
474 string label)
475 {
476 unsigned new_index;
477 ostringstream errormsg;
478 if (points.empty()) {
479 throw bad_surf_data(
480 "Cannot add response because there are no data points"
481 );
482 } else if (points.size() != mapping.size()) {
483 errormsg << "Cannot add response because physical set size is different "
484 << "than logical set size.\nBefore adding another response, "
485 << "clear \"excluded points\" or create a new data set by using "
486 << "the SurfData::copyActive method." << endl;
487 throw bad_surf_data(errormsg.str());
488 } else if (newValues.size() != points.size()) {
489 errormsg << "Cannot add another response: the number of new response"
490 << " values does not match the size of the physical data set."
491 << endl;
492 throw bad_surf_data(errormsg.str());
493 } else {
494 new_index = points[mapping[0]]->addResponse(newValues[0]);
495 fsize++;
496 for (unsigned i = 1; i < points.size(); i++) {
497 new_index = points[mapping[i]]->addResponse(newValues[i]);
498 assert(new_index == fsize - 1);
499 }
500 }
501 if (label != "") {
502 fLabels.push_back(label);
503 } else {
504 ostringstream labelos;
505 labelos << "f" << new_index ;
506 fLabels.push_back(labelos.str());
507 }
508 return new_index;
509 }
510
setConstraintPoint(const SurfPoint & sp)511 void SurfData::setConstraintPoint(const SurfPoint& sp)
512 {
513 // handle the case of this being the first point in the data set
514 if (points.empty()) {
515 xsize = sp.xSize();
516 fsize = sp.fSize();
517 gradsize = sp.fGradientsSize();
518 hesssize = sp.fHessiansSize();
519 if (xLabels.empty()) {
520 defaultLabels();
521 }
522 } else {
523 if (sp.xSize() != xsize || sp.fSize() != fsize ||
524 sp.fGradientsSize() != gradsize || sp.fHessiansSize() != hesssize) {
525 ostringstream errormsg;
526 errormsg << "Error in SurfData::setConstraintPoint. Points in this data set "
527 << "have " << xsize << " dimensions and " << fsize
528 << " response values; point to be added has "
529 << sp.xSize() << " dimensions and " << sp.fSize()
530 << " response values. (Or gradient and Hessian sizes don't "
531 << "match.)" << endl;
532 throw bad_surf_data(errormsg.str());
533 }
534 }
535 constraintPoint = sp;
536 }
537
538 /// Specify which points should be skipped. This can be used when only a
539 /// subset of the SurfPoints should be used for some computation.
setExcludedPoints(const set<unsigned> & excluded_points)540 void SurfData::setExcludedPoints(const set<unsigned>& excluded_points)
541 {
542 if (excluded_points.size() > points.size()) {
543 throw bad_surf_data(
544 "Size of set of excluded points exceeds size of SurfPoint set"
545 );
546 } else if (excluded_points.empty()) {
547 defaultMapping();
548 this->excludedPoints.clear();
549 } else {
550 // The size of the logical data set is the size of the physical
551 // data set less the number of excluded points
552 mapping.resize(points.size() - excluded_points.size());
553 unsigned mappingIndex = 0;
554 unsigned sdIndex = 0;
555 // map the valid indices to the physical points in points
556 while (sdIndex < points.size()) {
557 if (excluded_points.find(sdIndex) == excluded_points.end()) {
558 mapping[mappingIndex++] = sdIndex;
559 }
560 sdIndex++;
561 }
562 this->excludedPoints = excluded_points;
563 assert(mappingIndex == mapping.size());
564 }
565 }
566
567 /// For use with copy constructor and assignment operator-- creates a list of
568 /// pointers to the points in the data set which is used to check for
569 /// duplication when other points are added in the future
buildOrderedPoints()570 void SurfData::buildOrderedPoints()
571 {
572 orderedPoints.clear();
573 for (unsigned i = 0; i < points.size(); i++) {
574 orderedPoints.insert(points[i]);
575 }
576 }
577
578 /// Maps all indices to themselves in the mapping data member
defaultMapping()579 void SurfData::defaultMapping()
580 {
581 mapping.resize(points.size());
582 for (unsigned i = 0; i < points.size(); i++) {
583 mapping[i] = i;
584 }
585 }
586
587 /// Set the labels for the predictor variables
setXLabels(const vector<string> & labels)588 void SurfData::setXLabels(const vector<string>& labels)
589 {
590 if (labels.size() != xsize) {
591 throw string("Dim mismatch in SurfData::setXLabels");
592 }
593 xLabels = labels;
594 }
595
596 /// Set the labels for the response variables
setFLabels(const vector<string> & labels)597 void SurfData::setFLabels(const vector<string>& labels)
598 {
599 if (labels.size() != fsize) {
600 throw string("Dim mismatch in SurfData::setFLabels");
601 }
602 fLabels = labels;
603 }
604
605 /// Set the label for a single response variable
setFLabel(unsigned index,const string & response_name)606 void SurfData::setFLabel(unsigned index, const string& response_name)
607 {
608 if (index >= fsize) {
609 throw string("Dim mismatch in SurfData::setFLabel");
610 }
611 fLabels[index] = response_name;
612 }
613
614 // ____________________________________________________________________________
615 // I/O
616 // ____________________________________________________________________________
617
618 /// Write a set of SurfPoints to a file. Opens the file and calls
619 /// other version of write. All files include dimensions, but some
620 /// contain additional header and label information.
write(const string & filename) const621 void SurfData::write(const string& filename) const
622 {
623 if (mapping.empty()) {
624 ostringstream errormsg;
625 errormsg << "Cannot write SurfData object to stream."
626 << " No active data points." << endl;
627 throw bad_surf_data(errormsg.str());
628 }
629 bool binary = hasBinaryFileExtension(filename);
630 ofstream outfile(filename.c_str(),
631 (binary ? ios::out|ios::binary : ios::out));
632 if (!outfile) {
633 throw surfpack::file_open_failure(filename);
634 } else if (binary) {
635 writeBinary(outfile);
636 } else {
637 bool write_header = false;
638 // Write the header and label info for .spd, not for .dat
639 bool metadata = surfpack::hasExtension(filename,".spd");
640 writeText(outfile, write_header, metadata);
641 }
642 outfile.close();
643 }
644
645 /// Read a set of SurfPoints from a file. Opens file and calls other version.
read(const string & filename)646 void SurfData::read(const string& filename)
647 {
648 // Open file in binary or text mode based on filename extension (.bspd or .spd)
649 bool binary = hasBinaryFileExtension(filename);
650 ifstream infile(filename.c_str(), (binary ? ios::in|ios::binary : ios::in));
651 if (!infile) {
652 throw surfpack::file_open_failure(filename);
653 } else if (binary) {
654 readBinary(infile);
655 } else {
656 readText(infile);
657 }
658 // Object may have already been created
659 infile.close();
660 }
661
662 /// Write a set of SurfPoints to an output stream
writeBinary(ostream & os) const663 void SurfData::writeBinary(ostream& os) const
664 {
665 unsigned s = mapping.size();
666 os.write((char*)&s,sizeof(s));
667 os.write((char*)&xsize,sizeof(xsize));
668 os.write((char*)&fsize,sizeof(fsize));
669 os.write((char*)&gradsize,sizeof(gradsize));
670 os.write((char*)&hesssize,sizeof(hesssize));
671 ///\todo accumulate all the writes into one big chunk of data
672 /// and write it out all at once.
673 for (unsigned i = 0; i < mapping.size(); i++) {
674 points[mapping[i]]->writeBinary(os);
675 }
676 }
677
678 /// Write a set of SurfPoints to an output stream
writeText(ostream & os,bool write_header,bool write_labels) const679 void SurfData::writeText(ostream& os,
680 bool write_header, bool write_labels) const
681 {
682 if (write_header) {
683 os << mapping.size() << endl
684 << xsize << endl
685 << fsize << endl
686 << gradsize << endl
687 << hesssize << endl;
688 }
689 if (write_labels) {
690 os << '%';
691 for (unsigned i = 0; i < xLabels.size(); i++) {
692 int correction = i ? 0 : 1; // the '%' takes up one space
693 os << setw(surfpack::field_width - correction) << xLabels[i];
694 }
695 for (unsigned i = 0; i < fLabels.size(); i++) {
696 os << setw(surfpack::field_width) << fLabels[i];
697 }
698 os << endl;
699 }
700 for (unsigned i = 0; i < mapping.size(); i++) {
701 //if (!write_header) {
702 // os << setw((int)log10((double)(mapping.size())+2)) << (i+1);
703 //}
704 points[mapping[i]]->writeText(os);
705 }
706 }
707
708 /// Read a set of SurfPoints from an input stream
readBinary(istream & is)709 void SurfData::readBinary(istream& is)
710 {
711 ///\todo Eliminate need for SurfPoint constructor that takes istream
712 ///Instead, allocate a big chunk of memory, read all of the data in
713 /// at the same time, then iterate through the block of data and
714 /// create SurfPoints using the constructor that takes x and f.
715 unsigned n_points_read = 0;
716 unsigned size;
717 try {
718 cleanup();
719 is.read((char*)&size,sizeof(size));
720 is.read((char*)&xsize,sizeof(xsize));
721 is.read((char*)&fsize,sizeof(fsize));
722 is.read((char*)&gradsize,sizeof(gradsize));
723 is.read((char*)&hesssize,sizeof(hesssize));
724 points.clear();
725 for (n_points_read = 0; n_points_read < size; n_points_read++) {
726 // Throw an exception if we hit the end-of-file before we've
727 // read the number of points that were supposed to be there.
728 surfpack::checkForEOF(is);
729 this->addPoint(SurfPoint(is, xsize, fsize, gradsize, hesssize));
730 }
731 defaultMapping();
732 } catch(surfpack::io_exception&) {
733 cerr << "Expected: " << size << " points. "
734 << "Read: " << n_points_read << " points." << endl;
735 throw;
736 }
737 }
738
readHeaderInfo(istream & is)739 unsigned SurfData::readHeaderInfo(istream& is)
740 {
741 string single_line;
742
743 getline(is,single_line);
744 istringstream streamline(single_line);
745 unsigned declared_size;
746 streamline >> declared_size;
747
748 getline(is,single_line);
749 streamline.str(single_line); streamline.clear();
750 streamline >> xsize;
751
752 getline(is,single_line);
753 streamline.str(single_line); streamline.clear();
754 streamline >> fsize;
755
756 getline(is,single_line);
757 streamline.str(single_line); streamline.clear();
758 streamline >> gradsize;
759
760 getline(is,single_line);
761 streamline.str(single_line); streamline.clear();
762 streamline >> hesssize;
763
764 return declared_size;
765 }
766
767 /// Read a set of SurfPoints from a text input stream
readText(istream & is,bool read_header,unsigned skip_columns)768 void SurfData::readText(istream& is, bool read_header, unsigned skip_columns)
769 {
770 unsigned n_points_read = 0;
771 unsigned declared_size = 0;
772 string single_line;
773 try {
774 cleanup();
775 points.clear();
776 if (read_header) declared_size = readHeaderInfo(is);
777
778 getline(is,single_line);
779 istringstream streamline(single_line);
780 if (!readLabelsIfPresent(single_line)) {
781 if (single_line != "" && single_line != "\n" && single_line[0] != '%') {
782 this->addPoint(SurfPoint(single_line, xsize,
783 fsize, gradsize, hesssize, skip_columns));
784 n_points_read = 1;
785 }
786 }
787 while (!is.eof()) {
788 // Throw an exception if we hit the end-of-file before we've
789 // read the number of points that were supposed to be there.
790 //surfpack::checkForEOF(is);
791 getline(is,single_line);
792 if (single_line[0] == '%' || single_line == "") continue;
793 // False for last argument signals a text read
794 this->addPoint(SurfPoint(single_line, xsize,
795 fsize, gradsize, hesssize, skip_columns));
796 n_points_read++;
797 }
798 defaultMapping();
799 } catch(surfpack::io_exception& exception) {
800 cerr << exception.what() << endl;
801 throw;
802 }
803 if (read_header && n_points_read != declared_size) {
804 ostringstream errormsg;
805 errormsg << "Expected: " << declared_size << " points. "
806 << "Read: " << n_points_read << " points." << endl;
807 throw surfpack::io_exception(errormsg.str());
808 }
809 }
810
811 // Print set of data points to a stream.
operator <<(ostream & os,const SurfData & sd)812 ostream& operator<<(ostream& os, const SurfData& sd)
813 {
814 sd.writeText(os);
815 return os;
816 }
817
818 // ____________________________________________________________________________
819 // Helper methods
820 // ____________________________________________________________________________
821
822 /// Returns true if file has .bspd extension, false if it has .spd extension.
823 /// Otherwise, an exception is thrown.
hasBinaryFileExtension(const string & filename) const824 bool SurfData::hasBinaryFileExtension(const string& filename) const
825 {
826 if (surfpack::hasExtension(filename,".bspd")) {
827 return true;
828 } else if (surfpack::hasExtension(filename,".spd")) {
829 return false;
830 } else if (surfpack::hasExtension(filename,".dat")) {
831 return false;
832 } else {
833 throw surfpack::io_exception(
834 "Unrecognized filename extension. Use .bspd, or .spd"
835 );
836 }
837 }
838
839 /// Set x vars labels to x0 x1, etc.; resp. vars to f0 f1, etc.
defaultLabels()840 void SurfData::defaultLabels()
841 {
842 xLabels.resize(xsize);
843 for (unsigned i = 0; i < xsize; i++) {
844 ostringstream os;
845 os << "x" << i ;
846 xLabels[i] = os.str();
847 }
848 fLabels.resize(fsize);
849 for (unsigned i = 0; i < fsize; i++) {
850 ostringstream os;
851 os << "f" << i ;
852 fLabels[i] = os.str();
853 }
854 }
855
readLabelsIfPresent(string single_line)856 bool SurfData::readLabelsIfPresent(string single_line)
857 {
858 if (single_line[0] != '%') {
859 defaultLabels();
860 return false;
861 } else {
862 single_line[0] = ' ';
863 string label;
864 xLabels.resize(xsize);
865 istringstream is(single_line);
866 for (unsigned i = 0; i < xsize; i++) {
867 is >> xLabels[i];
868 if (xLabels[i] == "") {
869 // not enough heading names
870 // line of column headings. Use the default headings and return.
871 defaultLabels();
872 return false;
873 }
874 } // predictor variable labels
875 fLabels.resize(fsize);
876 for (unsigned i = 0; i < fsize; i++) {
877 is >> fLabels[i];
878 if (fLabels[i] == "") {
879 // not enough heading names
880 // line of column headings. Use the default headings and return.
881 defaultLabels();
882 return false;
883 }
884 } // response variable labels
885 } // custom labels
886 return true;
887 }
888 // ____________________________________________________________________________
889 // Testing
890 // ____________________________________________________________________________
891
892 // Throw an exception if there are any mismatches in the number of
893 // dimensions or number of response values among points in the data set
sanityCheck() const894 void SurfData::sanityCheck() const
895 {
896 if (!points.empty()) {
897 unsigned dimensionality = points[0]->xSize();
898 unsigned numResponses = points[0]->fSize();
899 unsigned numGrad = points[0]->fGradientsSize();
900 unsigned numHess = points[0]->fHessiansSize();
901 for (unsigned i = 1; i < points.size(); i++) {
902 if (points[i]->xSize() != dimensionality ||
903 points[i]->fSize() != numResponses ||
904 points[i]->fGradientsSize() != numGrad ||
905 points[i]->fHessiansSize() != numHess ) {
906 ostringstream errormsg;
907 errormsg << "Error in SurfData::sanityCheck." << endl
908 << "Point 0 has " << dimensionality << " dimensions "
909 << "and " << numResponses << " response values, " << endl
910 << "but point " << i << " has " << points[i]->xSize()
911 << " dimensions and " << points[i]->fSize() << "response "
912 << " values. (Or gradient and Hessian sizes are wrong.)";
913 throw bad_surf_data(errormsg.str());
914 } // if mismatch
915 } // for each point
916 } // if !empty
917 }
918
919 /// Check that the index falls within acceptable boundaries (i.e., is
920 /// less than mapping.size()
checkRangeNumPoints(const string & header,unsigned index) const921 void SurfData::checkRangeNumPoints(const string& header, unsigned index) const
922 {
923 if (index >= mapping.size()) {
924 ostringstream errormsg;
925 errormsg << header << endl;
926 if (mapping.empty()) {
927 errormsg << "Index " << index << " specified, but there are zero points "
928 << "in the logical data set."
929 << endl;
930 } else {
931 errormsg << "Requested: "
932 << index
933 << "; actual max index: "
934 << mapping.size() - 1
935 << endl;
936 }
937 throw range_error(errormsg.str());
938 }
939 }
940
941 /// Make sure an index falls within acceptable boundaries (i.e., index is less
942 /// than fsize)
checkRangeNumResponses(const string & header,unsigned index) const943 void SurfData::checkRangeNumResponses(const string& header,
944 unsigned index) const
945 {
946 if (index >= fsize) {
947 ostringstream errormsg;
948 errormsg << header << endl;
949 if (fsize == 0) {
950 errormsg << "Index " << index
951 << " specified, but there are zero response"
952 << "values."
953 << endl;
954 } else {
955 errormsg << "Requested: "
956 << index
957 << "; actual max index: "
958 << fsize - 1
959 << endl;
960 }
961 throw range_error(errormsg.str());
962 }
963 }
964
asVecVecDbl(const SurfData & data)965 VecVecDbl SurfData::asVecVecDbl(const SurfData& data)
966 {
967 VecVecDbl result(data.size());
968 for (unsigned i = 0; i < data.size(); i++) {
969 result[i].resize(data.xSize());
970 for (unsigned j = 0; j < data.xSize(); j++) {
971 result[i][j] = data(i,j);
972 }
973 }
974 return result;
975 }
976