Categorygithub.com/RoelofRuis/propagator
repositorypackage
1.3.0
Repository: https://github.com/roelofruis/propagator.git
Documentation: pkg.go.dev

# Packages

No description provided by the author

# README

Propagator

A library to assist in solving constraint satisfaction problems using constraint propagation.

See the examples folder for applied examples.

How to use

A constraint satisfaction problem consists of variables that can each take on one of multiple values. These values are called the variable domain. Constraints define relations between these variables, allowing to iteratively reduce the variable domains, until either a solution is found, or the problem turns out to be unsolvable, meaning there is no combination of values that satisfy all constraints.

The first step is defining your problem in terms of variables and constraints. Once you have done that, proceed with writing the implementation as per the next sections.

1 - Define a new Constraint Satisfaction Problem

Create a new problem instance on which we will define our variables and constraints. This object serves as a builder for the model.

csp := propagator.NewProblem()

2 - Add your variables

Define and add the variables for which a value need to be selected.

In the instance of a sudoku puzzle, the variables are the cells with their respective states. The domain values are of type int (the numbers 1 to 9). We could define the Cell type as such:

type Cell = propagator.Variable[int]

Add the variables to the CSP using AddVariable or AddVariableFromValues, the latter is useful when we know in advance that all our values will have equal probability.

v0 *Cell := propagator.AddVariableFromValues[int](csp, "v0", []int{1,2,3,4,5,6,7,8,9})
v1 *Cell := propagator.AddVariableFromValues[int](csp, "v1", []int{1,2,3,4,5,6,7,8,9})

The returned variable can then be used in your constraints.

3 - Add your constraints

Define the constraints that apply to these variables.

To define a constraint, implement the propagator.Constraint interface.

In a sudoku puzzle one of the constraints can be a House (combination of 9 cells that contain unique values).

type House struct {
	Cells []*Cell
}

func (h House) Scope() []DomainId {
	return propagator.IdsOf(h.Cells)
}

func (h House) Propagate(mutator *propagator.Mutator) {
	/* ... logic omitted ... */
}

Scope should return a list of DomainId that this constraint applies to. Because each Variable is a Domain, these can be easily extracted.

The implementation of Propagate holds the logic that defines the rules of the problem to be solved. By passing different mutations to the mutator, changes to the domain are defined. Often, optimizing this implementation can speed up the solution process significantly.

Add instances of your constraints to the CSP by calling AddConstraint.

house := House{Cells: []*Cell{v0, v1}}

csp.AddConstraint(house)

4 - Build the model

Build the model by calling Model() on the Problem.

model := csp.Model()

5 - Solve the model

Solve the model using a solver. Additional SolverOptions can be passed when creating a new solver.

solver := propagator.NewSolver(/* options */)

solved := solver.Solve(model)

if (!solved) {
    fmt.Printf("no solution!")
} else {
    fmt.Printf("%s", v0.GetFixedValue())
    fmt.Printf("%s", v1.GetFixedValue())
}

Advanced Usage

Hidden variables

If you want to define variables that should take part in constraint propagation but should not be picked and solved for (for instance when using the domain values as a constraint to the problem), you can mark these variables as hidden.

propagator.AddHiddenVariableFromValues[int](csp, "v", []int{1,2,3,4,5,6,7,8,9})

Solving strategies

Using the SelectDomainsBy* and SelectIndicesBy* solver options, various solving strategies can be configured.

Resources

Excellent overview of constraint satisfaction problems: http://aima.cs.berkeley.edu/newchap05.pdf

Slideshow with summary of most important points: https://www.cse.unsw.edu.au/~cs3411/18s1/lect/1page/wk08_CSP.pdf