Skip to content

Commit

Permalink
Fix GCC builds. Move a few templates from .hpp to .cpp files
Browse files Browse the repository at this point in the history
  • Loading branch information
robomics committed Nov 11, 2024
1 parent 4865938 commit e4d131f
Show file tree
Hide file tree
Showing 3 changed files with 316 additions and 327 deletions.
9 changes: 4 additions & 5 deletions src/libnchg/include/nchg/nchg.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,10 @@ class NCHG {
[[nodiscard]] auto aggregate_pixels(const hictk::GenomicInterval& range1,
const hictk::GenomicInterval& range2) const;

template <typename N>
requires arithmetic<N>
[[nodiscard]] static NCHGResult compute_stats(hictk::Pixel<N> pixel, double exp, N obs_sum, N N1,
N N2, double exp_sum, double L1, double L2,
std::vector<double>& buffer);
[[nodiscard]] static NCHGResult compute_stats(hictk::Pixel<std::uint64_t> pixel, double exp,
std::uint64_t obs_sum, std::uint64_t N1,
std::uint64_t N2, double exp_sum, double L1,
double L2, std::vector<double>& buffer);

public:
template <typename PixelSelector>
Expand Down
312 changes: 312 additions & 0 deletions src/libnchg/nchg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,138 @@ bool NCHGResult::operator==(const NCHGResult &other) const noexcept {

bool NCHGResult::operator!=(const NCHGResult &other) const noexcept { return !(*this == other); }

Check warning on line 58 in src/libnchg/nchg.cpp

View check run for this annotation

Codecov / codecov/patch

src/libnchg/nchg.cpp#L58

Added line #L58 was not covered by tests

[[nodiscard]] static std::pair<std::uint64_t, double> aggregate_pixels_cis(
const hictk::File &f, const ExpectedMatrixStats &expected_matrix,
const hictk::GenomicInterval &range1, const hictk::GenomicInterval &range2,
std::uint64_t min_delta, std::uint64_t max_delta, const std::vector<bool> &bin_mask) {
assert(range1.chrom() == range2.chrom());
assert(min_delta <= max_delta);

std::uint64_t obs{};
double exp{};

const auto query_offset1 =
static_cast<std::int64_t>(f.bins().at(range1.chrom(), range1.start()).rel_id());
const auto query_offset2 =
static_cast<std::int64_t>(f.bins().at(range2.chrom(), range2.start()).rel_id());

const auto query_height = (range1.size() + f.resolution() - 1) / f.resolution();
const auto query_width = (range2.size() + f.resolution() - 1) / f.resolution();

std::visit(
[&](const auto &f_) {
const auto sel = f_.fetch(range1.chrom().name(), range1.start(), range1.end(),
range2.chrom().name(), range2.start(), range2.end());

const hictk::transformers::JoinGenomicCoords jsel(
sel.template begin<std::uint32_t>(), sel.template end<std::uint32_t>(), f.bins_ptr());

for (const hictk::Pixel<std::uint32_t> &p : jsel) {
const auto delta = p.coords.bin2.start() - p.coords.bin1.start();

const auto i1 = p.coords.bin1.rel_id();
const auto i2 = p.coords.bin2.rel_id();

if (delta < min_delta || delta >= max_delta || bin_mask[i1] || bin_mask[i2])
[[unlikely]] {
continue;
}

Check warning on line 95 in src/libnchg/nchg.cpp

View check run for this annotation

Codecov / codecov/patch

src/libnchg/nchg.cpp#L93-L95

Added lines #L93 - L95 were not covered by tests

const auto observed_count = p.count;
const auto expected_count = expected_matrix.at(i1, i2);

const auto j1 = static_cast<std::int64_t>(i1) - query_offset1;
const auto j2 = static_cast<std::int64_t>(i2) - query_offset2;
const auto j3 = static_cast<std::int64_t>(i2) - query_offset1;
const auto j4 = static_cast<std::int64_t>(i1) - query_offset2;

bool added = false;
if (j1 >= 0 && j1 < query_height && j2 >= 0 && j2 < query_width) {
obs += observed_count;
exp += expected_count;
added = true;
}

if (added && j1 == j3 && j2 == j4) [[unlikely]] {
continue;
}

Check warning on line 114 in src/libnchg/nchg.cpp

View check run for this annotation

Codecov / codecov/patch

src/libnchg/nchg.cpp#L113-L114

Added lines #L113 - L114 were not covered by tests

if (j3 >= 0 && j3 < query_height && j4 >= 0 && j4 < query_width) {
obs += observed_count;
exp += expected_count;
}

Check warning on line 119 in src/libnchg/nchg.cpp

View check run for this annotation

Codecov / codecov/patch

src/libnchg/nchg.cpp#L117-L119

Added lines #L117 - L119 were not covered by tests
}
},
f.get());

return {obs, exp};
}

[[nodiscard]] static std::pair<std::uint64_t, double> aggregate_pixels_trans(
const hictk::File &f, const ExpectedMatrixStats &expected_matrix,
const hictk::GenomicInterval &range1, const hictk::GenomicInterval &range2,
const std::vector<bool> &bin_mask1, const std::vector<bool> &bin_mask2) {
assert(range1.chrom() != range2.chrom());

Check warning on line 131 in src/libnchg/nchg.cpp

View check run for this annotation

Codecov / codecov/patch

src/libnchg/nchg.cpp#L130-L131

Added lines #L130 - L131 were not covered by tests

std::uint64_t obs{};
double exp{};

Check warning on line 134 in src/libnchg/nchg.cpp

View check run for this annotation

Codecov / codecov/patch

src/libnchg/nchg.cpp#L133-L134

Added lines #L133 - L134 were not covered by tests

const auto chrom_offset1 = f.bins().at(range1.chrom(), 0).id();
const auto chrom_offset2 = f.bins().at(range2.chrom(), 0).id();

Check warning on line 137 in src/libnchg/nchg.cpp

View check run for this annotation

Codecov / codecov/patch

src/libnchg/nchg.cpp#L136-L137

Added lines #L136 - L137 were not covered by tests

std::visit(
[&](const auto &f_) {
const auto sel = f_.fetch(range1.chrom().name(), range1.start(), range1.end(),
range2.chrom().name(), range2.start(), range2.end());

Check warning on line 142 in src/libnchg/nchg.cpp

View check run for this annotation

Codecov / codecov/patch

src/libnchg/nchg.cpp#L139-L142

Added lines #L139 - L142 were not covered by tests

std::for_each(sel.template begin<std::uint32_t>(), sel.template end<std::uint32_t>(),
[&](const hictk::ThinPixel<std::uint32_t> &p) {
assert(p.bin1_id >= chrom_offset1);
assert(p.bin2_id >= chrom_offset2);
const auto i1 = p.bin1_id - chrom_offset1;
const auto i2 = p.bin2_id - chrom_offset2;

Check warning on line 149 in src/libnchg/nchg.cpp

View check run for this annotation

Codecov / codecov/patch

src/libnchg/nchg.cpp#L144-L149

Added lines #L144 - L149 were not covered by tests

if (!bin_mask1[i1] && !bin_mask2[i2]) [[likely]] {
obs += p.count;
exp += expected_matrix.at(i1, i2);
}
});
},
f.get());

Check warning on line 157 in src/libnchg/nchg.cpp

View check run for this annotation

Codecov / codecov/patch

src/libnchg/nchg.cpp#L152-L157

Added lines #L152 - L157 were not covered by tests

return {obs, exp};
}

Check warning on line 160 in src/libnchg/nchg.cpp

View check run for this annotation

Codecov / codecov/patch

src/libnchg/nchg.cpp#L159-L160

Added lines #L159 - L160 were not covered by tests

auto NCHG::aggregate_pixels(const hictk::GenomicInterval &range1,
const hictk::GenomicInterval &range2) const {
assert(_fp);
assert(_exp_matrix);

struct Result {
std::uint64_t obs{};
double exp{};
};

if (range1.chrom() == range2.chrom()) {
const auto &mask = _expected_values.bin_mask(range1.chrom());
assert(mask);
const auto [obs, exp] = aggregate_pixels_cis(*_fp, *_exp_matrix, range1, range2,
params().min_delta, params().max_delta, *mask);

return Result{obs, exp};
}

const auto &[mask1, mask2] = _expected_values.bin_mask(range1.chrom(), range2.chrom());

Check warning on line 181 in src/libnchg/nchg.cpp

View check run for this annotation

Codecov / codecov/patch

src/libnchg/nchg.cpp#L181

Added line #L181 was not covered by tests

assert(mask1);
assert(mask2);

Check warning on line 184 in src/libnchg/nchg.cpp

View check run for this annotation

Codecov / codecov/patch

src/libnchg/nchg.cpp#L183-L184

Added lines #L183 - L184 were not covered by tests

const auto [obs, exp] =
aggregate_pixels_trans(*_fp, *_exp_matrix, range1, range2, *mask1, *mask2);

Check warning on line 187 in src/libnchg/nchg.cpp

View check run for this annotation

Codecov / codecov/patch

src/libnchg/nchg.cpp#L186-L187

Added lines #L186 - L187 were not covered by tests

return Result{obs, exp};

Check warning on line 189 in src/libnchg/nchg.cpp

View check run for this annotation

Codecov / codecov/patch

src/libnchg/nchg.cpp#L189

Added line #L189 was not covered by tests
}

NCHG::NCHG(std::shared_ptr<const hictk::File> f, const hictk::Chromosome &chrom1,
const hictk::Chromosome &chrom2, const Params &params)
: NCHG(f, chrom1, chrom2, ExpectedValues::chromosome_pair(f, chrom1, chrom2, params)) {}
Expand Down Expand Up @@ -266,4 +398,184 @@ double NCHG::compute_pvalue_nchg(std::vector<double> &buffer, std::uint64_t obs,
return pvalue < 0 ? 1.0 : pvalue;
}

template <typename N>
requires arithmetic<N>
[[nodiscard]] constexpr double compute_odds_ratio(N n, N total_sum, N sum1, N sum2) {
if constexpr (std::is_floating_point_v<N>) {
if (std::isnan(n)) [[unlikely]] {
return std::numeric_limits<double>::quiet_NaN();
}

Check warning on line 407 in src/libnchg/nchg.cpp

View check run for this annotation

Codecov / codecov/patch

src/libnchg/nchg.cpp#L406-L407

Added lines #L406 - L407 were not covered by tests
}

if (sum1 == 0 || sum2 == 0) [[unlikely]] {
return std::numeric_limits<double>::quiet_NaN();
}

Check warning on line 412 in src/libnchg/nchg.cpp

View check run for this annotation

Codecov / codecov/patch

src/libnchg/nchg.cpp#L411-L412

Added lines #L411 - L412 were not covered by tests

assert(n <= sum1);
assert(n <= sum2);
assert(sum1 <= total_sum);
assert(sum2 <= total_sum);
assert(sum1 + sum2 <= total_sum + n);

const auto num = n * (total_sum - sum1 - sum2 + n);
const auto denom = (sum1 - n) * (sum2 - n);

assert(num >= 0);
assert(denom >= 0);

return conditional_static_cast<double>(num) / conditional_static_cast<double>(denom);
}

template <typename N1,
typename N2 = std::conditional_t<std::is_floating_point_v<N1>, double, std::uint64_t>>
requires arithmetic<N1>
constexpr std::pair<double, N2> aggregate_marginals(const hictk::GenomicInterval &range,
std::uint32_t resolution,
const std::vector<N1> &marginals,
const std::vector<bool> &bin_mask) noexcept {
assert(resolution > 0);

const auto i1 = range.start() / resolution;
const auto i2 = (range.end() + resolution - 1) / resolution;

if (i1 == i2) [[unlikely]] {
return {N2{}, N2{}};
}

Check warning on line 443 in src/libnchg/nchg.cpp

View check run for this annotation

Codecov / codecov/patch

src/libnchg/nchg.cpp#L442-L443

Added lines #L442 - L443 were not covered by tests

assert(i2 <= marginals.size());
assert(i2 <= bin_mask.size());

std::size_t bin_masked = 0;
N2 sum{};
for (auto i = i1; i < i2; ++i) {
sum += conditional_static_cast<N2>(marginals[i]);
bin_masked += bin_mask[i];
}

const auto bin_masked_frac = static_cast<double>(bin_masked) / static_cast<double>(i2 - i1);

return {bin_masked_frac, sum};
}

std::uint64_t NCHG::compute_N1(const hictk::GenomicInterval &range1,
const hictk::GenomicInterval &range2,
double max_bad_bin_threshold) const noexcept {
assert(_obs_matrix);
assert(_fp);
const auto &[bad_bin_frac, sum] =
aggregate_marginals(range1, _fp->resolution(), _obs_matrix->marginals1(),
*_expected_values.bin_mask(range1.chrom(), range2.chrom()).first);

if (bad_bin_frac >= max_bad_bin_threshold) {
return std::numeric_limits<std::uint64_t>::max();
}

Check warning on line 471 in src/libnchg/nchg.cpp

View check run for this annotation

Codecov / codecov/patch

src/libnchg/nchg.cpp#L470-L471

Added lines #L470 - L471 were not covered by tests

return sum;
}

std::uint64_t NCHG::compute_N2(const hictk::GenomicInterval &range1,
const hictk::GenomicInterval &range2,
double max_bad_bin_threshold) const noexcept {
assert(_obs_matrix);
assert(_fp);
const auto &[bad_bin_frac, sum] =
aggregate_marginals(range2, _fp->resolution(), _obs_matrix->marginals2(),
*_expected_values.bin_mask(range1.chrom(), range2.chrom()).second);

if (bad_bin_frac >= max_bad_bin_threshold) {
return std::numeric_limits<std::uint64_t>::max();
}

Check warning on line 487 in src/libnchg/nchg.cpp

View check run for this annotation

Codecov / codecov/patch

src/libnchg/nchg.cpp#L486-L487

Added lines #L486 - L487 were not covered by tests

return sum;
}

double NCHG::compute_L1(const hictk::GenomicInterval &range1, const hictk::GenomicInterval &range2,
double max_bad_bin_threshold) const noexcept {
assert(_exp_matrix);
assert(_fp);
const auto &[bad_bin_frac, sum] =
aggregate_marginals(range1, _fp->resolution(), _exp_matrix->marginals1(),
*_expected_values.bin_mask(range1.chrom(), range2.chrom()).first);

if (bad_bin_frac >= max_bad_bin_threshold) {
return std::numeric_limits<double>::quiet_NaN();
}

Check warning on line 502 in src/libnchg/nchg.cpp

View check run for this annotation

Codecov / codecov/patch

src/libnchg/nchg.cpp#L501-L502

Added lines #L501 - L502 were not covered by tests

return sum;
}

double NCHG::compute_L2(const hictk::GenomicInterval &range1, const hictk::GenomicInterval &range2,
double max_bad_bin_threshold) const noexcept {
assert(_exp_matrix);
assert(_fp);
const auto &[bad_bin_frac, sum] =
aggregate_marginals(range2, _fp->resolution(), _exp_matrix->marginals2(),
*_expected_values.bin_mask(range1.chrom(), range2.chrom()).second);

if (bad_bin_frac >= max_bad_bin_threshold) {
return std::numeric_limits<double>::quiet_NaN();
}

Check warning on line 517 in src/libnchg/nchg.cpp

View check run for this annotation

Codecov / codecov/patch

src/libnchg/nchg.cpp#L516-L517

Added lines #L516 - L517 were not covered by tests

return sum;
}

NCHGResult NCHG::compute_stats(hictk::Pixel<std::uint64_t> pixel, double exp, std::uint64_t obs_sum,
std::uint64_t N1, std::uint64_t N2, double exp_sum, double L1,
double L2, std::vector<double> &buffer) {
constexpr auto bad_sum = std::numeric_limits<N>::max();
if (pixel.count == 0 || N1 == bad_sum || N2 == bad_sum) [[unlikely]] {
return {.pixel = std::move(pixel),
.expected = exp,
.pval = 1.0,
.log_ratio = 0.0,
.odds_ratio = 0.0,
.omega = 0.0};
}

Check warning on line 533 in src/libnchg/nchg.cpp

View check run for this annotation

Codecov / codecov/patch

src/libnchg/nchg.cpp#L527-L533

Added lines #L527 - L533 were not covered by tests

assert(std::isfinite(exp));
assert(std::isfinite(L1));
assert(std::isfinite(L2));
assert(std::isfinite(exp_sum));

const auto intra_matrix = pixel.coords.bin1.chrom() == pixel.coords.bin2.chrom();

if (!intra_matrix) {
obs_sum *= 2;
}

Check warning on line 544 in src/libnchg/nchg.cpp

View check run for this annotation

Codecov / codecov/patch

src/libnchg/nchg.cpp#L543-L544

Added lines #L543 - L544 were not covered by tests

const auto odds_ratio = compute_odds_ratio(pixel.count, obs_sum, N1, N2);
const auto omega = intra_matrix ? compute_odds_ratio(exp, exp_sum, L1, L2) : 1.0;
const auto log_ratio = std::log2(static_cast<double>(pixel.count)) - std::log2(exp);

if (!std::isfinite(odds_ratio) || !std::isfinite(omega)) [[unlikely]] {
return {.pixel = std::move(pixel),
.expected = exp,
.pval = 1.0,
.log_ratio = log_ratio,
.odds_ratio = odds_ratio,
.omega = omega};
}

Check warning on line 557 in src/libnchg/nchg.cpp

View check run for this annotation

Codecov / codecov/patch

src/libnchg/nchg.cpp#L551-L557

Added lines #L551 - L557 were not covered by tests

if (!std::isfinite(omega) || omega > odds_ratio) [[unlikely]] {
return {.pixel = std::move(pixel),
.expected = exp,
.pval = 1.0,
.log_ratio = log_ratio,
.odds_ratio = odds_ratio,
.omega = omega};
}

if (!intra_matrix) {
obs_sum /= 2;
}

Check warning on line 570 in src/libnchg/nchg.cpp

View check run for this annotation

Codecov / codecov/patch

src/libnchg/nchg.cpp#L569-L570

Added lines #L569 - L570 were not covered by tests

const auto pvalue = compute_pvalue_nchg(buffer, pixel.count, N1, N2, obs_sum, omega);
return {.pixel = std::move(pixel),
.expected = exp,
.pval = pvalue,
.log_ratio = log_ratio,
.odds_ratio = odds_ratio,
.omega = omega};
}

} // namespace nchg
Loading

0 comments on commit e4d131f

Please sign in to comment.