mirror of
https://github.com/mudler/luet.git
synced 2025-08-24 18:08:38 +00:00
update vendor
This commit is contained in:
parent
4d6cccb2fa
commit
77c4bf1fd1
149
vendor/github.com/crillab/gophersat/explain/check.go
generated
vendored
Normal file
149
vendor/github.com/crillab/gophersat/explain/check.go
generated
vendored
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
// Package explain provides facilities to check and understand UNSAT instances.
|
||||||
|
package explain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/crillab/gophersat/solver"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Options is a set of options that can be set to true during the checking process.
|
||||||
|
type Options struct {
|
||||||
|
// If Verbose is true, information about resolution will be written on stdout.
|
||||||
|
Verbose bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks whether the clause satisfies the problem or not.
|
||||||
|
// Will return true if the problem is UNSAT, false if it is SAT or indeterminate.
|
||||||
|
func unsat(pb *Problem, clause []int) bool {
|
||||||
|
oldUnits := make([]int, len(pb.units))
|
||||||
|
copy(oldUnits, pb.units)
|
||||||
|
// lits is supposed to be implied by the problem.
|
||||||
|
// We add the negation of each lit as a unit clause to see if this is true.
|
||||||
|
for _, lit := range clause {
|
||||||
|
if lit > 0 {
|
||||||
|
pb.units[lit-1] = -1
|
||||||
|
} else {
|
||||||
|
pb.units[-lit-1] = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res := pb.unsat()
|
||||||
|
pb.units = oldUnits // We must restore the previous state
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnsatChan will wait RUP clauses from ch and use them as a certificate.
|
||||||
|
// It will return true iff the certificate is valid, i.e iff it makes the problem UNSAT
|
||||||
|
// through unit propagation.
|
||||||
|
// If pb.Options.ExtractSubset is true, a subset will also be extracted for that problem.
|
||||||
|
func (pb *Problem) UnsatChan(ch chan string) (valid bool, err error) {
|
||||||
|
defer pb.restore()
|
||||||
|
pb.initTagged()
|
||||||
|
for line := range ch {
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, err := strconv.Atoi(fields[0]); err != nil {
|
||||||
|
// This is not a clause: ignore the line
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
clause, err := parseClause(fields)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if !unsat(pb, clause) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
if len(clause) == 0 {
|
||||||
|
// This is the empty and unit propagation made the problem UNSAT: we're done.
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
// Since clause is a logical consequence, append it to the problem
|
||||||
|
pb.Clauses = append(pb.Clauses, clause)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we did not send any information through the channel
|
||||||
|
// It implies that the problem is trivially unsatisfiable
|
||||||
|
// Since we had only unit clauses inside the channel.
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unsat will parse a certificate, and return true iff the certificate is valid, i.e iff it makes the problem UNSAT
|
||||||
|
// through unit propagation.
|
||||||
|
// If pb.Options.ExtractSubset is true, a subset will also be extracted for that problem.
|
||||||
|
func (pb *Problem) Unsat(cert io.Reader) (valid bool, err error) {
|
||||||
|
defer pb.restore()
|
||||||
|
pb.initTagged()
|
||||||
|
sc := bufio.NewScanner(cert)
|
||||||
|
for sc.Scan() {
|
||||||
|
line := sc.Text()
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, err := strconv.Atoi(fields[0]); err != nil {
|
||||||
|
// This is not a clause: ignore the line
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
clause, err := parseClause(fields)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if !unsat(pb, clause) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
// Since clause is a logical consequence, append it to the problem
|
||||||
|
pb.Clauses = append(pb.Clauses, clause)
|
||||||
|
}
|
||||||
|
if err := sc.Err(); err != nil {
|
||||||
|
return false, fmt.Errorf("could not parse certificate: %v", err)
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrNotUnsat is the error returned when trying to get the MUS or UnsatSubset of a satisfiable problem.
|
||||||
|
var ErrNotUnsat = fmt.Errorf("problem is not UNSAT")
|
||||||
|
|
||||||
|
// UnsatSubset returns an unsatisfiable subset of the problem.
|
||||||
|
// The subset is not guaranteed to be a MUS, meaning some clauses of the resulting
|
||||||
|
// problem might be removed while still keeping the unsatisfiability of the problem.
|
||||||
|
// However, this method is much more efficient than extracting a MUS, as it only calls
|
||||||
|
// the SAT solver once.
|
||||||
|
func (pb *Problem) UnsatSubset() (subset *Problem, err error) {
|
||||||
|
solverPb := solver.ParseSlice(pb.Clauses)
|
||||||
|
if solverPb.Status == solver.Unsat {
|
||||||
|
// Problem is trivially UNSAT
|
||||||
|
// Make a copy so that pb and pb2 are not the same value.
|
||||||
|
pb2 := *pb
|
||||||
|
return &pb2, nil
|
||||||
|
}
|
||||||
|
s := solver.New(solver.ParseSlice(pb.Clauses))
|
||||||
|
s.Certified = true
|
||||||
|
s.CertChan = make(chan string)
|
||||||
|
status := solver.Unsat
|
||||||
|
go func() {
|
||||||
|
status = s.Solve()
|
||||||
|
close(s.CertChan)
|
||||||
|
}()
|
||||||
|
if valid, err := pb.UnsatChan(s.CertChan); !valid || status == solver.Sat {
|
||||||
|
return nil, ErrNotUnsat
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not solve problem: %v", err)
|
||||||
|
}
|
||||||
|
subset = &Problem{
|
||||||
|
NbVars: pb.NbVars,
|
||||||
|
}
|
||||||
|
for i, clause := range pb.Clauses {
|
||||||
|
if pb.tagged[i] {
|
||||||
|
// clause was used to prove pb is UNSAT: it's part of the subset
|
||||||
|
subset.Clauses = append(subset.Clauses, clause)
|
||||||
|
subset.NbClauses++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return subset, nil
|
||||||
|
}
|
213
vendor/github.com/crillab/gophersat/explain/mus.go
generated
vendored
Normal file
213
vendor/github.com/crillab/gophersat/explain/mus.go
generated
vendored
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
package explain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/crillab/gophersat/solver"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MUSMaxSat returns a Minimal Unsatisfiable Subset for the problem using the MaxSat strategy.
|
||||||
|
// A MUS is an unsatisfiable subset such that, if any of its clause is removed,
|
||||||
|
// the problem becomes satisfiable.
|
||||||
|
// A MUS can be useful to understand why a problem is UNSAT, but MUSes are expensive to compute since
|
||||||
|
// a SAT solver must be called several times on parts of the original problem to find them.
|
||||||
|
// With the MaxSat strategy, the function computes the MUS through several calls to MaxSat.
|
||||||
|
func (pb *Problem) MUSMaxSat() (mus *Problem, err error) {
|
||||||
|
pb2 := pb.clone()
|
||||||
|
nbVars := pb2.NbVars
|
||||||
|
NbClauses := pb2.NbClauses
|
||||||
|
weights := make([]int, NbClauses) // Weights of each clause
|
||||||
|
relaxLits := make([]solver.Lit, NbClauses) // Set of all relax lits
|
||||||
|
relaxLit := nbVars + 1 // Index of last used relax lit
|
||||||
|
for i, clause := range pb2.Clauses {
|
||||||
|
pb2.Clauses[i] = append(clause, relaxLit)
|
||||||
|
relaxLits[i] = solver.IntToLit(int32(relaxLit))
|
||||||
|
weights[i] = 1
|
||||||
|
relaxLit++
|
||||||
|
}
|
||||||
|
prob := solver.ParseSlice(pb2.Clauses)
|
||||||
|
prob.SetCostFunc(relaxLits, weights)
|
||||||
|
s := solver.New(prob)
|
||||||
|
s.Verbose = pb.Options.Verbose
|
||||||
|
var musClauses [][]int
|
||||||
|
done := make([]bool, NbClauses) // Indicates whether a clause is already part of MUS or not yet
|
||||||
|
for {
|
||||||
|
cost := s.Minimize()
|
||||||
|
if cost == -1 {
|
||||||
|
return makeMus(nbVars, musClauses), nil
|
||||||
|
}
|
||||||
|
if cost == 0 {
|
||||||
|
return nil, fmt.Errorf("cannot extract MUS from satisfiable problem")
|
||||||
|
}
|
||||||
|
model := s.Model()
|
||||||
|
for i, clause := range pb.Clauses {
|
||||||
|
if !done[i] && !satClause(clause, model) {
|
||||||
|
// The clause is part of the MUS
|
||||||
|
pb2.Clauses = append(pb2.Clauses, []int{-(nbVars + i + 1)}) // Now, relax lit has to be false
|
||||||
|
pb2.NbClauses++
|
||||||
|
musClauses = append(musClauses, clause)
|
||||||
|
done[i] = true
|
||||||
|
// Make it a hard clause before restarting solver
|
||||||
|
lits := make([]solver.Lit, len(clause))
|
||||||
|
for j, lit := range clause {
|
||||||
|
lits[j] = solver.IntToLit(int32(lit))
|
||||||
|
}
|
||||||
|
s.AppendClause(solver.NewClause(lits))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if pb.Options.Verbose {
|
||||||
|
fmt.Printf("c Currently %d/%d clauses in MUS\n", len(musClauses), NbClauses)
|
||||||
|
}
|
||||||
|
prob = solver.ParseSlice(pb2.Clauses)
|
||||||
|
prob.SetCostFunc(relaxLits, weights)
|
||||||
|
s = solver.New(prob)
|
||||||
|
s.Verbose = pb.Options.Verbose
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// true iff the clause is satisfied by the model
|
||||||
|
func satClause(clause []int, model []bool) bool {
|
||||||
|
for _, lit := range clause {
|
||||||
|
if (lit > 0 && model[lit-1]) || (lit < 0 && !model[-lit-1]) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeMus(nbVars int, clauses [][]int) *Problem {
|
||||||
|
mus := &Problem{
|
||||||
|
Clauses: clauses,
|
||||||
|
NbVars: nbVars,
|
||||||
|
NbClauses: len(clauses),
|
||||||
|
units: make([]int, nbVars),
|
||||||
|
}
|
||||||
|
for _, clause := range clauses {
|
||||||
|
if len(clause) == 1 {
|
||||||
|
lit := clause[0]
|
||||||
|
if lit > 0 {
|
||||||
|
mus.units[lit-1] = 1
|
||||||
|
} else {
|
||||||
|
mus.units[-lit-1] = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mus
|
||||||
|
}
|
||||||
|
|
||||||
|
// MUSInsertion returns a Minimal Unsatisfiable Subset for the problem using the insertion method.
|
||||||
|
// A MUS is an unsatisfiable subset such that, if any of its clause is removed,
|
||||||
|
// the problem becomes satisfiable.
|
||||||
|
// A MUS can be useful to understand why a problem is UNSAT, but MUSes are expensive to compute since
|
||||||
|
// a SAT solver must be called several times on parts of the original problem to find them.
|
||||||
|
// The insertion algorithm is efficient is many cases, as it calls the same solver several times in a row.
|
||||||
|
// However, in some cases, the number of calls will be higher than using other methods.
|
||||||
|
// For instance, if called on a formula that is already a MUS, it will perform n*(n-1) calls to SAT, where
|
||||||
|
// n is the number of clauses of the problem.
|
||||||
|
func (pb *Problem) MUSInsertion() (mus *Problem, err error) {
|
||||||
|
pb2, err := pb.UnsatSubset()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not extract MUS: %v", err)
|
||||||
|
}
|
||||||
|
mus = &Problem{NbVars: pb2.NbVars}
|
||||||
|
clauses := pb2.Clauses
|
||||||
|
for {
|
||||||
|
if pb.Options.Verbose {
|
||||||
|
fmt.Printf("c mus currently contains %d clauses\n", mus.NbClauses)
|
||||||
|
}
|
||||||
|
s := solver.New(solver.ParseSliceNb(mus.Clauses, mus.NbVars))
|
||||||
|
s.Verbose = pb.Options.Verbose
|
||||||
|
st := s.Solve()
|
||||||
|
if st == solver.Unsat { // Found the MUS
|
||||||
|
return mus, nil
|
||||||
|
}
|
||||||
|
// Add clauses until the problem becomes UNSAT
|
||||||
|
idx := 0
|
||||||
|
for st == solver.Sat {
|
||||||
|
clause := clauses[idx]
|
||||||
|
lits := make([]solver.Lit, len(clause))
|
||||||
|
for i, lit := range clause {
|
||||||
|
lits[i] = solver.IntToLit(int32(lit))
|
||||||
|
}
|
||||||
|
cl := solver.NewClause(lits)
|
||||||
|
s.AppendClause(cl)
|
||||||
|
idx++
|
||||||
|
st = s.Solve()
|
||||||
|
}
|
||||||
|
idx-- // We went one step too far, go back
|
||||||
|
mus.Clauses = append(mus.Clauses, clauses[idx]) // Last clause is part of the MUS
|
||||||
|
mus.NbClauses++
|
||||||
|
if pb.Options.Verbose {
|
||||||
|
fmt.Printf("c removing %d/%d clause(s)\n", len(clauses)-idx, len(clauses))
|
||||||
|
}
|
||||||
|
clauses = clauses[:idx] // Remaining clauses are not part of the MUS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MUSDeletion returns a Minimal Unsatisfiable Subset for the problem using the insertion method.
|
||||||
|
// A MUS is an unsatisfiable subset such that, if any of its clause is removed,
|
||||||
|
// the problem becomes satisfiable.
|
||||||
|
// A MUS can be useful to understand why a problem is UNSAT, but MUSes are expensive to compute since
|
||||||
|
// a SAT solver must be called several times on parts of the original problem to find them.
|
||||||
|
// The deletion algorithm is guaranteed to call exactly n SAT solvers, where n is the number of clauses in the problem.
|
||||||
|
// It can be quite efficient, but each time the solver is called, it is starting from scratch.
|
||||||
|
// Other methods keep the solver "hot", so despite requiring more calls, these methods can be more efficient in practice.
|
||||||
|
func (pb *Problem) MUSDeletion() (mus *Problem, err error) {
|
||||||
|
pb2, err := pb.UnsatSubset()
|
||||||
|
if err != nil {
|
||||||
|
if err == ErrNotUnsat {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("could not extract MUS: %v", err)
|
||||||
|
}
|
||||||
|
pb2.NbVars += pb2.NbClauses // Add one relax var for each clause
|
||||||
|
for i, clause := range pb2.Clauses { // Add relax lit to each clause
|
||||||
|
newClause := make([]int, len(clause)+1)
|
||||||
|
copy(newClause, clause)
|
||||||
|
newClause[len(clause)] = pb.NbVars + i + 1 // Add relax lit to the clause
|
||||||
|
pb2.Clauses[i] = newClause
|
||||||
|
}
|
||||||
|
s := solver.New(solver.ParseSlice(pb2.Clauses))
|
||||||
|
asumptions := make([]solver.Lit, pb2.NbClauses)
|
||||||
|
for i := 0; i < pb2.NbClauses; i++ {
|
||||||
|
asumptions[i] = solver.IntToLit(int32(-(pb.NbVars + i + 1))) // At first, all asumptions are false
|
||||||
|
}
|
||||||
|
for i := range pb2.Clauses {
|
||||||
|
// Relax current clause
|
||||||
|
asumptions[i] = asumptions[i].Negation()
|
||||||
|
s.Assume(asumptions)
|
||||||
|
if s.Solve() == solver.Sat {
|
||||||
|
// It is now sat; reinsert the clause, i.e re-falsify the relax lit
|
||||||
|
asumptions[i] = asumptions[i].Negation()
|
||||||
|
if pb.Options.Verbose {
|
||||||
|
fmt.Printf("c clause %d/%d: kept\n", i+1, pb2.NbClauses)
|
||||||
|
}
|
||||||
|
} else if pb.Options.Verbose {
|
||||||
|
fmt.Printf("c clause %d/%d: removed\n", i+1, pb2.NbClauses)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mus = &Problem{
|
||||||
|
NbVars: pb.NbVars,
|
||||||
|
}
|
||||||
|
for i, val := range asumptions {
|
||||||
|
if !val.IsPositive() {
|
||||||
|
// Lit is not relaxed, meaning the clause is part of the MUS
|
||||||
|
clause := pb2.Clauses[i]
|
||||||
|
clause = clause[:len(clause)-1] // Remove relax lit
|
||||||
|
mus.Clauses = append(mus.Clauses, clause)
|
||||||
|
}
|
||||||
|
mus.NbClauses = len(mus.Clauses)
|
||||||
|
}
|
||||||
|
return mus, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MUS returns a Minimal Unsatisfiable Subset for the problem.
|
||||||
|
// A MUS is an unsatisfiable subset such that, if any of its clause is removed,
|
||||||
|
// the problem becomes satisfiable.
|
||||||
|
// A MUS can be useful to understand why a problem is UNSAT, but MUSes are expensive to compute since
|
||||||
|
// a SAT solver must be called several times on parts of the original problem to find them.
|
||||||
|
// The exact algorithm used to compute the MUS is not guaranteed. If you want to use a given algorithm,
|
||||||
|
// use the relevant functions.
|
||||||
|
func (pb *Problem) MUS() (mus *Problem, err error) {
|
||||||
|
return pb.MUSDeletion()
|
||||||
|
}
|
104
vendor/github.com/crillab/gophersat/explain/parser.go
generated
vendored
Normal file
104
vendor/github.com/crillab/gophersat/explain/parser.go
generated
vendored
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
package explain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// parseClause parses a line representing a clause in the DIMACS CNF syntax.
|
||||||
|
func parseClause(fields []string) ([]int, error) {
|
||||||
|
clause := make([]int, 0, len(fields)-1)
|
||||||
|
for _, rawLit := range fields {
|
||||||
|
lit, err := strconv.Atoi(rawLit)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not parse clause %v: %v", fields, err)
|
||||||
|
}
|
||||||
|
if lit != 0 {
|
||||||
|
clause = append(clause, lit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return clause, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseCNF parses a CNF and returns the associated problem.
|
||||||
|
func ParseCNF(r io.Reader) (*Problem, error) {
|
||||||
|
sc := bufio.NewScanner(r)
|
||||||
|
var pb Problem
|
||||||
|
for sc.Scan() {
|
||||||
|
line := sc.Text()
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch fields[0] {
|
||||||
|
case "c":
|
||||||
|
continue
|
||||||
|
case "p":
|
||||||
|
if err := pb.parseHeader(fields); err != nil {
|
||||||
|
return nil, fmt.Errorf("could not parse header %q: %v", line, err)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if err := pb.parseClause(fields); err != nil {
|
||||||
|
return nil, fmt.Errorf("could not parse clause %q: %v", line, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := sc.Err(); err != nil {
|
||||||
|
return nil, fmt.Errorf("could not parse problem: %v", err)
|
||||||
|
}
|
||||||
|
return &pb, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pb *Problem) parseHeader(fields []string) error {
|
||||||
|
if len(fields) != 4 {
|
||||||
|
return fmt.Errorf("expected 4 fields, got %d", len(fields))
|
||||||
|
}
|
||||||
|
strVars := fields[2]
|
||||||
|
strClauses := fields[3]
|
||||||
|
var err error
|
||||||
|
pb.NbVars, err = strconv.Atoi(fields[2])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid number of vars %q: %v", strVars, err)
|
||||||
|
}
|
||||||
|
if pb.NbVars < 0 {
|
||||||
|
return fmt.Errorf("negative number of vars %d", pb.NbVars)
|
||||||
|
}
|
||||||
|
pb.units = make([]int, pb.NbVars)
|
||||||
|
pb.NbClauses, err = strconv.Atoi(fields[3])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid number of clauses %s: %v", strClauses, err)
|
||||||
|
}
|
||||||
|
if pb.NbClauses < 0 {
|
||||||
|
return fmt.Errorf("negative number of clauses %d", pb.NbClauses)
|
||||||
|
}
|
||||||
|
pb.Clauses = make([][]int, 0, pb.NbClauses)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pb *Problem) parseClause(fields []string) error {
|
||||||
|
clause, err := parseClause(fields)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pb.Clauses = append(pb.Clauses, clause)
|
||||||
|
if len(clause) == 1 {
|
||||||
|
lit := clause[0]
|
||||||
|
v := lit
|
||||||
|
if lit < 0 {
|
||||||
|
v = -v
|
||||||
|
}
|
||||||
|
if v > pb.NbVars {
|
||||||
|
// There was an error in the header
|
||||||
|
return fmt.Errorf("found lit %d but problem was supposed to hold only %d vars", lit, pb.NbVars)
|
||||||
|
}
|
||||||
|
if lit > 0 {
|
||||||
|
pb.units[v-1] = 1
|
||||||
|
} else {
|
||||||
|
pb.units[v-1] = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
125
vendor/github.com/crillab/gophersat/explain/problem.go
generated
vendored
Normal file
125
vendor/github.com/crillab/gophersat/explain/problem.go
generated
vendored
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
package explain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Problem is a conjunction of Clauses.
|
||||||
|
// This package does not use solver's representation.
|
||||||
|
// We want this code to be as simple as possible to be easy to audit.
|
||||||
|
// On the other hand, solver's code must be as efficient as possible.
|
||||||
|
type Problem struct {
|
||||||
|
Clauses [][]int
|
||||||
|
NbVars int
|
||||||
|
NbClauses int
|
||||||
|
units []int // For each var, 0 if the var is unbound, 1 if true, -1 if false
|
||||||
|
Options Options
|
||||||
|
tagged []bool // List of claused used whil proving the problem is unsat. Initialized lazily
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pb *Problem) initTagged() {
|
||||||
|
pb.tagged = make([]bool, pb.NbClauses)
|
||||||
|
for i, clause := range pb.Clauses {
|
||||||
|
// Unit clauses are tagged as they will probably be used during resolution
|
||||||
|
pb.tagged[i] = len(clause) == 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pb *Problem) clone() *Problem {
|
||||||
|
pb2 := &Problem{
|
||||||
|
Clauses: make([][]int, pb.NbClauses),
|
||||||
|
NbVars: pb.NbVars,
|
||||||
|
NbClauses: pb.NbClauses,
|
||||||
|
units: make([]int, pb.NbVars),
|
||||||
|
}
|
||||||
|
copy(pb2.units, pb.units)
|
||||||
|
for i, clause := range pb.Clauses {
|
||||||
|
pb2.Clauses[i] = make([]int, len(clause))
|
||||||
|
copy(pb2.Clauses[i], clause)
|
||||||
|
}
|
||||||
|
return pb2
|
||||||
|
}
|
||||||
|
|
||||||
|
// restore removes all learned clauses, if any.
|
||||||
|
func (pb *Problem) restore() {
|
||||||
|
pb.Clauses = pb.Clauses[:pb.NbClauses]
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsat will be true iff the problem can be proven unsat through unit propagation.
|
||||||
|
// This methods modifies pb.units.
|
||||||
|
func (pb *Problem) unsat() bool {
|
||||||
|
done := make([]bool, len(pb.Clauses)) // clauses that were deemed sat during propagation
|
||||||
|
modified := true
|
||||||
|
for modified {
|
||||||
|
modified = false
|
||||||
|
for i, clause := range pb.Clauses {
|
||||||
|
if done[i] { // That clause was already proved true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
unbound := 0
|
||||||
|
var unit int // An unbound literal, if any
|
||||||
|
sat := false
|
||||||
|
for _, lit := range clause {
|
||||||
|
v := lit
|
||||||
|
if v < 0 {
|
||||||
|
v = -v
|
||||||
|
}
|
||||||
|
binding := pb.units[v-1]
|
||||||
|
if binding == 0 {
|
||||||
|
unbound++
|
||||||
|
if unbound == 1 {
|
||||||
|
unit = lit
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else if binding*lit == v { // (binding == -1 && lit < 0) || (binding == 1 && lit > 0) {
|
||||||
|
sat = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if sat {
|
||||||
|
done[i] = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if unbound == 0 {
|
||||||
|
// All lits are false: problem is UNSAT
|
||||||
|
if i < pb.NbClauses {
|
||||||
|
pb.tagged[i] = true
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if unbound == 1 {
|
||||||
|
if unit < 0 {
|
||||||
|
pb.units[-unit-1] = -1
|
||||||
|
} else {
|
||||||
|
pb.units[unit-1] = 1
|
||||||
|
}
|
||||||
|
done[i] = true
|
||||||
|
if i < pb.NbClauses {
|
||||||
|
pb.tagged[i] = true
|
||||||
|
}
|
||||||
|
modified = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Problem is either sat or could not be proven unsat through unit propagation
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// CNF returns a representation of the problem using the Dimacs syntax.
|
||||||
|
func (pb *Problem) CNF() string {
|
||||||
|
lines := make([]string, 1, pb.NbClauses+1)
|
||||||
|
lines[0] = fmt.Sprintf("p cnf %d %d", pb.NbVars, pb.NbClauses)
|
||||||
|
for i := 0; i < pb.NbClauses; i++ {
|
||||||
|
clause := pb.Clauses[i]
|
||||||
|
strClause := make([]string, len(clause)+1)
|
||||||
|
for i, lit := range clause {
|
||||||
|
strClause[i] = fmt.Sprintf("%d", lit)
|
||||||
|
}
|
||||||
|
strClause[len(clause)] = "0"
|
||||||
|
line := strings.Join(strClause, " ")
|
||||||
|
lines = append(lines, line)
|
||||||
|
}
|
||||||
|
return strings.Join(lines, "\n")
|
||||||
|
}
|
20
vendor/github.com/crillab/gophersat/solver/solver.go
generated
vendored
20
vendor/github.com/crillab/gophersat/solver/solver.go
generated
vendored
@ -127,6 +127,25 @@ func New(problem *Problem) *Solver {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newVar is used to indicate a new variable must be added to the solver.
|
||||||
|
// This can be used when new clauses are appended and these clauses contain vars that were unseen so far.
|
||||||
|
// If the var already existed, nothing will happen.
|
||||||
|
func (s *Solver) newVar(v Var) {
|
||||||
|
if cnfVar := int(v.Int()); cnfVar > s.nbVars {
|
||||||
|
// If the var already existed, do nothing
|
||||||
|
for i := s.nbVars; i < cnfVar; i++ {
|
||||||
|
s.model = append(s.model, 0)
|
||||||
|
s.activity = append(s.activity, 0.)
|
||||||
|
s.polarity = append(s.polarity, false)
|
||||||
|
s.reason = append(s.reason, nil)
|
||||||
|
s.trailBuf = append(s.trailBuf, 0)
|
||||||
|
}
|
||||||
|
s.varQueue = newQueue(s.activity)
|
||||||
|
s.addVarWatcherList(v)
|
||||||
|
s.nbVars = cnfVar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// sets initial activity for optimization variables, if any.
|
// sets initial activity for optimization variables, if any.
|
||||||
func (s *Solver) initOptimActivity() {
|
func (s *Solver) initOptimActivity() {
|
||||||
for i, lit := range s.minLits {
|
for i, lit := range s.minLits {
|
||||||
@ -682,6 +701,7 @@ func (s *Solver) AppendClause(clause *Clause) {
|
|||||||
i := 0
|
i := 0
|
||||||
for i < clause.Len() {
|
for i < clause.Len() {
|
||||||
lit := clause.Get(i)
|
lit := clause.Get(i)
|
||||||
|
s.newVar(lit.Var())
|
||||||
switch s.litStatus(lit) {
|
switch s.litStatus(lit) {
|
||||||
case Sat:
|
case Sat:
|
||||||
w := clause.Weight(i)
|
w := clause.Weight(i)
|
||||||
|
5
vendor/github.com/crillab/gophersat/solver/types.go
generated
vendored
5
vendor/github.com/crillab/gophersat/solver/types.go
generated
vendored
@ -71,6 +71,11 @@ func (v Var) Lit() Lit {
|
|||||||
return Lit(v * 2)
|
return Lit(v * 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Int converts a Var to a CNF variable.
|
||||||
|
func (v Var) Int() int32 {
|
||||||
|
return int32(v + 1)
|
||||||
|
}
|
||||||
|
|
||||||
// SignedLit returns the Lit associated to v, negated if 'signed', positive else.
|
// SignedLit returns the Lit associated to v, negated if 'signed', positive else.
|
||||||
func (v Var) SignedLit(signed bool) Lit {
|
func (v Var) SignedLit(signed bool) Lit {
|
||||||
if signed {
|
if signed {
|
||||||
|
10
vendor/github.com/crillab/gophersat/solver/watcher.go
generated
vendored
10
vendor/github.com/crillab/gophersat/solver/watcher.go
generated
vendored
@ -39,6 +39,16 @@ func (s *Solver) initWatcherList(clauses []*Clause) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Should be called when new vars are added to the problem (see Solver.newVar)
|
||||||
|
func (s *Solver) addVarWatcherList(v Var) {
|
||||||
|
cnfVar := int(v.Int())
|
||||||
|
for i := s.nbVars; i < cnfVar; i++ {
|
||||||
|
s.wl.wlistBin = append(s.wl.wlistBin, nil, nil)
|
||||||
|
s.wl.wlist = append(s.wl.wlist, nil, nil)
|
||||||
|
s.wl.wlistPb = append(s.wl.wlistPb, nil, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// appendClause appends the clause without checking whether the clause is already satisfiable, unit, or unsatisfiable.
|
// appendClause appends the clause without checking whether the clause is already satisfiable, unit, or unsatisfiable.
|
||||||
// To perform those checks, call s.AppendClause.
|
// To perform those checks, call s.AppendClause.
|
||||||
// clause is supposed to be a problem clause, not a learned one.
|
// clause is supposed to be a problem clause, not a learned one.
|
||||||
|
3
vendor/modules.txt
vendored
3
vendor/modules.txt
vendored
@ -115,9 +115,10 @@ github.com/containerd/ttrpc
|
|||||||
github.com/containerd/typeurl
|
github.com/containerd/typeurl
|
||||||
# github.com/cpuguy83/go-md2man/v2 v2.0.0
|
# github.com/cpuguy83/go-md2man/v2 v2.0.0
|
||||||
github.com/cpuguy83/go-md2man/v2/md2man
|
github.com/cpuguy83/go-md2man/v2/md2man
|
||||||
# github.com/crillab/gophersat v1.3.2-0.20201023142334-3fc2ac466765
|
# github.com/crillab/gophersat v1.3.2-0.20210701121804-72b19f5b6b38
|
||||||
## explicit
|
## explicit
|
||||||
github.com/crillab/gophersat/bf
|
github.com/crillab/gophersat/bf
|
||||||
|
github.com/crillab/gophersat/explain
|
||||||
github.com/crillab/gophersat/solver
|
github.com/crillab/gophersat/solver
|
||||||
# github.com/cyphar/filepath-securejoin v0.2.2
|
# github.com/cyphar/filepath-securejoin v0.2.2
|
||||||
github.com/cyphar/filepath-securejoin
|
github.com/cyphar/filepath-securejoin
|
||||||
|
Loading…
Reference in New Issue
Block a user