func printSAT(tasks []Task, workers []Worker) { pAssign := sat.Pred("assign") pWorks := sat.Pred("works") sat.SetUp(4, sorters.Pairwise) var clauses sat.ClauseSet // at least one: simple clause for _, t := range tasks { lits := make([]sat.Literal, len(t.worker)) i := 0 for wId, _ := range t.worker { lits[i] = sat.Literal{true, sat.Atom{pAssign, wId, t.id}} i++ } clauses.AddClause("al1", lits...) clauses.AddClauseSet(sat.CreateCardinality("am1", lits, 1, sorters.AtMost)) } // count number of employees for _, w := range workers { for _, tId := range w.skills { l1 := sat.Literal{false, sat.Atom{pAssign, w.id, tId}} l2 := sat.Literal{true, sat.Atom{pWorks, w.id, 0}} clauses.AddClause("wrk", l1, l2) } } lits := make([]sat.Literal, len(workers)) for i, w := range workers { lits[i] = sat.Literal{true, sat.Atom{pWorks, w.id, 0}} } clauses.AddClauseSet(sat.CreateCardinality("cWo", lits, *nWorkers, sorters.AtMost)) // intersections on the timeline: two ways to do it // 1) list all intersecting tasks // 2) find maximal cliques in the interval graph, and post for that for _, w := range workers { ts := make([]Task, len(w.skills)) for i, s := range w.skills { ts[i] = tasks[s] } sort.Sort(ByStart(ts)) switch *typeIntersect { case "simple": for i, t1 := range ts { for j := i + 1; j < len(ts); j++ { t2 := ts[j] if t2.start < t1.end { l1 := sat.Literal{false, sat.Atom{pAssign, w.id, t1.id}} l2 := sat.Literal{false, sat.Atom{pAssign, w.id, t2.id}} clauses.AddClause("isc1", l1, l2) } } } case "clique": // find the maximal cliques in the interval graph and pose AMO on them clique := make([]Task, 0) for _, t := range ts { sort.Sort(ByEnd(clique)) //todo: use a priority queue, e.g. heap //first one is earliest end time if len(clique) > 0 && clique[0].end <= t.start { // max clique reached //output the maximal clique! if len(clique) > 1 { lits := make([]sat.Literal, len(clique)) for i, c := range clique { lits[i] = sat.Literal{true, sat.Atom{pAssign, w.id, c.id}} fmt.Print(c.id, "(", c.start, ",", c.end, ") ") } fmt.Println() //fmt.Println("clique", w.id, lits) clauses.AddClauseSet(sat.CreateCardinality("cli", lits, 1, sorters.AtMost)) } //start removing elements: for len(clique) > 0 && clique[0].end <= t.start { clique = clique[1:] } } clique = append(clique, t) } if len(clique) > 1 { lits := make([]sat.Literal, len(clique)) for i, c := range clique { lits[i] = sat.Literal{true, sat.Atom{pAssign, w.id, c.id}} } //fmt.Println("clique", w.id, lits) clauses.AddClauseSet(sat.CreateCardinality("cli", lits, 1, sorters.AtMost)) } default: panic("Type not implemented") } } g := sat.IdGenerator(len(clauses) * 7) g.GenerateIds(clauses) //g.Filename = strings.Split(*f, ".")[0] + ".cnf" //g.Filename = *out if *dbg { g.PrintDebug(clauses) } else { g.PrintClausesDIMACS(clauses) } }
func main() { glob.Init() input, err2 := os.Open(*glob.Filename_flag) defer input.Close() if err2 != nil { panic("Could not read file") return } scanner := bufio.NewScanner(input) buf := make([]byte, 0, 64*1024) scanner.Buffer(buf, 1024*1024) state := 0 // 0: read size, 1: read graph 1, 2: read graph 2 vars := 0 orig_vars := 0 size := 0 i := 0 var entries []entry for scanner.Scan() { l := strings.Trim(scanner.Text(), " ") if l == "" || strings.HasPrefix(l, "%") || strings.HasPrefix(l, "*") { continue } elements := strings.Fields(l) var b error switch state { case 0: // deprecated: for parsing the "header" of pb files, now parser is flexible { vars, b = strconv.Atoi(elements[0]) if b != nil { panic("bad conversion of numbers") } orig_vars = vars size, b = strconv.Atoi(elements[1]) if b != nil { panic("bad conversion of numbers") } entries = make([]entry, size) state = 1 } case 1: { entries[i].id1, b = strconv.Atoi(elements[0]) if b != nil { panic("bad conversion of numbers") } entries[i].id2, b = strconv.Atoi(elements[1]) if b != nil { panic("bad conversion of numbers") } var f float64 f, b = strconv.ParseFloat(elements[2], 64) if b != nil { panic("bad conversion of numbers") } entries[i].c = int64(f) if entries[i].id1 != entries[i].id2 { vars++ entries[i].and = vars } i++ } } } var clauses sat.ClauseSet var opt constraints.Threshold opt.Typ = constraints.OPT lits := make([]sat.Literal, vars+1) primaryVars := make(map[string]bool, 0) for i := 0; i <= vars; i++ { primaryVars[sat.NewAtomP1(sat.Pred("x"), i).Id()] = true } for i, _ := range lits { lits[i] = sat.Literal{true, sat.NewAtomP1(sat.Pred("x"), i)} } for _, e := range entries { if e.id1 == e.id2 { opt.Entries = append(opt.Entries, constraints.Entry{lits[e.id1], int64(e.c)}) } else { clauses.AddClause(sat.Neg(lits[e.id1]), sat.Neg(lits[e.id2]), lits[e.and]) clauses.AddClause(lits[e.id1], sat.Neg(lits[e.and])) clauses.AddClause(lits[e.id2], sat.Neg(lits[e.and])) opt.Entries = append(opt.Entries, constraints.Entry{lits[e.and], int64(e.c)}) } } if *glob.Gringo_flag { for i := 0; i <= orig_vars; i++ { fmt.Println("{x(", i, ")}.") } for _, e := range entries { if e.id1 != e.id2 { fmt.Println(lits[e.and].ToTxt(), ":-", lits[e.id1].ToTxt(), ",", lits[e.id2].ToTxt(), ".") } } opt.PrintGringo() return } g := sat.IdGenerator(clauses.Size()*7 + 1) g.PrimaryVars = primaryVars opt.NormalizePositiveCoefficients() opt.Offset = opt.K // opt.PrintGringo() // clauses.PrintDebug() glob.D("offset", opt.Offset) glob.A(opt.Positive(), "opt only has positive coefficients") g.Solve(clauses, &opt, *glob.Opt_bound_flag, -opt.Offset) }