Skip to content

Z3 Constraint Satisfaction Provider

This content is for v0.9.2. Switch to the latest version for up-to-date documentation.

The Z3 provider integrates Microsoft Z3, an industry-grade SMT (Satisfiability Modulo Theories) solver, into the nxusKit provider interface. It enables constraint satisfaction, optimization, and formal verification problems to be expressed and solved using the same chat() / chat_stream() API as LLM providers.

Unlike LLM providers, Z3 is deterministic — the same input always produces the same output.

{
"provider_type": "z3",
"timeout_ms": 30000,
"random_seed": 42,
"produce_unsat_core": true,
"logic": "QF_LIA"
}

Configuration options:

OptionTypeDefaultDescription
timeout_msinteger30000Solver timeout in milliseconds
random_seedintegernoneSeed for search heuristics (for reproducibility)
produce_unsat_corebooleanfalseExtract conflicting constraints when problem is unsatisfiable
logicstringautoSMT logic preset (e.g., "QF_LIA", "QF_LRA", "QF_BV")
max_conflictsintegernoneMaximum conflict (branch) limit for search
model_pathsstring[]noneDirectories to scan for .smt2 and .z3 model files

Capabilities: System messages, streaming, JSON mode, seed

The Z3 provider accepts structured JSON as the user message content. The JSON describes variables, constraints, and optionally an optimization objective.

{
"variables": [
{"name": "x", "var_type": "integer"},
{"name": "y", "var_type": "integer", "domain": {"min": 1, "max": 100}},
{"name": "flag", "var_type": "boolean"},
{"name": "ratio", "var_type": "real", "domain": {"min": 0.0, "max": 1.0}}
]
}

Variable types: integer, real, boolean

Domain constraints (optional): min, max for numeric types.

{
"constraints": [
{"constraint_type": "eq", "params": {"left": "x", "right": 42}},
{"constraint_type": "gt", "params": {"left": "y", "right": "x"}},
{"constraint_type": "all_different", "params": {"variables": ["x", "y", "z"]}},
{"constraint_type": "in_range", "params": {"variable": "x", "min": 1, "max": 10}}
]
}

Supported constraint types:

TypeDescriptionParameters
eqEqualleft, right
neqNot equalleft, right
ltLess thanleft, right
gtGreater thanleft, right
leLess than or equalleft, right
geGreater than or equalleft, right
addAddition expressionleft, right (or operands list)
subSubtraction expressionleft, right
mulMultiplication expressionleft, right
divDivision expressionleft, right
andLogical ANDoperands list
orLogical ORoperands list
notLogical NOToperand
impliesLogical implicationleft, right
iffIf and only ifleft, right
all_differentAll variables must differvariables list
at_mostAt most N truevariables, n
at_leastAt least N truevariables, n
exactlyExactly N truevariables, n
in_rangeValue within rangevariable, min, max

Parameters can reference variable names (strings), literal values (numbers, booleans), or nested expressions.

Add an objective field to minimize or maximize a variable:

{
"variables": [...],
"constraints": [...],
"objective": {
"direction": "minimize",
"expression": "cost"
}
}

Directions: "minimize" or "maximize"

Constraints can be named for unsat core extraction:

{
"constraints": [
{"name": "budget_limit", "constraint_type": "le", "params": {"left": "cost", "right": 1000}},
{"name": "quality_req", "constraint_type": "ge", "params": {"left": "quality", "right": 8}}
]
}

When the problem is unsatisfiable and produce_unsat_core is enabled, the response includes the names of conflicting constraints.

The response content field contains JSON:

{
"status": "sat",
"assignments": {
"x": 42,
"y": 58,
"flag": true
},
"stats": {
"solve_time_ms": 2,
"num_conflicts": 0,
"num_decisions": 5
}
}
{
"status": "unsat",
"unsat_core": ["budget_limit", "quality_req"],
"stats": {
"solve_time_ms": 1,
"num_conflicts": 3
}
}
{
"status": "optimal",
"assignments": {
"cost": 150,
"quality": 8
},
"objective_value": 150.0,
"stats": {
"solve_time_ms": 15,
"num_solutions_found": 4
}
}
  • Satisfaction problems: Single result chunk (solve is typically < 1ms).
  • Optimization problems: Progressive improvement — each improving solution is emitted as a StreamChunk, with the final chunk containing finish_reason and complete stats.
use nxuskit-engine::{Z3Provider, ChatRequest, Message};
let provider = Z3Provider::builder()
.timeout_ms(5_000)
.build()?;
let problem = r#"{
"variables": [
{"name": "x", "var_type": "integer", "domain": {"min": 1, "max": 9}},
{"name": "y", "var_type": "integer", "domain": {"min": 1, "max": 9}},
{"name": "z", "var_type": "integer", "domain": {"min": 1, "max": 9}}
],
"constraints": [
{"constraint_type": "all_different", "params": {"variables": ["x", "y", "z"]}},
{"constraint_type": "eq", "params": {"left": {"add": ["x", "y", "z"]}, "right": 15}}
]
}"#;
let request = ChatRequest::new("z3-solver")
.with_message(Message::user(problem));
let response = provider.chat(&request).await?;
let output: serde_json::Value = serde_json::from_str(&response.content)?;
assert_eq!(output["status"], "sat");
provider, err := nxuskit.NewZ3FFIProvider(
nxuskit.WithZ3Timeout(5000),
nxuskit.WithZ3UnsatCore(true),
)
response, err := provider.Chat(ctx, &nxuskit.ChatRequest{
Model: "z3-solver",
Messages: []nxuskit.Message{
{Role: "user", Content: problemJSON},
},
})
from nxuskit-py._ffi_provider import create_ffi_provider
provider = create_ffi_provider({
"provider_type": "z3",
"timeout_ms": 5000,
"produce_unsat_core": True,
})
response = provider.chat(model="z3-solver", messages=[
{"role": "user", "content": problem_json}
])

The Z3 provider can discover pre-defined constraint model files:

let models = provider.list_models().await?;
// Returns built-in models ("z3-solver", "z3-optimizer") plus
// any .smt2 and .z3 files found in configured model_paths
  • Scheduling: Resource allocation, timetabling, shift planning
  • Configuration: Product configuration with constraints, compatibility checking
  • Puzzles: Sudoku, N-Queens, logic puzzles
  • Verification: Property checking, invariant validation
  • Optimization: Cost minimization, resource optimization with constraints
  • Planning: Task sequencing with dependency and resource constraints