// Returns a zero terminated list of failed assumption in the last call to // Solve(). The pointer is valid until the next call to // Solve() or FailedAssumptions. It only makes sense if the // last call to Solve() returned Unsatisfiable. func (p *Pigosat) FailedAssumptions() Clause { if p.Res() != Unsatisfiable { return nil } defer p.ready(true)() p_int := C.picosat_failed_assumptions(p.p) // It should be reasonable to use the number of vars in // the solver as the max array size, since we aren't tracking // the active number of assumptions. size := int(C.picosat_variables(p.p)) var cints []C.int header := (*reflect.SliceHeader)(unsafe.Pointer(&cints)) header.Cap = size header.Len = size header.Data = uintptr(unsafe.Pointer(p_int)) // The returns int pointer is both temporary, and larger than // needed, so we need to copy the real values into a new slice, // up until the terminator. ints := Clause{} for _, cint := range cints { // break at the first sign of the 0 terminator. if cint == 0 { break } ints = append(ints, Literal(cint)) } return ints }
// BlockSolution adds a clause to the formula ruling out a given solution. It is // a no-op if p is nil and returns an error if the solution is the wrong // length. There is no need to call BlockSolution after calling Pigosat.Solve, // which calls it automatically for every Satisfiable solution. func (p *Pigosat) BlockSolution(solution Solution) error { defer p.ready(false)() if n := int(C.picosat_variables(p.p)); len(solution) != n+1 { return fmt.Errorf("solution length %d, but have %d variables", len(solution), n) } p.blocksol(solution) return nil }
// blocksol adds the inverse of the solution to the clauses. // This private method does not acquire the lock or check if p is nil. func (p *Pigosat) blocksol(sol Solution) { n := C.picosat_variables(p.p) clause := make([]C.int, n+1) for i := C.int(1); i <= n; i++ { if sol[i] { clause[i-1] = -i } else { clause[i-1] = i } } // int picosat_add_lits (PicoSAT *, int * lits); C.picosat_add_lits(p.p, &clause[0]) }
// Solve the formula and return the status of the solution: one of the constants // Unsatisfiable, Satisfiable, or Unknown. If satisfiable, return a slice // indexed by the variables in the formula (so the first element is always // false). Solve can be used like an iterator, yielding a new solution until // there are no more feasible solutions: // for status, solution := p.Solve(); status == Satisfiable; status, solution = p.Solve() { // // Do stuff with status, solution // } func (p *Pigosat) Solve() (status Status, solution Solution) { defer p.ready(false)() // int picosat_sat (PicoSAT *, int decision_limit); status = Status(C.picosat_sat(p.p, -1)) if status == Unsatisfiable || status == Unknown { return } else if status != Satisfiable { panic(fmt.Errorf("Unknown sat status: %d", status)) } n := int(C.picosat_variables(p.p)) // Calling Pigosat.Variables deadlocks solution = make(Solution, n+1) for i := 1; i <= n; i++ { // int picosat_deref (PicoSAT *, int lit); if val := C.picosat_deref(p.p, C.int(i)); val > 0 { solution[i] = true } else if val == 0 { panic(fmt.Errorf("Variable %d was assigned value 0", i)) } } p.blocksol(solution) return }
// Variables returns the number of variables in the formula: The m in the DIMACS // header "p cnf <m> n". func (p *Pigosat) Variables() int { defer p.ready(true)() // int picosat_variables (PicoSAT *); return int(C.picosat_variables(p.p)) }