void SparseMat::solve_lower_triangle_trans(const DoubleVec &rhs, DoubleVec &x) const { // Solve the transpose of a lower triangular matrix with explicitly // stored diagonal elements. rhs and x can be the same vector. // x_i = (1/L_ii) (rhs_i - \sum_{j=i+1}^M L_ji x_j) assert(is_lower_triangular(true)); assert(rhs.size() == nrows_); assert(x.size() == nrows_); assert(nrows_ == ncols_); // Copy rhs into x. This does all of the rhs_i terms. if(&x[0] != &rhs[0]) x = rhs; for(int j=int(x.size()-1); j>=0; j--) { const_row_iterator ji = --end(j); if(ji.row() != ji.col() || *ji == 0.0) throw ErrSetupError("Zero divisor in solve_lower_triangle_trans!"); // We're done with the sums for x[j]. x[j] /= *ji; for(--ji; ji>=begin(j); --ji) x[ji.col()] -= (*ji) * x[j]; } }
void SparseMat::axpy(double alpha, const DoubleVec &x, DoubleVec &y) const { if(ncols() != x.size() || nrows() != y.size()) { std::string msg = "Incompatible sizes in SparseMat::axpy! [" + to_string(nrows()) + "x" + to_string(ncols()) + "] * [" + to_string(x.size()) + "] + [" + to_string(y.size()) + "]"; throw ErrProgrammingError(msg, __FILE__, __LINE__); } if(alpha != 1.0) { for(unsigned int r=0; r<nrows_; r++) { const SparseMatRow &row = data[r]; double sum = 0.0; for(SparseMatRow::const_iterator j=row.begin(); j<row.end(); ++j) sum += (*j).val * x[(*j).col]; y[r] += alpha*sum; } } else { // alpha == 1.0 for(unsigned int r=0; r<nrows_; r++) { const SparseMatRow &row = data[r]; double sum = 0.0; for(SparseMatRow::const_iterator j=row.begin(); j<row.end(); ++j) sum += (*j).val * x[(*j).col]; y[r] += sum; } } }
double Stdev(const DoubleVec &val) { double mean=0; double sq=0; for(DoubleVec::const_iterator it=val.begin(); it< val.end(); it++) { mean+=*it; sq+=*it*(*it); } double std= sqrt((val.size()*sq-mean*mean)/((val.size()-1)*val.size())); mean/=val.size(); sq/=val.size(); return(std); }
DoubleVec SparseMat::operator*(const DoubleVec &x) const { assert(x.size() == ncols_); DoubleVec result(nrows_, 0.0); for(unsigned int r=0; r<nrows_; r++) { double sum = 0.0; const SparseMatRow &row = data[r]; for(SparseMatRow::const_iterator j=row.begin(); j<row.end(); ++j) { assert((*j).col >= 0 && (*j).col < x.size()); sum += (*j).val * x[(*j).col]; } result[r] = sum; } return result; }
/** Returns the time interval covered by the data. * * @param[in] times Times at which data were taken * * @return The length of time between the earliest observation in times and * the latest observation in times, in whatever units @p times is in. * * @pre @p times.size() ≥ 2 * @pre @p times contains at least two unique values * * @perform O(N) time, where N = @p times.size() * * @exception std::invalid_argument Thrown if @p times has at most one element. * @exception kpftimes::except::BadLightCurve Thrown if @p times has * at most one distinct value. * * @exceptsafe The function arguments are unchanged in the event * of an exception. * * @test Regular grid, length 1. Expected behavior: throws invalid_argument * @test Regular grid, length 2. Expected behavior: returns step * @test Regular grid, length 100. Expected behavior: returns 99*step * @test Irregular grid, 2 values randomly chosen from [0, 1). Expected behavior: returns max_element()-min_element() * @test Irregular grid, 100 values randomly chosen from [0, 1). Expected behavior: returns max_element()-min_element() */ double deltaT(const DoubleVec ×) { if (times.size() < 2) { throw std::invalid_argument("Parameter 'times' in deltaT() contains fewer than 2 observations"); } // In C++11, this entire body can be replaced with a call to std::minmax_element() // Scanning the array to verify that it's sorted would take just as // long as scanning it for min and max double tMin = times.front(); double tMax = tMin; for (DoubleVec::const_iterator i = times.begin(); i != times.end(); i++) { if (*i < tMin) { tMin = *i; } if (*i > tMax) { tMax = *i; } } if (tMax <= tMin) { throw except::BadLightCurve("Parameter 'times' in deltaT() contains only one unique value"); } else { return (tMax - tMin); } }
/** Returns the highest frequency that can be probed by the data. This is * defined as 1/2dt, where dt > 0 is the @b smallest time interval between * any two observations. * * @param[in] times Times at which data were taken * * @return The highest meaningful frequency, in the inverse of whatever units * @p times is in. * * @pre @p times.size() ≥ 2 * @pre @p times contains at least two unique values * @pre @p times is sorted in ascending order * * @perform O(N) time, where N = @p times.size() * * @exception std::invalid_argument Thrown if @p times has at most one element. * @exception kpftimes::except::BadLightCurve Thrown if @p times has * at most one distinct value. * @exception kpfutils::except::NotSorted Thrown if @p times is not in * ascending order. * * @exceptsafe The function arguments are unchanged in the event * of an exception. * * @test Regular grid, length 1. Expected behavior: throws invalid_argument * @test Regular grid, length 2. Expected behavior: returns PNF = 1/(2*step) * @test Regular grid, length 100. Expected behavior: returns PNF = 1/(2*step) * @todo How to test this for irregular grids? */ double maxFreq(const DoubleVec ×) { if (times.size() < 2) { throw std::invalid_argument("Parameter 'times' in maxFreq() contains fewer than 2 observations"); } // Test for sort in O(N) // Faster than sorting, O(N log N), or unsorted test, O(N^2) if(!kpfutils::isSorted(times.begin(), times.end())) { throw kpfutils::except::NotSorted("Parameter 'times' in maxFreq() is unsorted"); } // Look for the smallest interval DoubleVec::const_iterator t1 = times.begin(); DoubleVec::const_iterator t2 = t1 + 1; double minDeltaT = 0.0; while(t2 != times.end()) { if (*t2 > *t1 && (*t2 - *t1 < minDeltaT || minDeltaT == 0.0)) { minDeltaT = *t2 - *t1; } t1++; t2++; } // Report the results if (minDeltaT == 0.0) { throw except::BadLightCurve("Parameter 'times' in maxFreq() contains only one unique value"); } else { return 0.5 / minDeltaT; } }
double VectorOutputVal::dot(const DoubleVec &other) const { assert(size() == other.size()); double sum = 0; for(unsigned int i=0; i<size(); i++) sum += data[i]*other[i]; return sum; }
void SparseMat::solve_lower_triangle_unitd(const DoubleVec &rhs, DoubleVec &x) const { // Solve a lower triangular matrix assuming that the diagonal // elements are 1.0. rhs and x can be the same vector. assert(is_lower_triangular(false)); // diags aren't stored explicitly // x_n = rhs_n - \sum{i=0}^{n-1} L_ni x_i for(unsigned int rowno=0; rowno<rhs.size(); rowno++) { double sum = 0.0; for(const_row_iterator ij=begin(rowno); ij<end(rowno); ++ij) sum += (*ij)*x[ij.col()]; x[rowno] = rhs[rowno] - sum; } }
void SparseMat::solve_lower_triangle(const DoubleVec &rhs, DoubleVec &x) const { // Solve a lower triangular matrix. rhs and x can be the same // vector. assert(is_lower_triangular(true)); // diag elements are stored explicitly // x_n = (1/L_nn) (rhs_n - \sum{i=0}^{n-1} L_ni x_i) for(unsigned int rowno=0; rowno<rhs.size(); rowno++) { double sum = 0.0; const_row_iterator ij=--end(rowno); double diag = *ij; if(diag == 0.0) throw ErrSetupError("Zero divisor in solve_lower_triangle"); for(--ij; ij>=begin(rowno); --ij) sum += (*ij)*x[ij.col()]; x[rowno] = (rhs[rowno] - sum)/diag; } }
void SparseMat::solve_lower_triangle_trans_unitd(const DoubleVec &rhs, DoubleVec &x) const { // Solve the transpose of a lower triangular matrix with implicit // ones on the diagonal. rhs and x can be the same vector. // x_i = rhs_i - \sum_{j=i+1}^M L_ji x_j assert(is_lower_triangular(false)); if(&x[0] != &rhs[0]) x = rhs; for(int j=int(x.size()-1); j>=0; j--) { if(is_nonempty_row(j)) { for(const_row_iterator ji=begin(j); ji<end(j); ++ji) { x[ji.col()] -= (*ji) * x[j]; } } } }
void SparseMat::solve_upper_triangle(const DoubleVec &rhs, DoubleVec &x) const { // Solve an upper triangular matrix with explicit diagonal elements. // rhs and x can be the same vector. assert(is_upper_triangular(true)); // x_i = (1/U_ii) (rhs_i - \sum_{n=i+1}^M U_in x_n ) // rowno must be an int, not an unsigned int, or else rowno>=0 will // always be true. for(int rowno=int(rhs.size())-1; rowno>=0; rowno--) { const_row_iterator ij=begin(rowno); double diagterm = *ij; if(ij.row() != ij.col() || diagterm == 0.0) throw ErrSetupError("Zero divisor in solve_upper_triangle!"); double sum = rhs[rowno]; for(++ij; ij<end(rowno); ++ij) sum -= (*ij)*x[ij.col()]; x[rowno] = sum/diagterm; } }
void SparseMat::solve_upper_triangle_trans(const DoubleVec &rhs, DoubleVec &x) const { // Solve the transpose of an upper triangular matrix with explicit // diagonal elements. rhs and x can be the same vector. assert(is_upper_triangular(true)); // x_i = (1/U_ii) (rhs_i - \sum_{j=0}^{i-1} U_ji x_j) // Copy rhs into x. This does all of the rhs_i terms. if(&x[0] != &rhs[0]) x = rhs; for(unsigned int i=0; i<x.size(); i++) { const_row_iterator ji=begin(i); // loop over i^th col of the transpose // We're already done with the sums for x_i. if(ji.row() != ji.col() || *ji == 0.0) throw ErrSetupError("Zero divisor in solve_upper_triangle_trans!"); x[i] /= *ji; // divide by the diagonal // Accumulate contributions to later x[i]'s. for(++ji; ji<end(i); ++ji) x[ji.col()] -= (*ji) * x[i]; } }
double ROC(const DoubleVec & _score_pos, const DoubleVec & _score_neg, DoubleVec &sens_vec, DoubleVec &spec_vec, DoubleVec &cutoffs) { if(_score_pos.size()==0 || _score_neg.size()==0) { cout << "Error: No positive or negative scores"; return(-1); } sens_vec.clear(); spec_vec.clear(); cutoffs.clear(); DoubleVec score_pos=_score_pos; DoubleVec score_neg=_score_neg; std::sort(score_pos.begin(),score_pos.end()); std::sort(score_neg.begin(),score_neg.end()); double integral=0; sens_vec.push_back(0); spec_vec.push_back(1); cutoffs.push_back(min(score_neg.front(),score_pos.front())); DoubleVec::const_iterator cut_neg=score_neg.begin(); DoubleVec::const_iterator cut_pos=score_pos.begin(); while(cut_pos<score_pos.end()) { while(cut_neg<score_neg.end()) // Move cut_neg to first value >= cut_pos { if(*cut_neg<*cut_pos) cut_neg++; else break; } unsigned TP=unsigned(cut_pos-score_pos.begin()); unsigned FP=unsigned(cut_neg-score_neg.begin()); unsigned TN=score_neg.size()-FP; unsigned FN=score_pos.size()-TP; double sens=double(TP)/score_pos.size(); double spec=double(TN)/score_neg.size(); integral+=(sens-sens_vec.back())*(spec+spec_vec.back())/2.0; sens_vec.push_back(sens); spec_vec.push_back(spec); cutoffs.push_back(*cut_pos); double last_cutoff=*cut_pos; while(cut_pos<score_pos.end()) // Advance cut_pos while = last cutoff !!Change that to move cut_pos to first value >= cut_neg, perfect symmetry { if(*cut_pos==last_cutoff) cut_pos++; else break; } while(cut_neg<score_neg.end()) // Advance cut_pos while = last cutoff { if(*cut_neg==last_cutoff) cut_neg++; else break; } TP=unsigned(cut_pos-score_pos.begin()); FP=unsigned(cut_neg-score_neg.begin()); TN=score_neg.size()-FP; FN=score_pos.size()-TP; sens=double(TP)/score_pos.size(); spec=double(TN)/score_neg.size(); integral+=(sens-sens_vec.back())*(spec+spec_vec.back())/2.0; sens_vec.push_back(sens); spec_vec.push_back(spec); double cp=score_pos.back(); double cn=score_neg.back(); if(cut_pos<score_pos.end()) cp=*cut_pos; if(cut_neg<score_neg.end()) cn=*cut_neg; cutoffs.push_back(min(cp,cn)); } integral+=(1-sens_vec.back())*(spec_vec.back()+0)/2.0; sens_vec.push_back(1); spec_vec.push_back(0); cutoffs.push_back(max(score_neg.back(),score_pos.back())); return(integral); }
/** Returns the pseudo-Nyquist frequency for a grid of observations. * * The pseudo-Nyquist frequency is defined as N/2T, where N is the number of * observations and T is the length of the time interval covered by the data. * * @param[in] times Times at which data were taken * * @return The pseudo-Nyquist frequency, in the inverse of whatever units * times is in. * * @pre @p times.size() ≥ 2 * @pre @p times contains at least two unique values * * @perform O(N) time, where N = @p times.size() * * @exception std::invalid_argument Thrown if @p times has at most one element. * @exception kpftimes::except::BadLightCurve Thrown if @p times has * at most one distinct value. * * @exceptsafe The function arguments are unchanged in the event * of an exception. * * @test Regular grid, length 1. Expected behavior: throws invalid_argument * @test Regular grid, length 2. Expected behavior: returns PNF = 1/(2*step) * @test Regular grid, length 100. Expected behavior: returns PNF = 1/(2*step) * * @todo Come up with test cases for an irregular grid. */ double pseudoNyquistFreq(const DoubleVec ×) { // Delegate input validation to deltaT return 0.5 * times.size() / deltaT(times); }
VectorOutputVal::VectorOutputVal(const DoubleVec &vec) : size_(vec.size()), data(new double[vec.size()]) { (void) memcpy(data, &vec[0], size_*sizeof(double)); }
void CNumVec::SetTo(const DoubleVec &v) { resize(v.size()); for(unsigned i=0; i<v.size(); i++) gsl_vector_set(m_vec,i,v[i]); }