cdcl

package
v0.4.1 Latest Latest
Warning

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

Go to latest
Published: Nov 29, 2024 License: Apache-2.0 Imports: 7 Imported by: 0

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
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.

func NewBoolean

func NewBoolean() Boolean

NewBoolean creates a new boolean value.

func (Boolean) Defined

func (b Boolean) Defined() bool

Defined returns whether the variable is set.

func (Boolean) Undefined

func (b Boolean) Undefined() bool

Undefined returns whether the variable is unset.

func (Boolean) Value

func (b Boolean) Value() bool

Value returns the boolean value. Defaults to false if unset.

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.

func (*Literal) String

func (l *Literal) String() string

String formats a literal.

type Model

type Model[T comparable] struct {
	// contains filtered or unexported fields
}

Model implements a CNF model.

func NewModel

func NewModel[T comparable]() *Model[T]

New returns a new 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]) Clause

func (m *Model[T]) Clause(literals ...*Literal)

Clause defines a new clause from a set of literals.

func (*Model[T]) Dump

func (m *Model[T]) Dump()

Dump prints the model state to the console.

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]) Literal

func (m *Model[T]) Literal(t T) *Literal

Literal gets a literal for use in a clause.

func (*Model[T]) NegatedLiteral

func (m *Model[T]) NegatedLiteral(t T) *Literal

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.

func (*Model[T]) Unary

func (m *Model[T]) Unary(t T)

Unary adds a unary clause e.g. this must be true. 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]) Variables

func (m *Model[T]) Variables() iter.Seq2[T, Boolean]

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 New

func New() *Solver

New creates a new CDCL solver.

func (*Solver) Dump

func (s *Solver) Dump()

Dump prints solver state to the console.

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.

Jump to

Keyboard shortcuts

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