Skip to content

Fix AA-identity/property-value mismatch in sequenceEncoder()/sequenceDecoder() property mode - #22

Merged
ncborcherding merged 6 commits into
BorchLab:develfrom
Qile0317:fix-property-matrix-aa-reorder
Aug 22, 2026
Merged

Fix AA-identity/property-value mismatch in sequenceEncoder()/sequenceDecoder() property mode#22
ncborcherding merged 6 commits into
BorchLab:develfrom
Qile0317:fix-property-matrix-aa-reorder

Conversation

@Qile0317

Copy link
Copy Markdown
Contributor

Bug

sequenceEncoder(mode="property", ...) and sequenceDecoder(mode="property", ...)
silently mismatch amino-acid identity to property values for 3 of the ~10
supported property sets: crucianiProperties, MSWHIM, ProtFP.

Both functions assume .aa.property.matrix()'s output is ordered by the
canonical amino-acid alphabet, positionally — sequenceEncoder()'s roxygen
docs state this explicitly ("rows correspond to the 20 canonical amino acids
in the order of sequence.dictionary"), and sequenceEncoder()'s C++
backend / sequenceDecoder()'s .propertyDecoder() both index into it
purely positionally, never by name.

.aa.property.matrix() didn't actually guarantee that. It returns
Peptides::AAdata[[key]] reshaped via do.call(rbind, ...), in whatever
column order that scale's author originally stored it in Peptides — which is
NOT canonical for these 3 sets:

property set actual stored order
crucianiProperties canonical except positions 6,7 (E,Q instead of Q,E)
MSWHIM fully alphabetical
ProtFP fully alphabetical

calculateProperty() already reorders by name after calling this helper
(S[, amino.acids, drop=FALSE]); sequenceEncoder()/sequenceDecoder()
didn't, so e.g. sequenceEncoder("R", mode="property", property.set="MSWHIM", max.length=1)$flattened silently returned Cysteine's true MSWHIM values, not
Arginine's.

Verified against Peptides::mswhimScores()/crucianiProperties()/protFP()
— Peptides' own independent public scoring functions, not just immApex/
calculateProperty() internals — confirming the canonical-order reading is
the objectively correct one, not just a different convention.

Fix

Centralizes the canonical-AA reorder inside .aa.property.matrix() itself
(both the .builtin_scales branch and the Peptides::AAdata branch), so
every caller gets canonical order by construction instead of relying on each
call site to remember to reorder. This fixes both bug sites in one change:
R/sequenceEncoder.R:132 and R/sequenceDecoder.R:110 (the latter has the
identical root cause but wasn't previously flagged).

calculateProperty()'s own existing reorder is left in place — now
redundant but harmless/idempotent.

Tests

  • .aa.property.matrix() returns canonical column order for every supported
    property set (test-calculateProperty.R).
  • sequenceEncoder() property mode matches Peptides' own scoring functions
    for single-residue probes on all 3 previously-broken sets
    (test-sequenceEncoder.R).
  • sequenceDecoder() property mode correctly identifies an amino acid from a
    cube built directly from Peptides ground truth — not via
    sequenceEncoder(), since a pure round trip through immApex's own
    (formerly buggy) functions would self-cancel this exact bug
    (test-sequenceDecoder.R).

Version

1.7.11.7.2, NEWS.md updated.

.aa.property.matrix() returned property scales in whatever column order
Peptides::AAdata happened to store them in (alphabetical for MSWHIM/ProtFP,
E/Q-swapped for crucianiProperties), not the canonical amino-acid order that
sequenceEncoder()'s C++ backend and sequenceDecoder()'s distance-based caller
both assume positionally. calculateProperty() already reordered by name after
calling this helper; sequenceEncoder()/sequenceDecoder() did not, so e.g.
encoding "R" under MSWHIM silently returned Cysteine's values.

Centralizes the canonical-AA reorder inside .aa.property.matrix() itself so
every caller gets it by construction, fixing both call sites at once. Verified
against Peptides' own independent public scoring functions (mswhimScores(),
crucianiProperties(), protFP()), not just immApex internals.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@Qile0317

Copy link
Copy Markdown
Contributor Author

@ncborcherding Note: the test-coverage fail related to buildNetwork seems to be a separate issue that's probably to do with some obscure C++ behaviour across different versions or checker/compiler params, etc.

@codecov

codecov Bot commented Aug 22, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.00000% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
R/calculateProperty.R 87.50% 2 Missing ⚠️
R/sequenceDecoder.R 66.66% 1 Missing ⚠️
Files with missing lines Coverage Δ
R/sequenceEncoder.R 97.64% <100.00%> (ø)
R/utils.R 55.03% <ø> (ø)
R/sequenceDecoder.R 94.20% <66.66%> (ø)
R/calculateProperty.R 88.31% <87.50%> (-0.76%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ncborcherding
ncborcherding merged commit 2ec70dc into BorchLab:devel Aug 22, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants