Skip to content

fix: parse grRules into a tree; fix removeGenes, expandModel, findPotentialErrors - #673

Merged
edkerk merged 1 commit into
develop3from
fix/gpr-parse-tree
Jul 17, 2026
Merged

fix: parse grRules into a tree; fix removeGenes, expandModel, findPotentialErrors#673
edkerk merged 1 commit into
develop3from
fix/gpr-parse-tree

Conversation

@edkerk

@edkerk edkerk commented Jul 17, 2026

Copy link
Copy Markdown
Member

Main improvements in this PR:

  • features:
    • Shared grRule parser: parseGrRule, grRuleToDNF, isDnfGrRule, grRuleToString.
  • fix:
    • removeGenes removed the wrong gene's association when one gene id was a substring of another.
    • expandModel dropped mandatory subunits when expanding rules mixing and and or.
    • findPotentialErrors flagged valid DNF rules as errors, which also prevented standardizeGrRules from repairing them.
  • documentation:
    • grRule tests in tUtils.m and tManipulation.m.

Instructions on merging this PR:

  • This PR has develop3 as target branch, and will be resolved with a squash-merge.

…lErrors

Three functions each reasoned about grRules by searching the raw string, and
each got it wrong in a different way. They now share one parse tree.

New in utils/: parseGrRule (tokenise + recursive descent), isDnfGrRule,
grRuleToDNF, grRuleToString. Operators are matched as whole words, so a gene
called RAND1 is a gene, and "10" is never confused with "100".

raven-toolbox gets this for free from cobra's Python AST (utils/gpr.py,
manipulation/expand.py); MATLAB has no equivalent, so the tree is built here.
isDnfGrRule mirrors _is_dnf_node/_contains_or and grRuleToDNF mirrors
_node_to_dnf node for node.

removeGenes (R2): removeGeneFromRule used an unanchored strfind over the
' or '-split rule, so removing gene "10" from "10 or 100" also matched "100"
and left the reaction with no grRule at all while gene 100 stayed in the
model -- the association was destroyed, silently, and standardizeGrRules then
zeroed the rxnGeneMat row. Numeric Entrez/HGNC ids make this the norm rather
than the exception in the tINIT/HPA human workflow, and findGeneDeletions and
runSimpleOptKnock mispredict essentiality off the result. Both subfunctions
now walk the tree, comparing gene ids with strcmp. This also retires the last
eval-based grRule evaluation in the codebase.

expandModel (EX1): stripped every parenthesis and split on ' or ', which
destroys precedence. "g1 and (g2 or g3)" expanded to "g1 and g2" and "g3" --
the second isozyme silently lost its mandatory g1 subunit, so a g1 knockout
was predicted viable when it is lethal. Isozymes are now the DNF clauses.
The reaction count comes from the clauses too: "(g1 or g2) and (g3 or g4)"
has two or:s but four isozymes, and the old preallocation sized for three.
rxnToCheck now lists only rules that genuinely needed distributivity, not
every rule containing ' and '.

findPotentialErrors (S2): the one-level split flagged valid DNF such as
"((G1 and G2) or G3)" and "(G1 or G2) or (G3 and G4)". That was worse than
cosmetic: standardizeGrRules skips flagged rules, so a false positive meant
the rule was never repaired and kept the brackets the function exists to
remove. Unparseable rules are now reported as issues rather than skipped.

Also fix a pre-existing crash in expandModel: cpyIndices was a row, and
indexing a 1x1 field takes the orientation of the index, so every
concatenation failed on a single-reaction model.

Tests: nothing under testing/ exercised grRules at all. Adds parser coverage
to tUtils and regression tests to tManipulation for each defect above, using
the reproductions as the anchors.
@github-actions

Copy link
Copy Markdown

Function test results

259 tests   237 ✅  1m 2s ⏱️
 23 suites   22 💤
  1 files      0 ❌

Results for commit 14e8e2c.

@edkerk
edkerk merged commit 70cd65f into develop3 Jul 17, 2026
4 checks passed
@edkerk
edkerk deleted the fix/gpr-parse-tree branch July 17, 2026 16:49
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.

1 participant