Skip to main content
The Logic Engine uses Z3, a Satisfiability Modulo Theories (SMT) solver from Microsoft Research, to verify logical constraints.
The legacy VerificationEngine.verify_logic_rule() method has been removed as of v5.1.0. It now raises NotImplementedError. Use LogicVerifier from qwed_new.core.logic_verifier or the SDK’s client.verify_logic() method instead. See the changelog for migration details.

Capabilities


Quick start


QWED-Logic DSL

QWED uses a secure S-expression DSL for logic expressions.

Operators

Comparison and arithmetic operators accept multiple aliases for convenience. For example, GTE and GE both mean “greater than or equal to”, NEQ and NE both mean “not equal”, and MUL and MULT both mean multiplication.

Examples


Satisfiability checking

Basic check

Finding all solutions


Business rules

Age verification

Discount eligibility

Approval workflow


Quantifiers

Universal (FORALL)

Existential (EXISTS)


Security: operator whitelist

The DSL uses a strict whitelist — only approved operators are allowed:

Fail-closed constraint parsing

The logic engine uses SafeEvaluator for all constraint parsing. If SafeEvaluator is unavailable, the engine raises a RuntimeError instead of falling back to raw evaluation. This fail-closed design ensures that untrusted input is never passed to Python’s eval().

Provider tracking

When you verify a natural-language logic query, QWED translates it to DSL using the configured LLM provider. The response includes a provider_used field so you can see which provider handled the translation — even when the request falls back to a different provider or ends in an error.
This field also appears in error responses, which helps you debug provider-related issues without inspecting server logs.

Error handling


LogicVerifier returns DiagnosticResult (v5.4.0)

Introduced in v5.4.0. LogicVerifier is the second engine — after FactVerifier — to conform to the unified DiagnosticResult contract. The legacy LogicResult dataclass has been removed. The high-level SDK client (client.verify_logic()) and the /verify/logic API endpoint still return the same SAT/UNSAT/UNKNOWN shape shown above.
If you call the low-level LogicVerifier class directly, every one of its nine public methods now returns a DiagnosticResult:
  • verify_logic
  • verify_with_quantifiers
  • verify_bitvector
  • verify_array
  • prove_theorem
  • check_implication
  • check_equivalence
  • verify_optimization
  • check_vacuity

Reading a result

The three diagnostic layersagent_message, developer_fields, and proof_ref — are populated on every result. proof_ref is computed from the Z3 solver’s assertion stack and is present only when status == "VERIFIED".

SAT/UNSAT status matrix

Z3’s sat/unsat outcome has different meanings depending on the method. The engine disambiguates by mapping each per-method outcome to a DiagnosticResult status and recording the raw Z3 verdict under developer_fields.deterministic_verdict.
Always branch on result.is_verified (or result.proof_ref is not None) for control flow. Read developer_fields["deterministic_verdict"] for diagnostic context — never for authorization.

symbol_table on every VERIFIED result

developer_fields.symbol_table is a sorted list of every declared variable and its type. It is included on every VERIFIED result so audit logs record exactly what was proven:

New BLOCKED constraint IDs

Two fail-closed behaviors ship with the migration: Other constraint_ids used by the engine: logic_verifier.invalid_constraint, logic_verifier.unknown_quantifier, and logic_verifier.execution_error.

Migrating from LogicResult

Status field
Proving a theorem
Variable declarations are now mandatory
Malformed BitVec declarations fail closed

Performance


Next steps