Skip to content

ground_notcoexistence instantiates NotCoExistence with the same operand twice #34

Description

@askalot-io

Bug

nl2ltl/engines/grounding.py:

def ground_notcoexistence(connectors: Dict[str, float]) -> Set[Template]:
    if len(list(connectors)) >= 2:
        return {
            NotCoExistence(
                Atomic(decapitalize(list(connectors)[1])),
                Atomic(decapitalize(list(connectors)[1])),   # <-- [1] twice
            )
        }

Both operands are built from list(connectors)[1], so any two-symbol grounding produces a degenerate self-pair. Reproduction:

>>> from nl2ltl.engines.utils import _get_formulas
>>> _get_formulas("notcoexistence", {"reject_request": 1.0, "escalate_to_human": 1.0})
{(NotCoExistence escalate_to_human escalate_to_human)}

Expected: (NotCoExistence reject_request escalate_to_human).

The resulting LTLf, F(x) -> ~F(x), is equivalent to G(!x) — "x never happens" — which silently changes the meaning of the translated requirement (we caught it because a round-trip paraphrase of the formula no longer matched the input sentence).

Fix

Atomic(decapitalize(list(connectors)[0])),
Atomic(decapitalize(list(connectors)[1])),

Observed on nl2ltl from PyPI (Python 3.13). Found while building a Claude-backed custom Engine for translating agent-guardrail policies to DECLARE. Can send a PR if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions