qubx_idealize.cpp.html | mathcode2html |
Source file: qubx_idealize.cpp | |
Converted: Sun Aug 7 2016 at 13:47:27 | |
This documentation file will not reflect any later changes in the source file. |
$$\phantom{******** If you see this on the webpage then the
browser could not locate *********}$$
$$\phantom{******** jsMath/easy/load.js or the variable root
is set wrong in this file *********}$$
$$\newcommand{\vector}[1]{\left[\begin{array}{c} #1 \end{array}\right]}$$
$$\newenvironment{matrix}{\left[\begin{array}{cccccccccc}} {\end{array}\right]}$$
$$\newcommand{\A}{{\cal A}}$$
$$\newcommand{\W}{{\cal W}}$$
/* Copyright 2008-2014 Research Foundation State University of New York */ /* This file is part of QUB Express. */ /* QUB Express is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ /* the Free Software Foundation, either version 3 of the License, or */ /* (at your option) any later version. */ /* QUB Express is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU General Public License for more details. */ /* You should have received a copy of the GNU General Public License, */ /* named LICENSE.txt, in the QUB Express program directory. If not, see */ /* <http://www.gnu.org/licenses/>. */ /*
|
See also: Up: Index |
*/ #ifdef _WIN32 #include <windows.h> #else #include <stdlib.h> #define BOOL int #define TRUE 1 #define FALSE 0 #endif #include <string.h> #include <iostream> #include <fstream> #define _USE_MATH_DEFINES #include <math.h> #include <string.h> #include <set> #include <map> #include <vector> #include <algorithm> #include "qubx_idealize.h" #include "../qubfast/max_ll_util.h" #include "../qubfast/ublas_matrixutil.h" #include "../qubfast/callbk_reportstream.h" #include "../qubfast/running_mean.h" #include "../qubfast/sampled_gaussian.h" #include "../qubfast/qub_constraints.h" #include "../qubfast/nmsimplex.h" #include "../qubfast/qubx_model_storage.h" #include "../qubfast/qub_kalman.h" using std::cout; using std::cerr; using std::endl; using std::set; using std::map; using std::pair; using std::copy; using std::min; using std::max; class qubx_idlz_data; #define QUBX_IDLZ_AA_MAXITER 25 #define QUBX_IDLZ_AMPS_MAXITER 120 // like the telephone keypad: #define UNSET_VALUE 86738.82583 #ifdef _WIN32 #include "../qubfast/gettimeofday.h" #else #include <sys/time.h> #endif void print_timestamp(const char *lbl) { struct timeval tm; gettimeofday(&tm, NULL); cerr << "--- " << tm.tv_sec << " : " << (tm.tv_usec / 1000) << "." << (tm.tv_usec % 1000) << " --- " << lbl << endl; } typedef map<std::vector<double>, int> stimmap; void mplex_idl(int Nsignal, int *dwellCounts, int **classeses, float **durationses, double **ampses, int *out_dwellCount, int *out_classes, float *out_durations, stimmap& stimcls_of) { if ( ! Nsignal ) return; // the key to stimmap std::vector<double> amps(1+Nsignal); // output int &Nd = *out_dwellCount; Nd = 0; // per-signal stuff std::vector<int> finger(Nsignal, 0); std::vector<int> clss(Nsignal); std::vector<float> remain(Nsignal); for (int i=0; i<Nsignal; ++i) { if ( ! dwellCounts[i] ) return; clss[i] = classeses[i][0]; remain[i] = durationses[i][0]; if ( clss[i] >= 0 ) amps[i+1] = ampses[i][ clss[i] ]; else amps[i+1] = 0.0; // gap at beginning of stimulus: should complain and fail; instead silently defaulting to zero } while ( 1 ) { // until any signal ends bool done = false; for (int i=0; i<Nsignal; ++i) if ( finger[i] >= dwellCounts[i] ) done = true; if ( done ) break; // find signal with shortest remaining float tm = remain[0]; //int changing = 0; for (int i=0; i<Nsignal; ++i) { if ( remain[i] < tm ) { tm = remain[i]; //changing = i; } } // look up stimclass stimmap::iterator sti = stimcls_of.find(amps); if ( sti == stimcls_of.end() ) { int sc = (int) stimcls_of.size(); stimcls_of[amps] = sc; } // append the event out_classes[Nd] = stimcls_of[amps]; out_durations[Nd] = tm; ++Nd; // shorten all signals remain, and move the finger for any that are 0 for (int i=0; i<Nsignal; ++i) { remain[i] -= tm; if ( remain[i] <= 0.0 ) { if ( (++finger[i]) < dwellCounts[i] ) { remain[i] = durationses[i][ finger[i] ]; clss[i] = classeses[i][ finger[i] ]; if ( clss[i] >= 0 ) amps[i+1] = ampses[i][ clss[i] ]; // else: gap: keep the last event's amp } } } } } class qubx_idlz_data_piece { public: int stimclass; float *data; // pointer into mac_data->data, or NULL int *pt_cls; // pointer into mac_data->data_out, or NULL if count == 0 float *baseline; // "" int count; int first; qubx_idlz_data_piece(int stimclass_, float *data_, int *pt_cls_, float *baseline_, int count_, int first_) : stimclass(stimclass_), data(data_), pt_cls(pt_cls_), baseline(baseline_), count(count_), first(first_) {} int count_dwells() { if ( ! pt_cls ) return 1; int n = 1; int c = pt_cls[0]; for (int i=1; i<count; ++i) if ( pt_cls[i] != c ) { c = pt_cls[i]; ++n; } return n; } int get_dwells(int *ff, int *ll, int *cc) { if ( ! pt_cls ) { *ff = first; *ll = first + count - 1; *cc = -1; return 1; } int i_out = 0; int c = pt_cls[0]; int f = first; int n = 1; ff[0] = f; cc[0] = c; for (int i=1; i<count; ++i) if ( pt_cls[i] == c ) { ++n; } else { ll[i_out] = f + n - 1; ++i_out; f = ff[i_out] = f + n; n = 1; c = pt_cls[i]; cc[i_out] = c; } ll[i_out] = f + n - 1; return ( i_out + 1 ); } }; class qubx_idlz_data_seg { public: std::vector<qubx_idlz_data_piece> pieces; int first, count; std::vector<double> sc_frac; // algo responsibility: std::vector<double> amp, std; qubx_idlz_data_seg() : first(0), count(0) {} }; class qubx_idlz_data { public: double sampling; int Nstim, Nsig, Ndata, data_cap; float *data; int *pt_cls; float *baseline; std::vector<qubx_idlz_data_seg> segments; std::vector<double> stimclasses; stimmap stimcls_of; std::vector<double> sc_frac; qubx_idlz_data(double sampling_, int Nstimsig_, int data_cap_) : sampling(sampling_), Nstim(0), Nsig(Nstimsig_+1), Ndata(0), data_cap(data_cap_) , data(new float[data_cap_]), pt_cls(new int[data_cap_]), baseline(new float[data_cap_]) { memset(baseline, 0, data_cap_*sizeof(float)); } virtual ~qubx_idlz_data() { delete [] data; delete [] pt_cls; delete [] baseline; } void reset(double sampling_, int Nstimsig, int newcap) { Nstim = Ndata = 0; sampling = sampling_; Nsig = Nstimsig + 1; segments.clear(); stimclasses.clear(); stimcls_of.clear(); ensure_capacity(newcap); } void ensure_capacity(int newcap) { if ( data_cap < newcap ) { while ( data_cap < newcap ) data_cap *= 2; float *newdata = new float[data_cap]; int *new_pt_cls = new int[data_cap]; float *new_base = new float[data_cap]; memcpy(newdata, data, Ndata*sizeof(float)); memcpy(new_pt_cls, pt_cls, Ndata*sizeof(int)); memcpy(new_base, baseline, Ndata*sizeof(float)); memset(new_base+Ndata, 0, (data_cap - Ndata) * sizeof(float)); for (std::vector<qubx_idlz_data_seg>::iterator segi=segments.begin(); segi!=segments.end(); ++segi) { for (std::vector<qubx_idlz_data_piece>::iterator piece = segi->pieces.begin(); piece != segi->pieces.end(); ++piece) { if ( piece->data ) { piece->data = newdata + (piece->data - data); piece->pt_cls = new_pt_cls + (piece->pt_cls - pt_cls); piece->baseline = new_base + (piece->baseline - baseline); } } } delete [] data; delete [] pt_cls; delete [] baseline; data = newdata; pt_cls = new_pt_cls; baseline = new_base; memset(baseline, 0, data_cap*sizeof(float)); } } int append_segment(int first) { int ix = (int) segments.size(); segments.resize(ix+1); segments[ix].first = first; return ix; } void append_chunk(int seg_ix, int Nd, float *I, int *dwellCounts, int **classeses, float **durationses, double **ampses) { if ( I ) ensure_capacity(Ndata + Nd); float *chunk_data = I ? (data + Ndata) : NULL; int *chunk_pt_cls = I ? (pt_cls + Ndata) : NULL; float *chunk_baseline = I ? (baseline + Ndata) : NULL; if ( I ) { float *di = chunk_data; for (int i=0; i<Nd; ++i, ++di) *di = I[i]; Ndata += Nd; } if ( Nsig < 2 ) { segments[seg_ix].pieces.push_back(qubx_idlz_data_piece(0, chunk_data, chunk_pt_cls, chunk_baseline, Nd, segments[seg_ix].first + segments[seg_ix].count)); segments[seg_ix].count += Nd; return; } int max_mplex = 0; for (int i=0; i<(Nsig-1); ++i) max_mplex += dwellCounts[i]; std::vector<int> mplex_clss(max_mplex); std::vector<float> mplex_durs(max_mplex); int mplex_count = 0; mplex_idl(Nsig-1, dwellCounts, classeses, durationses, ampses, &mplex_count, & mplex_clss[0], & mplex_durs[0], stimcls_of); double sampling_ms = sampling * 1e3; int counts_so_far = 0; int points_so_far = 0; for (int i=0; i<mplex_count; ++i) { int count = (int) (mplex_durs[i] / sampling_ms + 0.5); int points = I ? count : 0; segments[seg_ix].pieces.push_back(qubx_idlz_data_piece(mplex_clss[i], points ? (chunk_data + points_so_far) : NULL, points ? (chunk_pt_cls + points_so_far) : NULL, points ? (chunk_baseline + points_so_far) : NULL, count, segments[seg_ix].first + segments[seg_ix].count + counts_so_far)); counts_so_far += count; points_so_far += points; } segments[seg_ix].count += counts_so_far; } double* get_stimclasses() { if ( stimclasses.size() ) return & stimclasses[0]; Nstim = (int) stimcls_of.size(); if ( ! Nstim ) Nstim = 1; stimclasses.resize(Nstim*Nsig); for (stimmap::iterator stimi = stimcls_of.begin(); stimi != stimcls_of.end(); ++stimi) { int i = stimi->second * Nsig; copy(stimi->first.begin(), stimi->first.end(), & stimclasses[i]); } // calculate which fraction of points are in each stimclass, for each seg and whole data sc_frac.resize(Nstim); memset(& sc_frac[0], 0, Nstim*sizeof(double)); int total = 0; for (std::vector<qubx_idlz_data_seg>::iterator segi=segments.begin(); segi!=segments.end(); ++segi) { segi->sc_frac.resize(Nstim); memset(& segi->sc_frac[0], 0, Nstim*sizeof(double)); int seg_total = 0; for (std::vector<qubx_idlz_data_piece>::iterator piece = segi->pieces.begin(); piece != segi->pieces.end(); ++piece) { if ( piece->data ) { seg_total += piece->count; segi->sc_frac[piece->stimclass] += piece->count; } } if ( seg_total ) { for (int i=0; i<Nstim; ++i) { sc_frac[i] += segi->sc_frac[i]; segi->sc_frac[i] /= seg_total; total += seg_total; } } } if ( total ) for (int i=0; i<Nstim; ++i) sc_frac[i] /= total; return & stimclasses[0]; } }; extern "C" MAXILL_API void* qubx_idlz_data_create(double sampling, int Nstimsig, int data_cap) { return new qubx_idlz_data(sampling, Nstimsig, data_cap); } extern "C" MAXILL_API void qubx_idlz_data_free(void *data) { delete (qubx_idlz_data *) data; } extern "C" MAXILL_API void qubx_idlz_data_reset(void *data, double sampling, int Nstimsig, int data_cap) { ((qubx_idlz_data *) data)->reset(sampling, Nstimsig, data_cap); } extern "C" MAXILL_API int qubx_idlz_data_append_segment(void *data, int first) { return ((qubx_idlz_data *) data)->append_segment(first); } extern "C" MAXILL_API void qubx_idlz_data_append_chunk(void *data, int seg_ix, int Nd, float *I, int *dwellCounts, int **classeses, float **durationses, double **ampses) { ((qubx_idlz_data *) data)->append_chunk(seg_ix, Nd, I, dwellCounts, classeses, durationses, ampses); } extern "C" MAXILL_API double* qubx_idlz_data_get_stimclasses(void *data, int *Nstimclass) // returns double[Nstimclass * Nsignal], Nsignal = Nstimsig + 1; { qubx_idlz_data *idldata = (qubx_idlz_data *) data; double *stimclasses = idldata->get_stimclasses(); *Nstimclass = idldata->Nstim; return stimclasses; } extern "C" MAXILL_API double* qubx_idlz_data_get_stimclass_frac(void *data) { qubx_idlz_data *idldata = (qubx_idlz_data *) data; return & idldata->sc_frac[0]; } extern "C" MAXILL_API int qubx_idlz_data_get_dwellcount(void *data, int iseg, int *ncls) { qubx_idlz_data *idldata = (qubx_idlz_data *) data; int count = 0; qubx_idlz_data_seg& seg = idldata->segments[iseg]; int maxcls = -1; for (std::vector<qubx_idlz_data_piece>::iterator piece = seg.pieces.begin(); piece != seg.pieces.end(); ++piece) { count += piece->count_dwells(); if ( piece->data ) for (int i=0; i<piece->count; ++i) if ( maxcls < piece->pt_cls[i] ) maxcls = piece->pt_cls[i]; } *ncls = maxcls + 1; return count; } extern "C" MAXILL_API void qubx_idlz_data_get_dwells(void *data, int iseg, int *ff, int *ll, int *cc, double *aa, double *ss) { qubx_idlz_data *idldata = (qubx_idlz_data *) data; int count = 0; qubx_idlz_data_seg& seg = idldata->segments[iseg]; for (std::vector<qubx_idlz_data_piece>::iterator piece = seg.pieces.begin(); piece != seg.pieces.end(); ++piece) count += piece->get_dwells(ff+count, ll+count, cc+count); int maxcls = -1; for (int i=0; i<count; ++i) if ( cc[i] > maxcls ) maxcls = cc[i]; memcpy(aa, & seg.amp[0], (maxcls+1)*sizeof(double)); memcpy(ss, & seg.std[0], (maxcls+1)*sizeof(double)); } extern "C" MAXILL_API int qubx_idlz_data_get_baseline(void *data, int iseg, int offset, int count, float *baseline) { qubx_idlz_data *idldata = (qubx_idlz_data *) data; if ( (iseg < (int) idldata->segments.size()) && idldata->segments[iseg].pieces.size() ) { memcpy(baseline, idldata->segments[iseg].pieces[0].baseline+offset, count*sizeof(float)); return count; } else { return 0; } } void qubx_idlz_set_seg_amps(qubx_stim_amps *ampm, qubx_idlz_data *data) { qubx_model *model = ampm->model; for (std::vector<qubx_idlz_data_seg>::iterator segi=data->segments.begin(); segi!=data->segments.end(); ++segi) { matrix<double> sc_frac_per_cls(model->Nclass, ampm->Nstim); memset(& sc_frac_per_cls(0,0), 0, model->Nclass*ampm->Nstim*sizeof(double)); std::vector<int> cls_total(model->Nclass, 0); for (std::vector<qubx_idlz_data_piece>::iterator piece=segi->pieces.begin(); piece!=segi->pieces.end(); ++piece) { if ( ! piece->data ) continue; int sc = piece->stimclass; for (int i=0; i<piece->count; ++i) { int cls = piece->pt_cls[i]; cls_total[cls] += 1; sc_frac_per_cls(cls, sc) += 1.0; } } for (int c=0; c<model->Nclass; ++c) { if ( cls_total[c] ) { for (int sc=0; sc<ampm->Nstim; ++sc) { sc_frac_per_cls(c, sc) /= cls_total[c]; } } } segi->amp.resize(model->Nclass); segi->std.resize(model->Nclass); for (int c=0; c<model->Nclass; ++c) { segi->amp[c] = UNSET_VALUE; double amp = 0.0; double std = 0.0; double weight = 0.0; for (int sc=0; sc<ampm->Nstim; ++sc) { double w = sc_frac_per_cls(c,sc); weight += w; amp += w * ampm->sc_amp[sc][c]; std += w * ampm->sc_std[sc][c]; } if ( weight ) { segi->amp[c] = amp; segi->std[c] = std; } } } } class qubx_idlz_halfamp_state { public: qubx_model *model; qubx_stim_amps *ampm; std::vector<double> thresh_v; std::vector<int> cls_ix_v; double *thresh; int *cls_ix; int cls_last; bool omp_stop; qubx_idlz_halfamp_state(qubx_model *model_, qubx_stim_amps *ampm_) : model(model_), ampm(ampm_), thresh_v(model_->Nclass), cls_ix_v(model_->Nclass), thresh(&(thresh_v[0])), cls_ix(&(cls_ix_v[0])), omp_stop(false) {} void setup(int sc) { double *amp = ampm->sc_amp[sc]; std::vector< pair<double, int> > cls_amp_ix; for (int c=0; c<model->Nclass; ++c) cls_amp_ix.push_back( pair<double, int>(amp[c], c) ); sort(cls_amp_ix.begin(), cls_amp_ix.end()); for (int c=0; c<(model->Nclass-1); ++c) { thresh[c] = (cls_amp_ix[c+1].first + cls_amp_ix[c].first) / 2.0; cls_ix[c] = cls_amp_ix[c].second; } cls_last = cls_amp_ix[model->Nclass-1].second; } int get_cls(double current) { for (int c=0; c<(model->Nclass-1); ++c) if ( current < thresh[c] ) return cls_ix[c]; return cls_last; } }; extern "C" MAXILL_API int qubx_idlz_halfamp(qubx_model *model, void *data, qubx_stim_amps *ampm) { //print_timestamp("halfamp..."); qubx_idlz_data *idldata = (qubx_idlz_data *) data; qubx_idlz_halfamp_state state(model, ampm); model->progress = 0; int rtn = 0; // success #pragma omp parallel if ( model->IeqFv ) { int idata = 0; for (std::vector<qubx_idlz_data_seg>::iterator segi=idldata->segments.begin(); segi!=idldata->segments.end(); ++segi) { for (std::vector<qubx_idlz_data_piece>::iterator piece=segi->pieces.begin(); piece!=segi->pieces.end(); ++piece) { if ( ! piece->data ) continue; #pragma omp single { state.setup(piece->stimclass); } #pragma omp for for (int i=0; i<piece->count; ++i) piece->pt_cls[i] = state.get_cls(piece->data[i]); #pragma omp single { idata += piece->count; model->progress = (int) (idata * 100.0 / (double) idldata->Ndata); state.omp_stop = state.omp_stop || model->stop_flag; } if ( state.omp_stop ) { rtn = -10; break; } } } } else { #pragma omp single { state.setup(0); } for (int block=0; block<idldata->Ndata; block += 65536) { int block_end = block + 65536; if ( idldata->Ndata < block_end ) block_end = idldata->Ndata; #pragma omp single { model->progress = (int) (block * 100.0 / (double) idldata->Ndata); state.omp_stop = state.omp_stop || model->stop_flag; } if ( state.omp_stop ) { rtn = -10; break; } #pragma omp for for (int i=block; i<block_end; ++i) idldata->pt_cls[i] = state.get_cls(idldata->data[i]); } } qubx_idlz_set_seg_amps(ampm, idldata); //print_timestamp("done."); return rtn; } double qubx_idlz_skm_AA_func_simplex(void *obj, double *params); double qubx_idlz_skm_amp_func_simplex(void *obj, double *params); class qubx_idlz_skm_state { public: qubx_model *model; qubx_stim_amps *ampm; qubx_stim_rates *ratesm; qubx_idlz_data *data; qubx_model_storage *st; double sampling; double resolution; int b_reest_amps, b_reest_rates, b_track_baseline; double baseline_std; std::vector< pointy_matrix<double> > AA_log, AA_r; std::vector< pointy_col_vector<double> > sc_amp_r, sc_std_r; matrix<char> best_prior; // (t, state) int t, t0_seg; matrix<double> P0_prev; std::vector< matrix<double> > P_from; qub_kalman_const *kalman_const; std::vector<qub_kalman_state *> kalman_prev, kalman_from; matrix<float> best_baseline; SampledGaussian bl_measure; int prev_sc; std::vector<double> amp_of_state; std::vector< std::vector< SampledGaussian > > measure; bool omp_stop; double progress_iterweight, progress_before; qubx_idlz_skm_state(qubx_model *model_, qubx_stim_amps *ampm_, qubx_stim_rates *ratesm_, qubx_idlz_data *data_, double resolution_, int reest_amps_, int reest_rates_, int track_baseline_, double baseline_std_) : model(model_), ampm(ampm_), ratesm(ratesm_), data(data_), st((qubx_model_storage*) model->storage), sampling(data->sampling), resolution(resolution_), b_reest_amps(reest_amps_), b_reest_rates(reest_rates_), b_track_baseline(track_baseline_), baseline_std(baseline_std_), best_prior(data->Ndata, model->Nstate), t(0), P0_prev(1, model->Nstate), P_from(model->Nstate), kalman_const(NULL), amp_of_state(model_->Nstate), measure(data->Nstim), omp_stop(false) { for (int i=0; i<model->Nstate; ++i) P_from[i].resize(1, model->Nstate); for (int sc=0; sc<data->Nstim; ++sc) measure[sc].resize(model->Nstate); AA_log.resize(data->Nstim); AA_r.resize(data->Nstim); sc_amp_r.resize(data->Nstim); sc_std_r.resize(data->Nstim); if ( model->Nstate ) { for (int sc=0; sc<data->Nstim; ++sc) { AA_log[sc].resize(model->Nstate, model->Nstate); AA_r[sc].resize(model->Nstate, model->Nstate); sc_amp_r[sc].resize(st->Nclass); sc_std_r[sc].resize(st->Nclass); } qubx_stim_rates_setup_QQ(ratesm); qubx_stim_rates_setup_AA(ratesm); } if ( b_track_baseline ) { kalman_const = qub_kalman_create(1, 1); // kalman_const.A[0][0] defaults to 1.0 (identity) deterministic process kalman_const->H[0][0] = 1.0; // baseline -> measurement kalman_const->Q[0][0] = baseline_std * baseline_std; // baseline variance kalman_const->R[0][0] = model->var0; // measurement variance for (int i=0; i<st->Nstate; ++i) { kalman_prev.push_back(qub_kalman_state_create(kalman_const)); for (int j=0; j<st->Nstate; ++j) kalman_from.push_back(qub_kalman_state_create(kalman_const)); } best_baseline.resize(data->Ndata, model->Nstate); bl_measure.setup(0.0, baseline_std, resolution, 6.0, 1024, true, true); } } virtual ~qubx_idlz_skm_state() { for (size_t i=0; i<kalman_prev.size(); ++i) qub_kalman_state_free(kalman_prev[i]); for (size_t i=0; i<kalman_from.size(); ++i) qub_kalman_state_free(kalman_from[i]); if ( kalman_const ) qub_kalman_free(kalman_const); } void setup(int sc) { for (int j=0; j<model->Nstate; ++j) measure[sc][j].setup(ampm->sc_amp[sc][model->clazz[j]], ampm->sc_std[sc][model->clazz[j]], resolution, 6.0, 1024, true, true); for (int i=0; i<model->Nstate; ++i) for (int j=0; j<model->Nstate; ++j) if ( ratesm->AA[sc][i][j] > 0.0 ) AA_log[sc].m(i,j) = log(ratesm->AA[sc][i][j]); else AA_log[sc].m(i,j) = -1e2; } void load_sc_amps(int sc) { for (int i=0; i<model->Nstate; ++i) amp_of_state[i] = ampm->sc_amp[sc][model->clazz[i]]; } void do_P0(int sc) { qubx_stim_rates_storage *ratesst = (qubx_stim_rates_storage *) ratesm->storage; prev_sc = sc; load_sc_amps(sc); if ( model->usePeq ) { QtoPe(ratesst->QQ[sc].m, P0_prev); } else { memcpy(&(P0_prev(0,0)), model->P0, model->Nstate * sizeof(double)); } for (int i=0; i<model->Nstate; ++i) if ( P0_prev(0,i) > 0 ) P0_prev(0,i) = log(P0_prev(0,i)); else P0_prev(0,i) = -1e2; } void reset() { t = 0; } void start_seg(int sc) { do_P0(sc); t0_seg = t; } void first_sample_of_seg(double y, int s) { if ( b_track_baseline ) { double baseline_prior = y - amp_of_state[s]; qub_kalman_state_reset(kalman_prev[s]); kalman_prev[s]->X[0] = baseline_prior; y -= kalman_prev[s]->X[0]; best_baseline(t,s) = (float) kalman_prev[s]->X[0]; } P0_prev(0,s) = measure[prev_sc][s](y); // TODO: += or = ? } void next_sample_to_lattice(double y, int sc, int prev_s, int next_s) { double logP_baseline = 0.0; if ( b_track_baseline ) { double baseline_prior = y - amp_of_state[next_s]; qub_kalman_state *into = kalman_from[prev_s*st->Nstate + next_s]; qub_kalman_state_next_into(into, kalman_prev[prev_s], &baseline_prior); y -= into->X[0]; logP_baseline = bl_measure(into->X[0] - kalman_prev[prev_s]->X[0]); } P_from[prev_s](0, next_s) = P0_prev(0,prev_s) + AA_log[prev_sc].m(prev_s, next_s) + measure[sc][next_s](y) + logP_baseline;; } void lattice_to_pick(int next_s) { double best_P = P_from[0](0,next_s); int best_i = 0; for (int prev_s=1; prev_s<model->Nstate; ++prev_s) { if ( P_from[prev_s](0, next_s) > best_P ) { best_P = P_from[prev_s](0, next_s); best_i = prev_s; } } best_prior(t, next_s) = (char) best_i; P0_prev(0, next_s) = best_P; if ( b_track_baseline ) { qub_kalman_state_copy(kalman_prev[next_s], kalman_from[best_i*st->Nstate + next_s]); best_baseline(t, next_s) = (float) kalman_prev[next_s]->X[0]; } } void end_sample(int sc) { prev_sc = sc; load_sc_amps(sc); ++t; } void skip_samples(int n, int sc) { // these numbers are too small for IEEE floating point, so we'll multiply with a proportional vector: double logshift = P0_prev(0,0); for (int i=1; i<model->Nstate; ++i) if ( logshift < P0_prev(0,i) ) logshift = P0_prev(0,i); logshift = - logshift - 1; for (int i=0; i<model->Nstate; ++i) P0_prev(0,i) = exp(P0_prev(0, i) + logshift); qubx_stim_rates_storage *ratesst = (qubx_stim_rates_storage *) ratesm->storage; ratesst->setup_A(sc, n*sampling); P0_prev = prod(P0_prev, ratesst->A.m); for (int i=0; i<model->Nstate; ++i) P0_prev(0, i) = log(P0_prev(0, i)) - logshift; } double end_seg() { int s_final = 0; // s = argmax_j( P0_prev(0,j) ); double best_P = P0_prev(0, s_final); for (int j=1; j<model->Nstate; ++j) { if ( best_P < P0_prev(0, j) ) { best_P = P0_prev(0, j); s_final = j; } } int s = s_final; for (int tt=t-1; tt>t0_seg; --tt) { data->pt_cls[tt] = s; s = best_prior(tt, s); } data->pt_cls[t0_seg] = s; if ( b_track_baseline ) { int *ss = data->pt_cls + t-1; for (int tt=t-1; tt>t0_seg; --tt) { data->baseline[tt] = best_baseline(tt,*(ss--)); } data->baseline[t0_seg] = best_baseline(t0_seg,s); } return best_P; } void avg_sc_AA() { for (int sc=0; sc<ratesm->Nstim; ++sc) if ( data->sc_frac[sc] ) AA_r[sc].clear(); matrix<int> sc_from_count(ratesm->Nstim, model->Nstate); sc_from_count.clear(); for (std::vector<qubx_idlz_data_seg>::iterator segi=data->segments.begin(); segi!=data->segments.end(); ++segi) { for (std::vector<qubx_idlz_data_piece>::iterator piece=segi->pieces.begin(); piece!=segi->pieces.end(); ++piece) { if ( ! piece->data ) continue; int sc = piece->stimclass; int from = piece->pt_cls[0]; for (int i=1; i<piece->count; ++i) { AA_r[sc].m(from, piece->pt_cls[i]) += 1.0; sc_from_count(sc, from) += 1; from = piece->pt_cls[i]; } } } for (int sc=0; sc<ratesm->Nstim; ++sc) { if ( data->sc_frac[sc] ) { for (int i=0; i<model->Nstate; ++i) { if ( sc_from_count(sc, i) ) { double scale = 1.0 / sc_from_count(sc, i); for (int j=0; j<model->Nstate; ++j) AA_r[sc].m(i,j) *= scale; } } } } } bool reest_k() { avg_sc_AA(); qubx_stim_rates_setup_constraints(ratesm); simplex(qubx_idlz_skm_AA_func_simplex, this, ratesm->cns->fPars, ratesm->cns->NfPar, 1e-9, 1.0, QUBX_IDLZ_AA_MAXITER); return true; } double AA_func_simplex(double *params) { memcpy(ratesm->cns->fPars, params, ratesm->cns->NfPar*sizeof(double)); qubx_constrained_free_to_pars(ratesm->cns); qubx_stim_rates_pars_to_model(ratesm); return AA_eval(); } double AA_eval() { double ssd = 0.0; for (int sc=0; sc<ratesm->Nstim; ++sc) { if ( data->sc_frac[sc] ) { double sc_ssd = 0.0; for (int i=0; i<model->Nstate; ++i) { for (int j=0; j<model->Nstate; ++j) { double diff = ratesm->AA[sc][i][j] - AA_r[sc].m(i,j); sc_ssd += diff * diff; } } ssd += data->sc_frac[sc] * sc_ssd; } } return ssd; } void avg_sc_amps() { if ( model->IeqFv && model->iVoltage ) { std::vector<RunningMean> avger(ampm->Nstim * model->Nclass); for (std::vector<qubx_idlz_data_seg>::iterator segi=data->segments.begin(); segi!=data->segments.end(); ++segi) { for (std::vector<qubx_idlz_data_piece>::iterator piece=segi->pieces.begin(); piece!=segi->pieces.end(); ++piece) { if ( ! piece->data ) continue; int sc_off = piece->stimclass * model->Nclass; for (int i=0; i<piece->count; ++i) avger[sc_off + piece->pt_cls[i]].add( piece->data[i] - piece->baseline[i] ); } } for (int sc=0; sc<ampm->Nstim; ++sc) { int sc_off = sc * model->Nclass; for (int cls=0; cls<model->Nclass; ++cls) { sc_amp_r[sc].p[cls] = avger[sc_off + cls].mean; sc_std_r[sc].p[cls] = avger[sc_off + cls].std(); } } } else { // no v-sensitive amps std::vector<RunningMean> avger(model->Nclass); for (int i=0; i<data->Ndata; ++i) avger[data->pt_cls[i]].add( data->data[i] - data->baseline[i] ); for (int cls=0; cls<model->Nclass; ++cls) { sc_amp_r[0].p[cls] = avger[cls].mean; sc_std_r[0].p[cls] = avger[cls].std(); } } } bool reest_amps() { avg_sc_amps(); if ( model->IeqFv && model->iVoltage ) { qubx_stim_amps_model_to_pars(ampm); qubx_constrained_setup(ampm->cns); simplex(qubx_idlz_skm_amp_func_simplex, this, ampm->cns->fPars, ampm->cns->NfPar, 1e-9, 1.0, QUBX_IDLZ_AMPS_MAXITER); } else { // no v-sensitive amps: all collected in _r[sc=0] memcpy(st->amp.p, sc_amp_r[0].p, st->Nclass*sizeof(double)); for (int c=0; c<st->Nclass; ++c) st->var.p[c] = sc_std_r[0].p[c] * sc_std_r[0].p[c]; qubx_stim_amps_setup_scs(ampm); } return true; } double amp_eval(double *params) { memcpy(ampm->cns->fPars, params, ampm->cns->NfPar * sizeof(double)); qubx_constrained_free_to_pars(ampm->cns); qubx_stim_amps_pars_to_model(ampm); qubx_stim_amps_setup_scs(ampm); double ssd = 0.0; for (int sc=0; sc<data->Nstim; ++sc) { if ( data->sc_frac[sc] ) { double sc_ssd = 0.0; for (int i=0; i<model->Nclass; ++i) { double diff = ampm->sc_amp[sc][i] - sc_amp_r[sc].p[i]; sc_ssd += diff * diff; diff = ampm->sc_std[sc][i] - sc_std_r[sc].p[i]; sc_ssd += diff * diff; } ssd += data->sc_frac[sc] * sc_ssd; } } return ssd; } }; double qubx_idlz_skm_AA_func_simplex(void *obj, double *params) { return ((qubx_idlz_skm_state *) obj)->AA_func_simplex(params); } double qubx_idlz_skm_amp_func_simplex(void *obj, double *params) { return ((qubx_idlz_skm_state *) obj)->amp_eval(params); } double qubx_idlz_skm_once(qubx_model *model, qubx_idlz_data *idldata, qubx_idlz_skm_state& state) { double ll = 0.0; int idata = 0; state.reset(); //print_timestamp("setup sc..."); #pragma omp parallel reduction (+: ll) { #pragma omp for for (int sc=0; sc<idldata->Nstim; ++sc) state.setup(sc); //print_timestamp("seg/piece/point loop..."); for (std::vector<qubx_idlz_data_seg>::iterator segi=idldata->segments.begin(); segi!=idldata->segments.end(); ++segi) { bool first_sample = true; for (std::vector<qubx_idlz_data_piece>::iterator piece=segi->pieces.begin(); piece!=segi->pieces.end(); ++piece) { if ( ! state.omp_stop ) { int sc = piece->stimclass; int n = piece->count; if ( piece == segi->pieces.begin() ) { #pragma omp single { state.start_seg(sc); } } if ( ! piece->data ) { #pragma omp single { state.skip_samples(n, sc); } } else if ( n ) { int i = 0; if ( first_sample ) { #pragma omp for for (int s=0; s<model->Nstate; ++s) state.first_sample_of_seg(piece->data[0], s); #pragma omp single { state.end_sample(sc); ++idata; } i = 1; first_sample = false; } for (; i<n && ! state.omp_stop; ++i) { double y = piece->data[i]; #pragma omp for for (int j=model->Nstate*model->Nstate-1; j>=0; --j) { state.next_sample_to_lattice(y, sc, j/model->Nstate, j%model->Nstate); } #pragma omp for for (int j=model->Nstate-1; j>=0; --j) { state.lattice_to_pick(j); } #pragma omp single { state.end_sample(sc); state.omp_stop = state.omp_stop || model->stop_flag; ++idata; if ( ! (idata % 16384) ) model->progress = (int) (state.progress_before + 100 * idata * state.progress_iterweight / idldata->Ndata); } } } } } #pragma omp single { if ( ! state.omp_stop ) ll += state.end_seg(); } } } if ( state.omp_stop ) { for (int i=0; i<idldata->Ndata; ++i) idldata->pt_cls[i] = 0; } else { // before converting state index to class, summarize the state-to-state transitions: if ( state.b_reest_rates ) { //print_timestamp("reestimating Ks..."); state.reest_k(); } //print_timestamp("recovering class sequence..."); int *clazz = state.st->clazz.p; for (int i=0; i<idldata->Ndata; ++i) { //cerr << i << ": " << idldata->pt_cls[i] << endl; idldata->pt_cls[i] = clazz[idldata->pt_cls[i]]; } // now get stats on class amp and std if ( state.b_reest_amps ) { //print_timestamp("reestimating amps..."); state.reest_amps(); // bool std?); } } return ll; } extern "C" MAXILL_API int qubx_idlz_skm(qubx_model *model, void *data, qubx_stim_amps *ampm, qubx_stim_rates *ratesm, double resolution, int method, int reest_amps, int reest_rates, int track_baseline, double baseline_std, double *ll_out) { *ll_out = 0.0; model->progress = 0; if ( model->stop_flag ) return -10; //print_timestamp("setup skm..."); qubx_idlz_data *idldata = (qubx_idlz_data *) data; qubx_idlz_skm_state state(model, ampm, ratesm, idldata, resolution, reest_amps, reest_rates, track_baseline, baseline_std); int rtn = 0; // success if ( method == 2/*SKM*/ ) { double prev_ll = -1e100; int maxiter = 7; state.progress_iterweight = 1.0 / maxiter; double conv_ll = 1e-8 * idldata->Ndata; for (int iter=0; iter<maxiter && !model->stop_flag; ++iter) { state.progress_before = iter * state.progress_iterweight; double ll = qubx_idlz_skm_once(model, idldata, state); model->progress = (int) (state.progress_before + state.progress_iterweight); cerr << "ll: " << ll << endl; if ( (ll - prev_ll) < conv_ll ) break; *ll_out = prev_ll = ll; } } else { state.progress_iterweight = 1.0; state.progress_before = 0.0; *ll_out = qubx_idlz_skm_once(model, idldata, state); } if ( model->stop_flag ) { rtn = -10; } else { //print_timestamp("averaging seg amps..."); qubx_idlz_set_seg_amps(ampm, idldata); } //print_timestamp("done."); return rtn; } // note: don't use openmp; it makes it slower (at least on my 2-core).