Skip to content

Add oneOfL: exactly-one-of-concepts constraint (fixes #371)#441

Open
Ashut0sh-mishra wants to merge 1 commit into
HLR:mainfrom
Ashut0sh-mishra:fix/issue-371-clean
Open

Add oneOfL: exactly-one-of-concepts constraint (fixes #371)#441
Ashut0sh-mishra wants to merge 1 commit into
HLR:mainfrom
Ashut0sh-mishra:fix/issue-371-clean

Conversation

@Ashut0sh-mishra

Copy link
Copy Markdown
Contributor

Summary

Fixes #371.

The issue asks how to express "for each (step, entity), exactly one of action_create, action_move, action_destroy can be correct". The maintainer noted that atMostL(...) alone allows none to be true — which isn't what the user wanted — and that the clean answer needed "another constraint requiring that action has some class".

This PR introduces oneOfL (and its accumulated sibling oneOfAL) — a first-class, intent-revealing LC meaning "exactly one of these concept applications holds per candidate". It is semantically exactL(c1, c2, ..., 1) with one crucial difference: the limit is pinned via fixedLimit = 1 and cannot be silently shifted by a trailing integer.

Usage

from domiknows.graph import oneOfL

forAllL(
    combinationC(step, entity)('i', 'e'),
    ifL(
        action('x', path=(('i', action_step.reversed),
                          ('e', action_entity.reversed))),
        oneOfL(action_create(path='x'),
               action_destroy(path='x'),
               action_move(path='x')),
    ),
)

Contrast:

  • atMostL(c1, c2, c3) — ≤1 true, including 0 (the issue's footgun).
  • exactL(c1, c2, c3) — ==1 true, but exactL(c1, c2, c3, 2) silently means "==2" because exactL reads a trailing int as the limit.
  • oneOfL(c1, c2, c3) — ==1, always. Trailing ints are ignored because fixedLimit takes precedence in _CountBaseL.__call__.

Changes

  • domiknows/graph/logicalConstrain.py — new oneOfL / oneOfAL classes. Trivial subclasses of _CountBaseL / _AccumulatedCountBaseL with limitOp = '==' and fixedLimit = 1. Full docstring showing the issue's own example.
  • domiknows/graph/__init__.py — re-exports oneOfL and oneOfAL alongside the existing exactL/atMostL/etc.
  • domiknows/solver/adaptiveTNormLossCalculator.py — adds both names to COUNTING_CONSTRAINTS and DEFAULT_TNORM_BY_TYPE ('L' mode), and maps oneOfL → oneOfAL in get_constraint_type's normaliser so they slot into the adaptive-t-norm pipeline exactly like exactL.

Backward compatibility

Additive only. exactL, atMostL, atLeastL, existsL and friends are unchanged; existing users keep identical semantics. No signatures or defaults were altered anywhere.

Tests

  • test_regr/test_oneofl_between_concepts.py — 10 targeted tests covering:
    • Class semantics (limitOp == '==', fixedLimit == 1).
    • Public-import path from domiknows.graph.
    • Adaptive-t-norm registry entries and normaliser.
    • Limit-pinning (ignores trailing ints).
    • Parity with exactL(..., 1) and explicit contrast with the exactL(..., k) footgun that motivated the new class.
    • Docstring quality.
  • test_regr/test_oneofl_stress.py — randomised sweeps:
    • 10,000 random (concepts, trailing int) combos confirming oneOfL always resolves to limit 1.
    • 10,000 combos confirming exactL's trailing-int behaviour is preserved (back-compat guard).
    • 10,000 registry re-reads confirming the adaptive-t-norm map keeps both classes across imports.
    • 100,000-iteration combined sweep behind DOMIKNOWS_RUN_HEAVY=1.
    • Per-test iteration count overridable via DOMIKNOWS_ONEOFL_STRESS_ITERS.

Local results:

test_regr/test_oneofl_stress.py ...s                       [ 28%]
test_regr/test_oneofl_between_concepts.py ..........       [100%]
13 passed, 1 skipped in 0.07s

Neighbouring suites (test_existsL_variable_scope, test_copt_pluggable, test_inference_program_modes) also run clean — 35 passed, no regressions.

Co-authored-by: nik464 nikhil18chaudhary@gmail.com

HLR#371)

Introduces oneOfL (and its accumulated sibling oneOfAL) — counting constraints with limitOp == '==' and fixedLimit = 1 that cannot be overridden by a trailing int. Makes `exactly one of these concepts holds` a first-class, intent-revealing LC, directly answering issue HLR#371. atMostL(c1,c2,c3) would permit zero; oneOfL does not.

Co-authored-by: nik464 <nikhil18chaudhary@gmail.com>
@Ashut0sh-mishra

Copy link
Copy Markdown
Contributor Author

@kordjamshidi Hi, just checking in on this PR whenever you get a chance to review it.
Please let me know if any changes or updates are needed from my side. Thanks!

@kordjamshidi

Copy link
Copy Markdown
Member

Thank you very much for the reminder @Ashut0sh-mishra . @auszok could you also review this?

@Ashut0sh-mishra

Copy link
Copy Markdown
Contributor Author

Hi @auszok and @kordjamshidi,

Just following up on this PR when you have a chance. Since it addresses issue #371, I'd be happy to make any changes or revisions needed after review.

Thanks again for your time.

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.

Defining ExactL between various concepts

3 participants