Skip to content

Commit 71d3378

Browse files
authored
Documentation update (#110)
* Add javadoc badge * Mention Java support * Advanced usage examples * KSMT overview section
1 parent c7c4182 commit 71d3378

5 files changed

Lines changed: 454 additions & 37 deletions

File tree

README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
# KSMT
2-
Kotlin API for various SMT solvers.
2+
Kotlin/Java API for various SMT solvers.
33

44
[![KSMT: build](https://github.com/UnitTestBot/ksmt/actions/workflows/build-and-run-tests.yml/badge.svg)](https://github.com/UnitTestBot/ksmt/workflows/build-and-run-tests.yml)
55
![Maven Central](https://img.shields.io/maven-central/v/io.ksmt/ksmt-core)
6+
[![javadoc](https://javadoc.io/badge2/io.ksmt/ksmt-core/javadoc.svg)](https://javadoc.io/doc/io.ksmt/ksmt-core)
7+
8+
# Overview
9+
10+
`KSMT` provides a simple and unified way to work with SMT:
11+
* Define formulas in a simple and solver-independent way
12+
* Simplify and transform formulas without involving the SMT solver
13+
* Solve SMT formulas using various SMT solvers through a unified API
614

715
# Getting started
816
Install via [Gradle](https://gradle.org/).
@@ -64,3 +72,10 @@ independent of the solver.
6472
KSMT provides a high-performant API for running SMT solvers in separate processes.
6573
This feature is important for implementing hard timeouts and
6674
solving using multiple solvers in portfolio mode.
75+
76+
### Formula simplification and manipulation utils
77+
KSMT provides a simplification engine that can simplify and especially evaluate all supported expressions in all
78+
supported theories.
79+
Also, KSMT provides utilities for performing formula transformation, visiting and expression substitution.
80+
81+
Check out our [advanced features tutorial](docs/advanced-usage.md) for the examples.

docs/advanced-usage.md

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
# Advanced usage
2+
3+
Advanced features usage tutorial.
4+
If you are new to KSMT, please check out our [getting started guide](/docs/getting-started.md) first.
5+
6+
For complete code examples, see our [example project](/examples) and
7+
particularly [`AdvancedExamples.kt`](/examples/src/main/kotlin/AdvancedExamples.kt)
8+
9+
## Working with SMT formulas
10+
11+
### Parsing formulas in SMT-LIB2 format
12+
13+
KSMT provides an API for parsing formulas in the SMT-LIB2 format.
14+
Currently, KSMT provides a parser implemented on top of the Z3 solver API
15+
and therefore `ksmt-z3` module is required for parsing.
16+
17+
```kotlin
18+
val formula = """
19+
(declare-fun x () Int)
20+
(declare-fun y () Int)
21+
(assert (>= x y))
22+
(assert (>= y x))
23+
"""
24+
with(ctx) {
25+
val assertions = KZ3SMTLibParser().parse(formula)
26+
}
27+
```
28+
29+
### Simplification during expressions creation
30+
31+
By default, `KContext` will attempt to apply lightweight simplifications during expression creation. If simplifications
32+
are not suitable for your use cases you can disable them using the `KContext.simplificationMode` parameter.
33+
34+
```kotlin
35+
// Simplification is enabled by default
36+
val simplifyingContext = KContext()
37+
38+
// Disable simplifications on a context level
39+
val nonSimplifyingContext = KContext(simplificationMode = KContext.SimplificationMode.NO_SIMPLIFY)
40+
41+
val simplifiedExpr = with(simplifyingContext) {
42+
val a by boolSort
43+
!(a and falseExpr)
44+
}
45+
46+
val nonSimplifiedExpr = with(nonSimplifyingContext) {
47+
val a by boolSort
48+
!(a and falseExpr)
49+
}
50+
51+
println(nonSimplifiedExpr) // (not (and a false))
52+
println(simplifiedExpr) // true
53+
```
54+
55+
### Manual expression simplification
56+
57+
KSMT provides a `KExprSimplifier` which allows you to manually perform simplification of an arbitrary expression.
58+
59+
```kotlin
60+
// Context do not simplify expressions during creation
61+
val ctx = KContext(simplificationMode = KContext.SimplificationMode.NO_SIMPLIFY)
62+
63+
with(ctx) {
64+
val a by boolSort
65+
val nonSimplifiedExpr = !(a and falseExpr)
66+
67+
val simplifier = KExprSimplifier(ctx)
68+
val simplifiedExpr = simplifier.apply(nonSimplifiedExpr)
69+
70+
println(nonSimplifiedExpr) // (not (and a false))
71+
println(simplifiedExpr) // true
72+
}
73+
```
74+
75+
### Expression substitution
76+
77+
KSMT provides a `KExprSubstitutor` which allows you to replace all occurrences of one expression with another
78+
expression.
79+
80+
```kotlin
81+
val a by boolSort
82+
val b by boolSort
83+
val expr = !(a and b)
84+
85+
val substitutor = KExprSubstitutor(this).apply {
86+
// Substitute all occurrences of `b` with `false`
87+
substitute(b, falseExpr)
88+
}
89+
90+
val exprAfterSubstitution = substitutor.apply(expr)
91+
92+
println(expr) // (not (and a b))
93+
println(exprAfterSubstitution) // true
94+
```
95+
96+
## Working with SMT solvers
97+
98+
### Solver configuration
99+
100+
KSMT provides an API for modifying solver-specific parameters.
101+
Since the parameters and their correct values are solver-specific
102+
KSMT does not perform any checks.
103+
See the corresponding solver documentation for a list of available options.
104+
105+
```kotlin
106+
with(ctx) {
107+
KZ3Solver(this).use { solver ->
108+
solver.configure {
109+
// set Z3 solver parameter random_seed to 42
110+
setZ3Option("random_seed", 42)
111+
}
112+
}
113+
}
114+
```
115+
116+
### Solver independent models
117+
118+
By default, SMT solver models lazily initialized.
119+
The values of the declared variables are loaded from the underlying solver's native model on demand.
120+
Therefore, models become invalid after solver close. Also, solvers like `Bitwuzla` invalidate their models every
121+
time `check-sat` is called.
122+
To overcome these problems, KSMT provides the `KModel.detach` function that allows you to make the model independent of
123+
the underlying native representation.
124+
125+
```kotlin
126+
val a by boolSort
127+
val b by boolSort
128+
val expr = a and b
129+
130+
val (model, detachedModel) = KZ3Solver(this).use { solver ->
131+
solver.assert(expr)
132+
println(solver.check()) // SAT
133+
val model = solver.model()
134+
135+
// Detach model from solver
136+
val detachedModel = model.detach()
137+
138+
model to detachedModel
139+
}
140+
141+
try {
142+
model.eval(expr)
143+
} catch (ex: Exception) {
144+
println("Model no longer valid after solver close")
145+
}
146+
147+
println(detachedModel.eval(expr)) // true
148+
```
149+
150+
Note: it is usually a good idea to use `KModel.detach` when you need to keep model e.g. in a `List`.
151+
152+
### Solver runner
153+
154+
SMT solvers are implemented via native libraries and usually have the following issues:
155+
156+
1. Timeout ignore. SMT solver may hang in a long-running operation before reaching a checkpoint.
157+
Since solver is a native library, there is no way to interrupt it.
158+
2. Solvers are usually research projects with a bunch of bugs, e.g. pointer issues. Such
159+
errors lead to the interruption of the entire process, including the user's app.
160+
161+
To overcome these problems KSMT provides a solver runner that runs solvers in separate processes to preserve your
162+
application stability.
163+
164+
```kotlin
165+
// Create a long-lived solver manager that manages a pool of solver workers
166+
KSolverRunnerManager().use { solverManager ->
167+
168+
// Use solver API as usual
169+
with(ctx) {
170+
val a by boolSort
171+
val b by boolSort
172+
val expr = a and b
173+
174+
// Create solver using manager instead of direct constructor invocation
175+
solverManager.createSolver(this, KZ3Solver::class).use { solver ->
176+
solver.assert(expr)
177+
println(solver.check()) // SAT
178+
}
179+
}
180+
}
181+
```
182+
183+
### Using custom solvers in a runner
184+
185+
Solver runner also supports user defined solvers. Custom solvers must be registered before being used in the runner
186+
using `registerSolver`.
187+
188+
```kotlin
189+
// Create a long-lived solver manager that manages a pool of solver workers
190+
KSolverRunnerManager().use { solverManager ->
191+
// Register user-defined solver in a current manager
192+
solverManager.registerSolver(CustomZ3BasedSolver::class, KZ3SolverUniversalConfiguration::class)
193+
194+
// Use solver API as usual
195+
with(ctx) {
196+
val a by boolSort
197+
val b by boolSort
198+
val expr = a and b
199+
200+
// Create solver using manager instead of direct constructor invocation
201+
solverManager.createSolver(this, CustomZ3BasedSolver::class).use { solver ->
202+
solver.assert(expr)
203+
println(solver.check()) // SAT
204+
}
205+
}
206+
}
207+
```
208+
209+
### Solver portfolio
210+
211+
Various SMT solvers usually show different performance on a same SMT formula.
212+
[Portfolio solving](https://arxiv.org/abs/1505.03340) is a simple idea of
213+
running different solvers on the same formula simultaneously and waiting only
214+
for the first result.
215+
The portfolio solver workflow in KSMT is similar to the solver runner.
216+
217+
```kotlin
218+
// Create a long-lived portfolio solver manager that manages a pool of solver workers
219+
KPortfolioSolverManager(
220+
// Solvers to include in portfolio
221+
listOf(KZ3Solver::class, CustomZ3BasedSolver::class)
222+
).use { solverManager ->
223+
// Since we use user-defined solver in our portfolio we must register it in the current manager
224+
solverManager.registerSolver(CustomZ3BasedSolver::class, KZ3SolverUniversalConfiguration::class)
225+
226+
// Use solver API as usual
227+
with(ctx) {
228+
val a by boolSort
229+
val b by boolSort
230+
val expr = a and b
231+
232+
// Create portfolio solver using manager
233+
solverManager.createPortfolioSolver(this).use { solver ->
234+
solver.assert(expr)
235+
println(solver.check()) // SAT
236+
}
237+
}
238+
}
239+
```

docs/getting-started.md

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -340,39 +340,3 @@ with(ctx) {
340340
}
341341
}
342342
```
343-
344-
### Parsing formulas in SMT-LIB2 format
345-
346-
KSMT provides an API for parsing formulas in the SMT-LIB2 format.
347-
Currently, KSMT provides a parser implemented on top of the Z3 solver API
348-
and therefore `ksmt-z3` module is required for parsing.
349-
350-
```kotlin
351-
val formula = """
352-
(declare-fun x () Int)
353-
(declare-fun y () Int)
354-
(assert (>= x y))
355-
(assert (>= y x))
356-
"""
357-
with(ctx) {
358-
val assertions = KZ3SMTLibParser().parse(formula)
359-
}
360-
```
361-
362-
### Solver configuration
363-
364-
KSMT provides an API for modifying solver-specific parameters.
365-
Since the parameters and their correct values are solver-specific
366-
KSMT does not perform any checks.
367-
See the corresponding solver documentation for a list of available options.
368-
369-
```kotlin
370-
with(ctx) {
371-
KZ3Solver(this).use { solver ->
372-
solver.configure {
373-
// set Z3 solver parameter random_seed to 42
374-
setZ3Option("random_seed", 42)
375-
}
376-
}
377-
}
378-
```

examples/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ dependencies {
1212
implementation("io.ksmt:ksmt-core:0.5.3")
1313
// z3 solver
1414
implementation("io.ksmt:ksmt-z3:0.5.3")
15+
// Runner and portfolio solver
16+
implementation("io.ksmt:ksmt-runner:0.5.3")
1517
}
1618

1719
java {

0 commit comments

Comments
 (0)