centipede

package module
v1.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 20, 2022 License: Apache-2.0 Imports: 6 Imported by: 4

README

Centipede - Constraint Satisfaction Problem Solver for Go

badge

Centipede is a Constraint Satisfaction Problem solver written in Golang. Learn more about CSPs.

There is also a very informative slide deck about CSPs available from Stanford University here.

Features

  • Problems are defined using sets of Variable, Constraint, and Domain. Some convenient generators have been provided for Constraint and Domain.
  • Variable values can be set to values of any comparable data type in Go (using generic types). Mixing datatypes in variables that are compared to each other is not currently possible.
  • The search algorithm used in this library is an implementation of backtracking search.
  • The solution of many complex problems can be simplified by enforcing arc consistency. This library provides an implementation of the popular AC-3 algorithm as solver.State.MakeArcConsistent(). Call this method before calling solver.Solve() to achieve best results.
    • See the Sudoku solver for an example of how to use arc consistency.

Project Status

As of March 2022, this project has been updated to take advantage of the generic types that were added in Go 1.18. Previously, this project was relying heavily on storing values using a type of interface{} and casting the values to their respective types (i.e. value.(int)) when necessary. This was extremely inconvenient, and now, three years after it was first created, generic types have greatly simplified library usage. For more information on how generic types work in Go, see the Go 1.18 Release Notes as well as the very detailed Type Parameters Proposal.

The project is very much a work in progress. Here are some planned future improvements:

  • I have plans to implement the minimum remaining values (MRV) heuristic, the least constraining value (LCV) heuristic, and the degree heuristic.
  • It would also be nice to have some better documentation.

Examples

An example usage of the library is provided below:

// some integer variables
vars := centipede.Variables[int]{
  centipede.NewVariable("A", centipede.IntRange(1, 10)),
  centipede.NewVariable("B", centipede.IntRange(1, 10)),
  centipede.NewVariable("C", centipede.IntRange(1, 10)),
  centipede.NewVariable("D", centipede.IntRange(1, 10)),
  centipede.NewVariable("E", centipede.IntRangeStep(0, 20, 2)), // even numbers < 20
}

// numeric constraints
constraints := centipede.Constraints[int]{
  // using some constraint generators
  centipede.Equals[int]("A", "D"), // A = D
  // here we implement a custom constraint
  centipede.Constraint[int]{Vars: centipede.VariableNames{"A", "E"}, // E = A * 2
    ConstraintFunction: func(variables *centipede.Variables[int]) bool {
      // here we have to use type assertion for numeric methods since
      // Variable.Value is stored as interface{}
      if variables.Find("E").Empty || variables.Find("A").Empty {
        return true
      }
      return variables.Find("E").Value == variables.Find("A").Value*2
    }},
}
constraints = append(constraints, centipede.AllUnique[int]("A", "B", "C", "E")...) // A != B != C != E

// solve the problem
solver := centipede.NewBackTrackingCSPSolver(vars, constraints)
begin := time.Now()
success := solver.Solve() // run the solution
elapsed := time.Since(begin)

// output results and time elapsed
if success {
  fmt.Printf("Found solution in %s\n", elapsed)
  for _, variable := range solver.State.Vars {
    // print out values for each variable
    fmt.Printf("Variable %v = %v\n", variable.Name, variable.Value)
  }
} else {
  fmt.Printf("Could not find solution in %s\n", elapsed)
}

Unit tests have been provided to serve as additional examples of how to use the library.

Documentation

Godocs are available here on pkg.go.dev.

Installation

go get github.com/gnboorse/[email protected]

So far, this project has only been tested on macOS and Linux.

Running tests

go test -v

Contributing

Feel free to make a pull request if you spot anything out of order or want to improve the project!

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrExecutionCanceled error = errors.New("execution canceled")
)

Functions

func RunWithContext added in v1.0.1

func RunWithContext[T any](ctx context.Context, f func() T) (*T, error)

RunWithContext accepts a context and a function that produces T The function will be run, and so long as the context is not done, its result will be returned. Otherwise an error will be returned.

Types

type BackTrackingCSPSolver

type BackTrackingCSPSolver[T comparable] struct {
	State CSPState[T]
}

BackTrackingCSPSolver struct for holding solver state

func NewBackTrackingCSPSolver

func NewBackTrackingCSPSolver[T comparable](vars Variables[T], constraints Constraints[T]) BackTrackingCSPSolver[T]

NewBackTrackingCSPSolver create a solver

func NewBackTrackingCSPSolverWithPropagation

func NewBackTrackingCSPSolverWithPropagation[T comparable](vars Variables[T], constraints Constraints[T], propagations Propagations[T]) BackTrackingCSPSolver[T]

NewBackTrackingCSPSolverWithPropagation create a solver

func (*BackTrackingCSPSolver[T]) Solve

func (solver *BackTrackingCSPSolver[T]) Solve(ctx context.Context) (bool, error)

Solve solves for values in the CSP

type CSPState

type CSPState[T comparable] struct {
	Vars        Variables[T]
	Constraints Constraints[T]
	Propagations[T]
}

CSPState state object for CSP Solver

func (*CSPState[T]) MakeArcConsistent

func (state *CSPState[T]) MakeArcConsistent(ctx context.Context) error

MakeArcConsistent algorithm based off of AC-3 used to make the given CSP fully arc consistent. https://en.wikipedia.org/wiki/AC-3_algorithm

func (*CSPState[T]) SimplifyPreAssignment

func (state *CSPState[T]) SimplifyPreAssignment(ctx context.Context) error

SimplifyPreAssignment basic constraint propagation algorithm used to simplify variable domains before solving based on variables already assigned to. Condition: if a variable has been assigned to with a given value, remove that value from the domain of all variables mutually exclusive to it, i.e. if A != B and B = 2, remove 2 from the domain of A. Use of this algorith is not recommended. Enforce arc consistency instead.

type Constraint

type Constraint[T comparable] struct {
	Vars               VariableNames
	ConstraintFunction VariablesConstraintFunction[T]
}

Constraint CSP constraint considering integer variables

func Equals

func Equals[T comparable](var1 VariableName, var2 VariableName) Constraint[T]

Equals Constraint generator that checks if two vars are equal

func GreaterThan

GreaterThan Constraint generator that checks if first variable is less than second variable

func GreaterThanOrEqualTo

func GreaterThanOrEqualTo[T constraints.Integer | constraints.Float](var1 VariableName, var2 VariableName) Constraint[T]

GreaterThanOrEqualTo Constraint generator that checks if first variable is less than or equal to second variable

func LessThan

LessThan Constraint generator that checks if first variable is less than second variable

func LessThanOrEqualTo

func LessThanOrEqualTo[T constraints.Integer | constraints.Float](var1 VariableName, var2 VariableName) Constraint[T]

LessThanOrEqualTo Constraint generator that checks if first variable is less than or equal to second variable

func NotEquals

func NotEquals[T comparable](var1 VariableName, var2 VariableName) Constraint[T]

NotEquals Constraint generator that checks if two vars are not equal

func UnaryEquals

func UnaryEquals[T comparable](var1 VariableName, value interface{}) Constraint[T]

UnaryEquals Unary constraint that checks if var1 equals some constant

func UnaryNotEquals

func UnaryNotEquals[T comparable](var1 VariableName, value interface{}) Constraint[T]

UnaryNotEquals Unary constraint that checks if var1 is not equal to some constant

func (*Constraint[T]) Satisfied

func (constraint *Constraint[T]) Satisfied(variables *Variables[T]) bool

Satisfied checks to see if the given Constraint is satisfied by the variables presented

type Constraints

type Constraints[T comparable] []Constraint[T]

Constraints collection type for Constraint

func AllEquals

func AllEquals[T comparable](varnames ...VariableName) Constraints[T]

AllEquals Constraint generator that checks that all given variables are equal

func AllUnique

func AllUnique[T comparable](varnames ...VariableName) Constraints[T]

AllUnique Constraint generator to check if all variable values are unique

func (*Constraints[T]) AllSatisfied

func (constraints *Constraints[T]) AllSatisfied(variables *Variables[T]) bool

AllSatisfied check if a collection of Constraints are satisfied

func (*Constraints[T]) FilterByName

func (constraints *Constraints[T]) FilterByName(name VariableName) Constraints[T]

FilterByName return all constraints related to a particular variable name

func (*Constraints[T]) FilterByOrder

func (constraints *Constraints[T]) FilterByOrder(order int) Constraints[T]

FilterByOrder return all constraints with the given order (number of related variables)

type Domain

type Domain[T comparable] []T

Domain domain object

func FloatRange

func FloatRange(start float64, end float64) Domain[float64]

FloatRange returns a slice of integers in the desired range with a step of 1

func FloatRangeStep

func FloatRangeStep(start float64, end float64, step float64) Domain[float64]

FloatRangeStep returns a slice of integers in the desired range with the given step

func Generator

func Generator[T comparable](inputDomain Domain[T], fx func(T) T) Domain[T]

Generator generates a Domain from another input domain and a function f(x). For example:

func IntRange

func IntRange(start int, end int) Domain[int]

IntRange returns a slice of integers in the desired range with a step of 1

func IntRangeStep

func IntRangeStep(start int, end int, step int) Domain[int]

IntRangeStep returns a slice of integers in the desired range with the given step

func TimeRange

func TimeRange(start time.Time, end time.Time) Domain[time.Time]

TimeRange get the range of days from the start to the end time

func TimeRangeStep

func TimeRangeStep(start time.Time, end time.Time, step time.Duration) Domain[time.Time]

TimeRangeStep get the range of time between start to end with step as a Duration (in nanoseconds).

func (*Domain[T]) Contains

func (domain *Domain[T]) Contains(value T) bool

Contains slice contains method for Domain

func (Domain[T]) Remove

func (domain Domain[T]) Remove(value T) Domain[T]

Remove given a value and return the updated domain

type DomainRemoval

type DomainRemoval[T comparable] struct {
	// VariableName the variable whose domain we are pruning
	VariableName
	// Value the value to prune from its domain
	Value T
}

DomainRemoval a struct indicating removing an item from a domain

type DomainRemovals

type DomainRemovals[T comparable] []DomainRemoval[T]

DomainRemovals list type for DomainRemoval

type Propagation

type Propagation[T comparable] struct {
	Vars VariableNames
	PropagationFunction[T]
}

Propagation type representing a domain propagation

func (*Propagation[T]) Execute

func (propagation *Propagation[T]) Execute(assignment VariableAssignment[T], variables *Variables[T]) []DomainRemoval[T]

Execute this Propagation

type PropagationFunction

type PropagationFunction[T comparable] func(assignment VariableAssignment[T], variables *Variables[T]) []DomainRemoval[T]

PropagationFunction used to determine domain pruning

type Propagations

type Propagations[T comparable] []Propagation[T]

Propagations list type for Propagation

func (*Propagations[T]) Execute

func (propagations *Propagations[T]) Execute(assignment VariableAssignment[T], variables *Variables[T]) []DomainRemoval[T]

Execute all propagations in the list

type Variable

type Variable[T comparable] struct {
	Name   VariableName
	Value  T
	Domain Domain[T]
	Empty  bool
}

Variable indicates a CSP variable of interface{} type

func NewVariable

func NewVariable[T comparable](name VariableName, domain Domain[T]) Variable[T]

NewVariable constructor for Variable type

func (*Variable[T]) SetDomain

func (variable *Variable[T]) SetDomain(domain Domain[T])

SetDomain set the domain of the given variable

func (*Variable[T]) SetValue

func (variable *Variable[T]) SetValue(value T)

SetValue setter for Variable value field

func (*Variable[T]) Unset

func (variable *Variable[T]) Unset()

Unset the variable

type VariableAssignment

type VariableAssignment[T comparable] struct {
	// VariableName the variable that was assigned to
	VariableName
	// Value the value being assigned
	Value T
}

VariableAssignment a struct indicating assigning a value to a variable

type VariableName

type VariableName string

VariableName is our string type for names of variables

type VariableNames

type VariableNames []VariableName

VariableNames collection type for VariableName

func (*VariableNames) Contains

func (varnames *VariableNames) Contains(varname VariableName) bool

Contains slice contains method for VariableNames

type Variables

type Variables[T comparable] []Variable[T]

Variables collection type for interface{} type variables

func (*Variables[T]) Complete

func (variables *Variables[T]) Complete() bool

Complete indicates if all variables have been assigned to

func (*Variables[T]) Contains

func (variables *Variables[T]) Contains(name VariableName) bool

Contains slice contains method for Variables

func (*Variables[T]) EvaluateDomainRemovals

func (variables *Variables[T]) EvaluateDomainRemovals(domainRemovals DomainRemovals[T])

EvaluateDomainRemovals remove values from domain based on DomainRemovals in propagation

func (*Variables[T]) Find

func (variables *Variables[T]) Find(name VariableName) *Variable[T]

Find find an Variable by name in an Variables collection

func (*Variables[T]) ResetDomainRemovalEvaluation

func (variables *Variables[T]) ResetDomainRemovalEvaluation(domainRemovals DomainRemovals[T])

ResetDomainRemovalEvaluation undo pruning on a variable's domain

func (*Variables[T]) SetDomain

func (variables *Variables[T]) SetDomain(name VariableName, domain Domain[T])

SetDomain set the domain of the given variable by name

func (*Variables[T]) SetValue

func (variables *Variables[T]) SetValue(name VariableName, value T)

SetValue setter for Variables collection

func (*Variables[T]) Unassigned

func (variables *Variables[T]) Unassigned() int

Unassigned return the number of unassigned variables

func (*Variables[T]) Unset

func (variables *Variables[T]) Unset(name VariableName)

Unset unset a variable with the given name

type VariablesConstraintFunction

type VariablesConstraintFunction[T comparable] func(variables *Variables[T]) bool

VariablesConstraintFunction function used to determine validity of Variables

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL