Skip to content

Support inequalities in the answer and response - #276

Open
m-messer wants to merge 6 commits into
mainfrom
feature/inequalities
Open

Support inequalities in the answer and response#276
m-messer wants to merge 6 commits into
mainfrom
feature/inequalities

Conversation

@m-messer

@m-messer m-messer commented Sep 10, 2026

Copy link
Copy Markdown
Member

Problem

compareExpressions has limited support for equalities in the answer and
response (x^2 = 5y^2 + 7, see the user docs) but no support for
inequalities. Issue #271 asks for the analogous feature.

Separately, != was silently mis-parsed: x != 5 became Eq(factorial(x), 5)
(the ! consumed as factorial, the = as equality), so a response of x != 5
against an answer of x != 5 only "passed" because both sides mis-parsed
identically, and 5 != x became Eq(120, x).

Changes

  • app/utility/expression_utilities.py: parse_expression now detects relational
    operators before the = split. <, <=, >, >= → the matching SymPy
    relation; a two-operator, single-direction chain (1 < x < 5) → an And of two
    relations; != (and unicode , normalised to !=) → Ne. Chains of three or
    more operators, mixed-direction chains (1 < x > 5), and != combined with any
    other operator raise a parse error.
  • app/context/symbolic.py:
    • inequality_bounds() — classifies a parsed expression as a single relation, a
      chained order inequality (And of order relations), or neither.
    • check_inequality_equivalence() with helpers _compare_single_inequality,
      _compare_not_equal, _compare_chained_inequalities. Two relations are
      equivalent when, after moving all terms to one side, the difference ratio
      D_response / D_answer is a positive constant with matching
      strictness
      — a negative constant ⇒ opposite direction, < vs <=
      strictness mismatch. != uses the same ratio test but any non-zero constant
      counts and direction/strictness do not apply. Chains are matched bound by
      bound in either pairing. This mirrors the existing equality_equivalence
      check that (a-b)/(c-d) is a constant.
    • criterion_equality_node gains a use_inequality_equivalence branch
      (parallel to use_equality_equivalence), used when either the response or the
      answer parses to a relation. Emits _TRUE / _FALSE / _UNKNOWN /
      _WRONG_DIRECTION / _STRICTNESS_MISMATCH / _RESPONSE_NOT_INEQUALITY /
      _ANSWER_NOT_INEQUALITY tags.
    • check_equality guards relational operands so they never reach the
      lhs - rhs subtraction (which raises TypeError on SymPy relations).
  • app/feedback/symbolic.py: feedback strings for the seven new
    INEQUALIT* / *_NOT_INEQUALITY INTERNAL tags.
  • app/preview_implementations/symbolic_preview.py: split the previewed response
    on a lone = only (re.split(r"(?<![<>=!])=(?!=)", …)), so x >= 5,
    x != 5 and 1 < x < 5 preview instead of erroring or splitting on the =
    inside >= / !=.
  • app/docs/user.md, app/docs/dev.md: document the feature, the equivalence
    rule and the limitations.
  • Tests: TestParseInequalities in app/tests/expression_utilities_test.py;
    test_inequality_*, test_chained_inequality_*, test_not_equal_* in
    app/tests/symbolic_evaluation_test.py; inequality preview cases in
    app/tests/preview_test.py.

Behaviour

Answer Accepted student responses Rejected student responses
2x - 10 >= 0 x >= 5, 5 <= x, 4x - 20 >= 0, 10 - 2x <= 0 x > 5 (strictness), x <= 5 (opposite direction)
1 < x < 5 5 > x > 1, 0 < x - 1 < 4, 2 < 2x < 10 1 <= x < 5 (strictness)
x != 5 5 != x, 2x != 10, x - 5 != 0, x ≠ 5 x = 5 (not comparable)

Known limitation: when the student response's bound is not a scalar multiple
of the answer's — e.g. a response of x > 1 against an answer of x > 5, or a
response of x != 3 against an answer of x != 5 — the result is UNKNOWN
rather than FALSE. The response is still marked incorrect; only the feedback
tag is weaker. This matches the existing ratio-based equality-equivalence
behaviour. plus_minus-expanded relation sets and <> are not supported.

Checklist

  • Tests added/updated
  • Docs updated (app/docs/user.md, app/docs/dev.md)

Closes #271

🤖 Generated with Claude Code

https://claude.ai/code/session_01DzjPKz78pwESTLPdSGXF4q

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.

Feature: Inequalities

2 participants