From 6d8f11b142039a08c657543a24fcc51aa5c1e8f4 Mon Sep 17 00:00:00 2001 From: aviezerl Date: Tue, 21 Jul 2026 20:11:34 +0300 Subject: [PATCH] fix: symmetric N/* handling across strands in PWM energy routines (5.11.11) integrate_energy(), integrate_energy_logspat() and integrate_energy_max_logspat() scored N/* with the column average (get_avg_log_prob) on the forward strand but a flat log(0.25) on the reverse strand. Both strands now use the column average, matching the forward branch, the symmetric likelihood routines (max_like_match, integrate_like_seg), and the shared DnaPSSM code in the prego package. No behavior change for genomic vtrack scoring: an N-containing window is masked to -Inf before these branches run. This keeps misha's DnaPSSM in sync with prego, where the same code path (compute_pwm) feeds ambiguous bases through unmasked and the asymmetry was an observable ~1.6 nat strand-dependent error. Claude-Session: https://claude.ai/code/session_01PK3qefBGDoBwd9w5226FEq --- DESCRIPTION | 2 +- NEWS.md | 4 ++++ src/DnaPSSM.cpp | 6 +++--- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 9c7e69084..87e55b169 100755 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Type: Package Package: misha Title: Toolkit for Analysis of Genomic Data -Version: 5.11.10 +Version: 5.11.11 Authors@R: c( person("Misha", "Hoichman", , "misha@hoichman.com", role = "aut"), person("Aviezer", "Lifshitz", , "aviezer.lifshitz@weizmann.ac.il", role = c("aut", "cre")), diff --git a/NEWS.md b/NEWS.md index a0faf7e55..496ded4e6 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,7 @@ +# misha 5.11.11 + +* **Internal consistency:** `N`/`*` are now scored the same on both strands in the PWM energy routines (reverse used `log(0.25)`, forward used the column average; both now use the average). No behavior change for genomic scoring, where `N`-windows are masked to `-Inf`; keeps `DnaPSSM` in sync with the `prego` package. + # misha 5.11.10 * **Behavior fix:** non-multitask `gintervals.quantiles()` (`options(gmultitasking = FALSE)`) no longer truncates the result to the first ~1000 intervals on a large scope; the streaming (`intervals.set.out=`) and in-memory results now match the multitasking output (#149). diff --git a/src/DnaPSSM.cpp b/src/DnaPSSM.cpp index 5096c819c..4332403a6 100644 --- a/src/DnaPSSM.cpp +++ b/src/DnaPSSM.cpp @@ -915,7 +915,7 @@ void DnaPSSM::integrate_energy(const string &target, float &energy, vectorget_avg_log_prob(); } else { int code = DnaLookupTables::COMPLEMENT_ENCODE[(unsigned char)*j]; if(code >= 0) { @@ -986,7 +986,7 @@ void DnaPSSM::integrate_energy_logspat(const string &target, float &energy, vect break; } if(DnaLookupTables::NEUTRAL_CHAR[(unsigned char)*j]) { - logp += c_log_quarter; + logp += p->get_avg_log_prob(); } else { int code = DnaLookupTables::COMPLEMENT_ENCODE[(unsigned char)*j]; if(code >= 0) { @@ -1058,7 +1058,7 @@ void DnaPSSM::integrate_energy_max_logspat(const string &target, float &energy, break; } if(DnaLookupTables::NEUTRAL_CHAR[(unsigned char)*j]) { - logp_rev += c_log_quarter; + logp_rev += p->get_avg_log_prob(); } else { int code = DnaLookupTables::COMPLEMENT_ENCODE[(unsigned char)*j]; if(code >= 0) {