Program Listing for File fitness_models.hpp¶
↰ Return to documentation for file (fwdpp/fitness_models.hpp)
#ifndef _FITNESS_MODELS_HPP_
#define _FITNESS_MODELS_HPP_
#include <fwdpp/forward_types.hpp>
#include <fwdpp/fwd_functional.hpp>
#include <fwdpp/type_traits.hpp>
#include <fwdpp/util/named_type.hpp>
#include <cmath>
#include <stdexcept>
#include <type_traits>
#include <algorithm>
#include <functional>
/*
Revision history
KRT June 9 2016:
1. Added range-based operator() to additive/multiplicative models.
1a. I have not (yet) made these new operator() called by the old ones taking
haploid_genomes as args.
2. Updated documentation for site_dependent_genetic_value
3. Changed typedef for result_type in additive/multiplicative models to refer
to value from site_dependent_genetic_value
KRT June 8 2016:
1. Added range-based operator() to site_dependent_genetic_value.
2. Changed site_dependent_genetic_value::operator() that took haploid_genomes as
arguments
to call the new range-based function.
3. Fixed site_dependent_genetic_value::operator() for custom diploids.
This had never been updated from the iterator-based library to the
index-based library,
but it was never caught b/c most calls go through the specific
multiplicative/additive models.
KRT April 24 2017
1. additive_diploid and multiplicative_diploid changed to
accept closures allowing them to calculate "fitness" or "trait value"
more naturally. This is in response to #49 on github. This change
does not break API compatibility. The default is still to calculate
"fitness".
*/
namespace fwdpp
{
struct no_selection
{
using result_type = double;
template <typename HaploidGenomeType, typename MutationContainerType>
inline result_type
operator()(const HaploidGenomeType &, const HaploidGenomeType &,
const MutationContainerType &) const noexcept
{
static_assert(traits::is_haploid_genome<HaploidGenomeType>::value,
"HaploidGenomeType::value_type must be a haploid_genome "
"type");
static_assert(
traits::is_mutation<typename MutationContainerType::value_type>::value,
"MutationContainerType::value_type must be a mutation type");
return 1.;
}
template <typename T>
inline result_type
operator()(const T &) const noexcept
{
return 1.;
}
};
struct site_dependent_genetic_value
{
using result_type = double;
template <typename iterator_t, typename MutationContainerType,
typename updating_policy_hom, typename updating_policy_het,
typename make_return_value>
inline result_type
operator()(iterator_t first1, iterator_t last1, iterator_t first2,
iterator_t last2, const MutationContainerType &mutations,
const updating_policy_hom &fpol_hom,
const updating_policy_het &fpol_het,
const make_return_value &rv_function,
const double starting_value) const noexcept
{
static_assert(
traits::is_mutation<typename MutationContainerType::value_type>::value,
"MutationContainerType::value_type must be a mutation type");
static_assert(
std::is_convertible<
updating_policy_hom,
std::function<void(double &, const typename MutationContainerType::
value_type &)>>::value,
"decltype(fpol_hom) must be convertible to "
"std::function<void(double &,const typename "
"MutationContainerType::value_type");
static_assert(
std::is_convertible<
updating_policy_het,
std::function<void(double &, const typename MutationContainerType::
value_type &)>>::value,
"decltype(fpol_het) must be convertible to "
"std::function<void(double &,const typename "
"MutationContainerType::value_type");
result_type w = starting_value;
if (first1 == last1 && first2 == last2)
return rv_function(w);
else if (first1 == last1)
{
for (; first2 != last2; ++first2)
fpol_het(w, mutations[*first2]);
return rv_function(w);
}
else if (first2 == last2)
{
for (; first1 != last1; ++first1)
fpol_het(w, mutations[*first1]);
return rv_function(w);
}
for (; first1 != last1; ++first1)
{
for (; first2 != last2 && *first1 != *first2
&& mutations[*first2].pos < mutations[*first1].pos;
++first2)
// All mutations in this range are Aa
{
fpol_het(w, mutations[*first2]);
}
if (first2 < last2
&& (*first1 == *first2
|| mutations[*first1].pos == mutations[*first2].pos))
// mutation with index first1 is homozygous
{
fpol_hom(w, mutations[*first1]);
++first2; // increment so that we don't re-process
// this site as a het next time 'round
}
else // mutation first1 is heterozygous
{
fpol_het(w, mutations[*first1]);
}
}
for (; first2 != last2; ++first2)
{
fpol_het(w, mutations[*first2]);
}
return rv_function(w);
}
template <typename iterator_t, typename MutationContainerType,
typename updating_policy_hom, typename updating_policy_het>
inline result_type
operator()(iterator_t first1, iterator_t last1, iterator_t first2,
iterator_t last2, const MutationContainerType &mutations,
const updating_policy_hom &fpol_hom,
const updating_policy_het &fpol_het,
const double starting_value) const noexcept
{
return this->operator()(
first1, last1, first2, last2, mutations, fpol_hom, fpol_het,
[](double d) { return d; }, starting_value);
}
template <typename HaploidGenomeType, typename MutationContainerType,
typename updating_policy_hom, typename updating_policy_het,
typename make_return_value>
inline result_type
operator()(const HaploidGenomeType &g1, const HaploidGenomeType &g2,
const MutationContainerType &mutations,
const updating_policy_hom &fpol_hom,
const updating_policy_het &fpol_het,
const make_return_value &rv_function,
const double starting_value) const noexcept
{
static_assert(traits::is_haploid_genome<HaploidGenomeType>::value,
"HaploidGenomeType::value_type must be a haploid_genome "
"type");
return this->operator()(g1.smutations.cbegin(), g1.smutations.cend(),
g2.smutations.cbegin(), g2.smutations.cend(),
mutations, fpol_hom, fpol_het, rv_function,
starting_value);
}
template <typename HaploidGenomeType, typename MutationContainerType,
typename updating_policy_hom, typename updating_policy_het>
inline result_type
operator()(const HaploidGenomeType &g1, const HaploidGenomeType &g2,
const MutationContainerType &mutations,
const updating_policy_hom &fpol_hom,
const updating_policy_het &fpol_het,
const double starting_value) const noexcept
{
return this->operator()(
g1, g2, mutations, fpol_hom, fpol_het, [](double d) { return d; },
starting_value);
}
template <typename DiploidType, typename GenomeContainerType,
typename MutationContainerType, typename updating_policy_hom,
typename updating_policy_het, typename make_return_value>
inline result_type
operator()(const DiploidType &dip, const GenomeContainerType &haploid_genomes,
const MutationContainerType &mutations,
const updating_policy_hom &fpol_hom,
const updating_policy_het &fpol_het,
const make_return_value &rv_function,
const double starting_value) const noexcept
{
return this->operator()(haploid_genomes[dip.first],
haploid_genomes[dip.second], mutations, fpol_hom,
fpol_het, rv_function, starting_value);
}
template <typename DiploidType, typename GenomeContainerType,
typename MutationContainerType, typename updating_policy_hom,
typename updating_policy_het>
inline result_type
operator()(const DiploidType &dip, const GenomeContainerType &haploid_genomes,
const MutationContainerType &mutations,
const updating_policy_hom &fpol_hom,
const updating_policy_het &fpol_het,
const double starting_value) const noexcept
{
return this->operator()(
dip, haploid_genomes, mutations, fpol_hom, fpol_het,
[](double d) { return d; }, starting_value);
}
};
using site_dependent_fitness = site_dependent_genetic_value;
struct haplotype_dependent_trait_value
{
using result_type = double;
template <typename HaploidGenomeType, typename MutationContainerType,
typename haplotype_policy, typename DiploidTypePolicy>
inline result_type
operator()(const HaploidGenomeType &g1, const HaploidGenomeType &g2,
const MutationContainerType &mutations, const haplotype_policy &hpol,
const DiploidTypePolicy &dpol) const noexcept
{
static_assert(traits::is_haploid_genome_v<HaploidGenomeType>,
"HaploidGenomeType must be a haploid_genome type");
static_assert(
traits::is_mutation<typename MutationContainerType::value_type>::value,
"MutationContainerType::value_type must be a mutation type");
return dpol(hpol(g1, mutations), hpol(g2, mutations));
}
template <typename DiploidType, typename GenomeContainerType,
typename MutationContainerType, typename haplotype_policy,
typename DiploidTypePolicy>
inline result_type
operator()(const DiploidType &diploid,
const GenomeContainerType &haploid_genomes,
const MutationContainerType &mutations, const haplotype_policy &hpol,
const DiploidTypePolicy &dpol) const noexcept
{
static_assert(traits::is_diploid<DiploidType>::value,
"diploid_t must represent a diploid");
return this->operator()(haploid_genomes[diploid.first],
haploid_genomes[diploid.second], mutations, hpol,
dpol);
}
};
using haplotype_dependent_fitness = haplotype_dependent_trait_value;
struct genetic_value_is_trait
{
};
struct genetic_value_is_fitness
{
};
using trait = strong_types::named_type<double, genetic_value_is_trait>;
using fitness = strong_types::named_type<double, genetic_value_is_fitness>;
inline bool
assign_is_trait_value(const trait &)
{
return true;
}
inline bool
assign_is_trait_value(const fitness &)
{
return false;
}
inline bool
assign_is_fitness_value(const trait &)
{
return false;
}
inline bool
assign_is_fitness_value(const fitness &)
{
return true;
}
struct multiplicative_diploid
{
std::function<double(double)>
assign_f(trait &)
{
return [](const double d) { return d - 1.0; };
}
std::function<double(double)>
assign_f(fitness &)
{
return [](const double d) { return std::max(0.0, d); };
}
const double scaling;
const bool gvalue_is_trait;
const bool gvalue_is_fitness;
using result_type = site_dependent_genetic_value::result_type;
const std::function<double(double)> make_return_value;
multiplicative_diploid(trait t)
: scaling{t.get()}, gvalue_is_trait(assign_is_trait_value(t)),
gvalue_is_fitness(assign_is_fitness_value(t)), make_return_value{
assign_f(t)}
{
if (!std::isfinite(scaling))
{
throw std::invalid_argument("scaling parameter must be finite");
}
}
template <typename make_return_value_fxn>
multiplicative_diploid(fitness f, make_return_value_fxn &&make_rv)
: scaling{f.get()}, gvalue_is_trait{assign_is_trait_value(f)},
gvalue_is_fitness{assign_is_fitness_value(f)},
make_return_value{std::forward<make_return_value_fxn>(make_rv)}
{
if (!std::isfinite(scaling))
{
throw std::invalid_argument("scaling parameter must be finite");
}
}
multiplicative_diploid(fitness gvtype)
: scaling{gvtype.get()}, gvalue_is_trait(assign_is_trait_value(gvtype)),
gvalue_is_fitness(assign_is_fitness_value(gvtype)), make_return_value{
assign_f(gvtype)}
{
if (!std::isfinite(scaling))
{
throw std::invalid_argument("scaling parameter must be finite");
}
}
template <typename make_return_value_fxn>
multiplicative_diploid(trait t, make_return_value_fxn &&make_rv)
: scaling{t.get()}, gvalue_is_trait{assign_is_trait_value(t)},
gvalue_is_fitness{assign_is_fitness_value(t)},
make_return_value{std::forward<make_return_value_fxn>(make_rv)}
{
if (!std::isfinite(scaling))
{
throw std::invalid_argument("scaling parameter must be finite");
}
}
template <typename iterator_t, typename MutationContainerType>
inline result_type
operator()(iterator_t first1, iterator_t last1, iterator_t first2,
iterator_t last2, const MutationContainerType &mutations) const
noexcept
{
using __mtype = typename MutationContainerType::value_type;
return site_dependent_genetic_value()(
first1, last1, first2, last2, mutations,
[this](double &value, const __mtype &mut) noexcept {
value *= (1. + scaling * mut.s);
},
[](double &value, const __mtype &mut) noexcept {
value *= (1. + mut.h * mut.s);
},
make_return_value, 1.);
}
template <typename HaploidGenomeType, typename MutationContainerType>
inline double
operator()(const HaploidGenomeType &g1, const HaploidGenomeType &g2,
const MutationContainerType &mutations) const noexcept
{
using __mtype = typename MutationContainerType::value_type;
return site_dependent_genetic_value()(
g1, g2, mutations,
[this](double &value, const __mtype &mut) noexcept {
value *= (1. + scaling * mut.s);
},
[](double &value, const __mtype &mut) noexcept {
value *= (1. + mut.h * mut.s);
},
make_return_value, 1.);
}
template <typename DiploidType, typename GenomeContainerType,
typename MutationContainerType>
inline result_type
operator()(const DiploidType &dip, const GenomeContainerType &haploid_genomes,
const MutationContainerType &mutations) const noexcept
{
return this->operator()(haploid_genomes[dip.first],
haploid_genomes[dip.second], mutations);
}
};
struct additive_diploid
{
std::function<double(double)> assign_f(trait)
{
return [](const double d) { return d; };
}
std::function<double(double)> assign_f(fitness)
{
return [](const double d) { return std::max(0.0, 1.0 + d); };
}
const double scaling;
const bool gvalue_is_trait;
const bool gvalue_is_fitness;
const std::function<double(double)> make_return_value;
using result_type = site_dependent_genetic_value::result_type;
additive_diploid(fitness f)
: scaling{f.get()}, gvalue_is_trait{assign_is_trait_value(f)},
gvalue_is_fitness{assign_is_fitness_value(f)}, make_return_value{
assign_f(f)}
{
if (!std::isfinite(scaling))
{
throw std::invalid_argument("scaling parameter must be finite");
}
}
template <typename make_return_value_fxn>
additive_diploid(fitness f, make_return_value_fxn &&make_rv)
: scaling{f.get()}, gvalue_is_trait{assign_is_trait_value(f)},
gvalue_is_fitness{assign_is_fitness_value(f)},
make_return_value{std::forward<make_return_value_fxn>(make_rv)}
{
if (!std::isfinite(scaling))
{
throw std::invalid_argument("scaling parameter must be finite");
}
}
additive_diploid(trait t)
: scaling{t.get()}, gvalue_is_trait{assign_is_trait_value(t)},
gvalue_is_fitness{assign_is_fitness_value(t)}, make_return_value{
assign_f(t)}
{
if (!std::isfinite(scaling))
{
throw std::invalid_argument("scaling parameter must be finite");
}
}
template <typename make_return_value_fxn>
additive_diploid(trait t, make_return_value_fxn &&make_rv)
: scaling{t.get()}, gvalue_is_trait{assign_is_trait_value(t)},
gvalue_is_fitness{assign_is_fitness_value(t)},
make_return_value{std::forward<make_return_value_fxn>(make_rv)}
{
if (!std::isfinite(scaling))
{
throw std::invalid_argument("scaling parameter must be finite");
}
}
template <typename iterator_t, typename MutationContainerType>
inline result_type
operator()(iterator_t first1, iterator_t last1, iterator_t first2,
iterator_t last2, const MutationContainerType &mutations) const
noexcept
{
using __mtype = typename MutationContainerType::value_type;
return site_dependent_genetic_value()(
first1, last1, first2, last2, mutations,
[this](double &value, const __mtype &mut) noexcept {
value += (scaling * mut.s);
},
[](double &value, const __mtype &mut) noexcept {
value += (mut.h * mut.s);
},
make_return_value, 0.);
}
template <typename HaploidGenomeType, typename MutationContainerType>
inline result_type
operator()(const HaploidGenomeType &g1, const HaploidGenomeType &g2,
const MutationContainerType &mutations) const noexcept
{
using __mtype = typename MutationContainerType::value_type;
return site_dependent_genetic_value()(
g1, g2, mutations,
[this](double &value, const __mtype &mut) noexcept {
value += (scaling * mut.s);
},
[](double &value, const __mtype &mut) noexcept {
value += (mut.h * mut.s);
},
make_return_value, 0.);
}
template <typename DiploidType, typename GenomeContainerType,
typename MutationContainerType>
inline result_type
operator()(const DiploidType &dip, const GenomeContainerType &haploid_genomes,
const MutationContainerType &mutations) const noexcept
{
return this->operator()(haploid_genomes[dip.first],
haploid_genomes[dip.second], mutations);
}
};
} // namespace fwdpp
#endif /* _FITNESS_MODELS_HPP_ */