This framework allows solving arbitrary Constraint Satisfaction Problems (CSP) with finite domain.
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}dependencies {
implementation 'com.github.RedShuhart:csp-framework:470991d413'
}<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories><dependency>
<groupId>com.github.RedShuhart</groupId>
<artifactId>csp-framework</artifactId>
<version>470991d413</version>
</dependency>import com.tsovedenski.csp.*
import com.tsovedenski.csp.strategies.Backtracking
fun main(args: Array<String>) {
val problem = Problem(
variables = listOf('a', 'b', 'c'),
domain = listOf(1, 2, 3, 4, 5),
constraints = listOf(
UnaryConstraint('a') { it in 3..4 },
BinaryConstraint('a', 'b') { a, b -> a != b },
BinaryConstraint('b', 'c') { b, c -> b + c > 4 }
)
)
val solution = problem.solve(Backtracking())
when (solution) {
is NoSolution -> println("No solution found")
is Solved -> solution.assignment.print()
}
// a -> 3
// b -> 1
// c -> 4
}A constraint is a predicate of Assignment.
There are several constraints:
UnaryConstraint- constraint on a single variable.BinaryConstraint- constraint between two variables.AllConstraint- constraint between all variables.AllDiffConstraint- all variables' values should be different.
GeneralConstraint- constraint that allows arbitrary logic with arbitrary number of variables.
Solvable::solve accepts a Strategy as a parameter.
Strategy is responsible for solving the problem.
With this framework there are two strategies included: backtracking and generate-and-test.
Backtracking accepts a number of parameters, some of which are variable ordering and pruning scheme.
By default, the order in which variables are defined is used.
You can alter the way variables are selected by passing one of the 4 options to variableOrdering parameter:
SmallestDomainVariable- select the variable with smallest domain.BiggestDomainVariable- select the variable with biggest domain.MostFrequentVariable- select the variable which is used in most constraints.LeastFrequentVariable- select the variable used in smallest amount of constraints.
By default no pruning is done.
You can select a pruning scheme by passing one of the 3 options to pruneScheme parameter:
ForwardCheckingPartialLookAheadFullLookAhead
If you want to create your own class that can be solved, you have to implement Solvable.
In other words - define a CSP problem: variables, domains and constraints.
Problem can be defined in 3 ways (constructors):
(domains, constraints)- the canonical representation, wheredomainsis a map from variable to a list of values.(variables, domain, constraints)- heredomaingets copied to each variable.(variables, constraints, domainMapper)- here the functiondomainMapperallows arbitrary logic that maps each variable to a domain.
For examples, see csp-sudoku, csp-nqueens, csp-wordsum and csp-scheduling.
In order to generate the API documentation, run ./gradlew dokka.
The result will be in $project/build/javadoc.
Alternatively, run ./gradlew $project:dokka to generate the docs for $project.
(There is documentation for csp-framework only.)
If you want to see which strategy (together with its parameters) is better for a given problem, you can use Benchmark.
It runs the problem several times and takes the average amount of checks and time it's taken to solve.
For example, to see the difference between pruning schemes, we'd write the following:
val runs = 5
val warmup = 2 // run 2 times beforehand for each strategy
val benchmark = Benchmark(problem, runs, warmup, mapOf(
"no prune" to Backtracking(),
"FC" to Backtracking(pruneSchema = ForwardChecking()),
"PLA" to Backtracking(pruneSchema = PartialLookAhead()),
"FLA" to Backtracking(pruneSchema = FullLookAhead())
))
benchmark.execute().prettyPrint()