Documentation
¶
Index ¶
- Variables
- func DefaultChooser(m ModelInterface) (int, bool)
- type Boolean
- type ConflictError
- type Literal
- type Model
- func (m *Model[T]) AtLeastOneOf(t ...T)
- func (m *Model[T]) AtMostOneOf(t ...T)
- func (m *Model[T]) Clause(literals ...*Literal)
- func (m *Model[T]) Dump()
- func (m *Model[T]) ImpliesAtLeastOneOf(t T, ti ...T)
- func (m *Model[T]) Literal(t T) *Literal
- func (m *Model[T]) NegatedLiteral(t T) *Literal
- func (m *Model[T]) NegatedUnary(t T)
- func (m *Model[T]) Reset()
- func (m *Model[T]) Unary(t T)
- func (m *Model[T]) Variables() iter.Seq2[T, Boolean]
- type ModelInterface
- type Solver
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var (
ErrUnexpected = errors.New("unexpected error")
)
Functions ¶
func DefaultChooser ¶
func DefaultChooser(m ModelInterface) (int, bool)
DefaultChooser selects a variable to set. This defaults to the first one found that is unset, and sets it to false.
Types ¶
type Boolean ¶
type Boolean struct {
// contains filtered or unexported fields
}
Boolean wraps up a boolean variable which may be undefined.
type ConflictError ¶
type ConflictError struct {
// contains filtered or unexported fields
}
ConflictError is returned when a clause resolves to false.
func (*ConflictError) Error ¶
func (e *ConflictError) Error() string
Error implements the error interface.
type Literal ¶
type Literal struct {
// Boolean allows the variable to notify subscribers of updates.
Boolean
// contains filtered or unexported fields
}
Literal is a reference to a variable used by a clause.
type Model ¶
type Model[T comparable] struct { // contains filtered or unexported fields }
Model implements a CNF model.
func (*Model[T]) AtLeastOneOf ¶
func (m *Model[T]) AtLeastOneOf(t ...T)
AtLeastOneOf is a helper that defines a clause: x1 v x2 v x3 v ... xN.
func (*Model[T]) AtMostOneOf ¶
func (m *Model[T]) AtMostOneOf(t ...T)
AtMostOneOf is a helper that defines a set of clauses: ^x1 v ^x2, ^x1 v ^x3, ..., ^xN-1 v ^xN.
func (*Model[T]) ImpliesAtLeastOneOf ¶
func (m *Model[T]) ImpliesAtLeastOneOf(t T, ti ...T)
ImpliesAtLeastOneOf is a helper that defines a clause: ^x1 v y1 v y2 v ... yN.
func (*Model[T]) NegatedLiteral ¶
NegatedLiteral gets a negated literal for use in a clause.
func (*Model[T]) NegatedUnary ¶
func (m *Model[T]) NegatedUnary(t T)
NegatedUnary adds a negated unary clause e.g. this must be false. NOTE: The assumption here is this is an initial condition for the problem being solved, and should not be preserved across resets.
func (*Model[T]) Reset ¶
func (m *Model[T]) Reset()
Reset resets the mode for reuse. Any unary clauses will be deleted.
type ModelInterface ¶
type ModelInterface interface {
// contains filtered or unexported methods
}
ModelInterface provides a type agnostic interface for the main solver algorithm.
type Solver ¶
type Solver struct {
// contains filtered or unexported fields
}
Solver implements CDCL (conflict driven clause learning).
Example ¶
package main
import (
"fmt"
"github.com/spjmurray/go-sat/pkg/cdcl"
)
//nolint:gochecknoglobals
var sudoku = [9][9]int{
{6, 0, 0, 0, 0, 3, 2, 0, 4},
{0, 4, 0, 2, 0, 0, 0, 9, 0},
{0, 0, 8, 0, 0, 0, 0, 5, 0},
{0, 0, 9, 0, 3, 0, 0, 0, 0},
{0, 0, 0, 6, 0, 0, 0, 0, 0},
{3, 0, 6, 0, 0, 0, 5, 4, 0},
{8, 0, 3, 0, 0, 2, 4, 0, 0},
{0, 0, 0, 1, 8, 0, 0, 6, 0},
{1, 6, 5, 0, 7, 0, 0, 0, 8},
}
// variable maps to a location on the grid and a value.
type variable struct {
i int
j int
n int
}
// sudokuRules adds Sudoku rules to the solver.
//
//nolint:cyclop
func sudokuRules(m *cdcl.Model[variable]) {
for i := range 9 {
for j := range 9 {
names := make([]variable, 9)
for n := range 9 {
names[n] = variable{i, j, n}
}
m.AtLeastOneOf(names...)
m.AtMostOneOf(names...)
}
for n := range 9 {
names := make([]variable, 9)
for j := range 9 {
names[j] = variable{i, j, n}
}
m.AtMostOneOf(names...)
}
}
for j := range 9 {
for n := range 9 {
names := make([]variable, 9)
for i := range 9 {
names[i] = variable{i, j, n}
}
m.AtMostOneOf(names...)
}
}
for i := 0; i < 9; i += 3 {
for j := 0; j < 9; j += 3 {
for n := range 9 {
names := make([]variable, 9)
for x := range 9 {
names[x] = variable{i + x/3, j + x%3, n}
}
m.AtMostOneOf(names...)
}
}
}
}
func sudokuInitialize(m *cdcl.Model[variable]) {
for i := range 9 {
for j := range 9 {
if sudoku[i][j] > 0 {
m.Unary(variable{i, j, sudoku[i][j] - 1})
}
}
}
}
func sudokuPrint(m *cdcl.Model[variable]) {
result := [9][9]int{}
for variable, value := range m.Variables() {
if value.Value() {
result[variable.i][variable.j] = variable.n + 1
}
}
fmt.Println("\u250c\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2510")
for i := range 9 {
if i != 0 && i%3 == 0 {
fmt.Println("\u251c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2524")
}
for j := range 9 {
if j%3 == 0 {
fmt.Print("\u2502")
}
if result[i][j] == 0 {
fmt.Print(" ")
} else {
fmt.Print(result[i][j])
}
}
fmt.Println("\u2502")
}
fmt.Println("\u2514\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2518")
}
func main() {
m := cdcl.NewModel[variable]()
// Add implicit rules that apply to all Sudoku problems.
sudokuRules(m)
sudokuInitialize(m)
s := cdcl.New()
if err := s.Solve(m, cdcl.DefaultChooser); err != nil {
fmt.Println(err)
}
sudokuPrint(m)
}
Output: ┌───┬───┬───┐ │691│753│284│ │547│218│396│ │238│496│157│ ├───┼───┼───┤ │489│531│672│ │752│649│831│ │316│827│549│ ├───┼───┼───┤ │873│962│415│ │924│185│763│ │165│374│928│ └───┴───┴───┘
func (*Solver) Solve ¶
func (s *Solver) Solve(m ModelInterface, decide func(ModelInterface) (int, bool)) error
Solve does the top level solving of the SAT problem. It accepts a custom decision function so the client can choose how to select a candidate when trial and error is required. For example, you may maintain some domain specific knowledge about variables and clauses and be able to make more sensible choices than an arbitrary selector.