#pragma once
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <vector>
#include <utility>
#include <tuple>
#include "aligned_ptr.h"
#include "isoSpec++.h"
#ifdef DEBUG
#define ISOSPEC_INIT_TABLE_SIZE 16
#else
#define ISOSPEC_INIT_TABLE_SIZE 1024
#endif
#ifndef ISOSPEC_BINNED_LAYER_MAXSTEP
# define ISOSPEC_BINNED_LAYER_MAXSTEP (-3.0)
#endif
#ifndef ISOSPEC_TOTALPROB_LAYER_MAXSTEP
# define ISOSPEC_TOTALPROB_LAYER_MAXSTEP (-5.0)
#endif
namespace IsoSpec
{
class FixedEnvelope;
struct ISOSPEC_EXPORT_SYMBOL ReleasedArray {
void* ptr;
std::size_t size;
void (*deleter)(void*, std::size_t) noexcept;
};
class ISOSPEC_EXPORT_SYMBOL FixedEnvelope {
protected:
double* _masses;
double* _probs;
int* _confs;
size_t _confs_no;
int allDim;
bool sorted_by_mass;
bool sorted_by_prob;
double total_prob;
size_t current_size;
double* tmasses;
double* tprobs;
int* tconfs;
int allDimSizeofInt;
aligned_unique_ptr<double, DOUBLE_SIMD_ALIGNMENT> _masses_owner;
aligned_unique_ptr<double, DOUBLE_SIMD_ALIGNMENT> _probs_owner;
aligned_unique_ptr<int, DOUBLE_SIMD_ALIGNMENT> _confs_owner;
static void free_deleter(void* p, std::size_t) noexcept { free(p); }
template<typename T, std::size_t A>
static void free_array(aligned_unique_ptr<T, A>& owner, T*& ptr) noexcept
{
if(owner)
owner.reset(0);
else
free(ptr);
ptr = nullptr;
}
template<typename T, std::size_t A>
static T* release_array(aligned_unique_ptr<T, A>& owner, T*& ptr)
{
T* ret = owner ? owner.release() : ptr;
ptr = nullptr;
return ret;
}
template<typename T, std::size_t A>
static ReleasedArray release_array_with_deleter(aligned_unique_ptr<T, A>& owner, T*& ptr) noexcept
{
ReleasedArray ret;
if(owner)
{
typename aligned_unique_ptr<T, A>::release_result r = owner.release_with_deleter();
ret = ReleasedArray{static_cast<void*>(r.ptr), r.size, r.deleter};
}
else
ret = ReleasedArray{static_cast<void*>(ptr), 0, &free_deleter};
ptr = nullptr;
return ret;
}
template<typename T, std::size_t A>
static void disown_array(aligned_unique_ptr<T, A>& owner, T*& ptr) noexcept
{
if(owner)
(void)owner.release_with_deleter(); ptr = nullptr;
}
template<typename T, std::size_t A>
static void fresh_array(aligned_unique_ptr<T, A>& owner, T*& ptr, size_t new_n)
{
if(!owner && ptr != nullptr)
{
free(ptr);
ptr = nullptr;
}
owner.reset(new_n);
ptr = owner.get();
}
template<typename T, std::size_t A>
static void grow_array(aligned_unique_ptr<T, A>& owner, T*& ptr, size_t new_n, size_t old_n)
{
if(!owner && ptr != nullptr)
{
aligned_unique_ptr<T, A> fresh;
fresh.realloc(new_n);
const size_t keep = (std::min)(old_n, new_n);
if(keep > 0)
memcpy(fresh.get(), ptr, sizeof(T) * keep);
free(ptr);
owner = std::move(fresh);
}
else
owner.realloc(new_n);
ptr = owner.get();
}
public:
ISOSPEC_FORCE_INLINE FixedEnvelope() : _masses(nullptr),
_probs(nullptr),
_confs(nullptr),
_confs_no(0),
allDim(0),
sorted_by_mass(false),
sorted_by_prob(false),
total_prob(NAN),
current_size(0),
allDimSizeofInt(0)
{};
FixedEnvelope(const FixedEnvelope& other);
FixedEnvelope(FixedEnvelope&& other);
FixedEnvelope& operator=(const FixedEnvelope& other);
FixedEnvelope& operator=(FixedEnvelope&& other);
FixedEnvelope(double* masses, double* probs, size_t confs_no, bool masses_sorted = false, bool probs_sorted = false, double _total_prob = NAN);
FixedEnvelope(double* masses, double* probs, int* confs, size_t confs_no, int _allDim, bool masses_sorted = false, bool probs_sorted = false, double _total_prob = NAN);
FixedEnvelope(aligned_unique_ptr<double, DOUBLE_SIMD_ALIGNMENT>&& masses,
aligned_unique_ptr<double, DOUBLE_SIMD_ALIGNMENT>&& probs,
size_t confs_no, bool masses_sorted = false, bool probs_sorted = false, double _total_prob = NAN);
virtual ~FixedEnvelope()
{
free_array(_masses_owner, _masses);
free_array(_probs_owner, _probs);
free_array(_confs_owner, _confs);
};
FixedEnvelope operator+(const FixedEnvelope& other) const;
FixedEnvelope operator*(const FixedEnvelope& other) const;
inline size_t confs_no() const { return _confs_no; }
inline int getAllDim() const { return allDim; }
inline const double* masses() const { return _masses; }
inline const double* probs() const { return _probs; }
inline const int* confs() const { return _confs; }
inline double* release_masses() { return release_array(_masses_owner, _masses); }
inline double* release_probs() { return release_array(_probs_owner, _probs); }
inline int* release_confs() { return release_array(_confs_owner, _confs); }
inline ReleasedArray release_masses_with_deleter() { return release_array_with_deleter(_masses_owner, _masses); }
inline ReleasedArray release_probs_with_deleter() { return release_array_with_deleter(_probs_owner, _probs); }
inline ReleasedArray release_confs_with_deleter() { return release_array_with_deleter(_confs_owner, _confs); }
inline void release_everything() { disown_array(_masses_owner, _masses); disown_array(_probs_owner, _probs); disown_array(_confs_owner, _confs); }
inline double mass(size_t i) const { return _masses[i]; }
inline double prob(size_t i) const { return _probs[i]; }
inline const int* conf(size_t i) const { return _confs + i*allDim; }
void sort_by_mass();
void sort_by_prob();
double get_total_prob();
void scale(double factor);
void normalize();
void shift_mass(double shift);
void resample(size_t ionic_current, double beta_bias = 1.0, std::mt19937& rng = random_gen);
double empiric_average_mass();
double empiric_variance();
double empiric_stddev() { return sqrt(empiric_variance()); }
double WassersteinDistance(FixedEnvelope& other);
double OrientedWassersteinDistance(FixedEnvelope& other);
double AbyssalWassersteinDistance(FixedEnvelope& other, double abyss_depth, double other_scale = 1.0);
std::tuple<double, double, double> WassersteinMatch(FixedEnvelope& other, double flow_distance, double other_scale = 1.0);
static FixedEnvelope LinearCombination(const std::vector<const FixedEnvelope*>& spectra, const std::vector<double>& intensities);
static FixedEnvelope LinearCombination(const FixedEnvelope* const * spectra, const double* intensities, size_t size);
FixedEnvelope bin(double bin_width = 1.0, double middle = 0.0);
static FixedEnvelope FromArrays(const double* masses, const double* probs, size_t confs_no);
private:
void sort_by(double* order);
protected:
template<typename T, bool tgetConfs> ISOSPEC_FORCE_INLINE void store_conf(const T& generator)
{
*tmasses = generator.mass(); tmasses++;
*tprobs = generator.prob(); tprobs++;
constexpr_if(tgetConfs) { generator.get_conf_signature(tconfs); tconfs += allDim; }
}
ISOSPEC_FORCE_INLINE void store_conf(double _mass, double _prob)
{
if(_confs_no == current_size)
reallocate_memory<false>(current_size*2);
*tprobs = _prob;
*tmasses = _mass;
tprobs++;
tmasses++;
_confs_no++;
}
template<bool tgetConfs> ISOSPEC_FORCE_INLINE void swap(size_t idx1, size_t idx2, ISOSPEC_MAYBE_UNUSED int* conf_swapspace)
{
std::swap<double>(this->_probs[idx1], this->_probs[idx2]);
std::swap<double>(this->_masses[idx1], this->_masses[idx2]);
constexpr_if(tgetConfs)
{
int* c1 = this->_confs + (idx1*this->allDim);
int* c2 = this->_confs + (idx2*this->allDim);
memcpy(conf_swapspace, c1, this->allDimSizeofInt);
memcpy(c1, c2, this->allDimSizeofInt);
memcpy(c2, conf_swapspace, this->allDimSizeofInt);
}
}
template<bool tgetConfs> void reallocate_memory(size_t new_size);
template<bool tgetConfs> void allocate_memory(size_t new_size);
size_t store_run(IsoLayeredGenerator& generator, isa_fill_run_fn fill_run);
template<bool tgetConfs> void store_layer(IsoLayeredGenerator& generator);
template<bool tgetConfs> bool store_layer_to_prob(IsoLayeredGenerator& generator,
double& prob_so_far,
double target_total_prob);
public:
template<bool tgetConfs> void threshold_init(Iso&& iso, double threshold, bool absolute);
template<bool tgetConfs, typename GenType = IsoLayeredGenerator> void addConfILG(const GenType& generator)
{
if(this->_confs_no == this->current_size)
this->template reallocate_memory<tgetConfs>(this->current_size*2);
this->template store_conf<GenType, tgetConfs>(generator);
this->_confs_no++;
}
template<bool tgetConfs> void total_prob_init(Iso&& iso, double target_prob, bool trim);
static FixedEnvelope FromThreshold(Iso&& iso, double threshold, bool absolute, bool tgetConfs = false)
{
FixedEnvelope ret;
if(tgetConfs)
ret.threshold_init<true>(std::move(iso), threshold, absolute);
else
ret.threshold_init<false>(std::move(iso), threshold, absolute);
return ret;
}
inline static FixedEnvelope FromThreshold(const Iso& iso, double _threshold, bool _absolute, bool tgetConfs = false)
{
return FromThreshold(Iso(iso, true), _threshold, _absolute, tgetConfs);
}
static FixedEnvelope FromTotalProb(Iso&& iso, double target_total_prob, bool optimize, bool tgetConfs = false)
{
FixedEnvelope ret;
if(tgetConfs)
ret.total_prob_init<true>(std::move(iso), target_total_prob, optimize);
else
ret.total_prob_init<false>(std::move(iso), target_total_prob, optimize);
return ret;
}
inline static FixedEnvelope FromTotalProb(const Iso& iso, double _target_total_prob, bool _optimize, bool tgetConfs = false)
{
return FromTotalProb(Iso(iso, true), _target_total_prob, _optimize, tgetConfs);
}
template<bool tgetConfs> void stochastic_init(Iso&& iso, size_t _no_molecules, double _precision, double _beta_bias, std::mt19937& rng);
inline static FixedEnvelope FromStochastic(Iso&& iso, size_t _no_molecules, double _precision = 0.9999, double _beta_bias = 5.0, bool tgetConfs = false, std::mt19937& rng = random_gen)
{
FixedEnvelope ret;
if(tgetConfs)
ret.stochastic_init<true>(std::move(iso), _no_molecules, _precision, _beta_bias, rng);
else
ret.stochastic_init<false>(std::move(iso), _no_molecules, _precision, _beta_bias, rng);
return ret;
}
static FixedEnvelope FromStochastic(const Iso& iso, size_t _no_molecules, double _precision = 0.9999, double _beta_bias = 5.0, bool tgetConfs = false, std::mt19937& rng = random_gen)
{
return FromStochastic(Iso(iso, true), _no_molecules, _precision, _beta_bias, tgetConfs, rng);
}
static FixedEnvelope Binned(Iso&& iso, double target_total_prob, double bin_width, double bin_middle = 0.0);
static FixedEnvelope Binned(const Iso& iso, double target_total_prob, double bin_width, double bin_middle = 0.0)
{
return Binned(Iso(iso, true), target_total_prob, bin_width, bin_middle);
}
};
}