github.com/gopherd/gonum@v0.0.4/graph/topo/2sat_example_test.go (about) 1 // Copyright Β©2017 The Gonum Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package topo_test 6 7 import ( 8 "bufio" 9 "fmt" 10 "io" 11 "log" 12 "sort" 13 "strings" 14 15 "github.com/gopherd/gonum/graph/simple" 16 "github.com/gopherd/gonum/graph/topo" 17 ) 18 19 var systems = []string{ 20 // Unsatisfiable system. 21 `π₯_a β¨ Β¬π₯_b 22 Β¬π₯_b β¨ π₯_f 23 π₯_h β¨ π₯_i 24 π₯_a β¨ π₯_b 25 π₯_k β¨ π₯_c 26 Β¬π₯_f β¨ π₯_h 27 π₯_c β¨ π₯_g 28 π₯_f β¨ π₯_g 29 π₯_h β¨ Β¬π₯_l 30 Β¬π₯_h β¨ π₯_i 31 π₯_i β¨ π₯_b 32 Β¬π₯_i β¨ Β¬π₯_h 33 π₯_i β¨ Β¬π₯_c 34 π₯_l β¨ π₯_d 35 Β¬π₯_j β¨ Β¬π₯_i 36 Β¬π₯_a β¨ Β¬π₯_j 37 Β¬π₯_a β¨ π₯_b 38 Β¬π₯_d β¨ π₯_e 39 Β¬π₯_k β¨ π₯_h 40 π₯_l β¨ Β¬π₯_d 41 π₯_l β¨ π₯_d 42 π₯_l β¨ Β¬π₯_f 43 π₯_b β¨ π₯_d 44 π₯_b β¨ Β¬π₯_g 45 π₯_d β¨ Β¬π₯_l 46 Β¬π₯_l β¨ Β¬π₯_k 47 `, 48 // Satisfiable system. 49 `π₯_a β¨ Β¬π₯_b 50 Β¬π₯_b β¨ π₯_f 51 π₯_h β¨ π₯_i 52 π₯_a β¨ π₯_b 53 π₯_k β¨ π₯_c 54 Β¬π₯_f β¨ π₯_h 55 π₯_c β¨ π₯_g 56 π₯_f β¨ π₯_g 57 π₯_h β¨ Β¬π₯_l 58 Β¬π₯_h β¨ π₯_i 59 π₯_i β¨ π₯_b 60 Β¬π₯_i β¨ π₯_e 61 π₯_i β¨ Β¬π₯_c 62 Β¬π₯_g β¨ Β¬π₯_a 63 π₯_l β¨ π₯_f 64 Β¬π₯_j β¨ Β¬π₯_i 65 Β¬π₯_a β¨ Β¬π₯_j 66 Β¬π₯_a β¨ π₯_b 67 Β¬π₯_d β¨ π₯_e 68 π₯_k β¨ Β¬π₯_a 69 π₯_k β¨ π₯_h 70 π₯_l β¨ Β¬π₯_d 71 π₯_l β¨ π₯_e 72 π₯_l β¨ Β¬π₯_f 73 π₯_b β¨ π₯_d 74 π₯_b β¨ Β¬π₯_g 75 π₯_d β¨ Β¬π₯_l 76 π₯_l β¨ π₯_e 77 `, 78 79 `fun β¨ Β¬fun 80 fun β¨ Β¬Gonum 81 Gonum β¨ Gonum 82 `, 83 } 84 85 // twoSat returns whether the system described in the data read from r is 86 // satisfiable and a set of states that satisfies the system. 87 // The syntax used by twoSat is "π₯ β¨ π¦" where π₯ and π¦ may be negated by 88 // leading "Β¬" characters. twoSat uses the implication graph approach to 89 // system analysis. 90 func twoSat(r io.Reader) (state map[string]bool, ok bool) { 91 g := simple.NewDirectedGraph() 92 93 sc := bufio.NewScanner(r) 94 nodes := make(map[string]node) 95 for count := 1; sc.Scan(); count++ { 96 line := sc.Text() 97 fields := strings.Split(line, "β¨") 98 if len(fields) != 2 { 99 log.Fatalf("failed to parse on line %d %q: invalid syntax", count, line) 100 } 101 var variables [2]node 102 for i, f := range fields { 103 f = strings.TrimSpace(f) 104 var negate bool 105 for strings.Index(f, "Β¬") == 0 { 106 f = strings.TrimPrefix(f, "Β¬") 107 negate = !negate 108 } 109 n, ok := nodes[f] 110 if !ok { 111 n = node{ 112 id: int64(len(nodes) + 1), // id must not be zero. 113 name: f, 114 } 115 nodes[f] = n 116 } 117 if negate { 118 n = n.negated() 119 } 120 variables[i] = n 121 } 122 123 // Check for tautology. 124 if variables[0].negated().ID() == variables[1].ID() { 125 for _, v := range variables { 126 if g.Node(v.ID()) == nil { 127 g.AddNode(v) 128 } 129 } 130 continue 131 } 132 133 // Add implications to the graph. 134 g.SetEdge(simple.Edge{F: variables[0].negated(), T: variables[1]}) 135 g.SetEdge(simple.Edge{F: variables[1].negated(), T: variables[0]}) 136 } 137 138 // Find implication inconsistencies. 139 sccs := topo.TarjanSCC(g) 140 for _, c := range sccs { 141 set := make(map[int64]struct{}) 142 for _, n := range c { 143 id := n.ID() 144 if _, ok := set[-id]; ok { 145 return nil, false 146 } 147 set[id] = struct{}{} 148 } 149 } 150 151 // Assign states. 152 state = make(map[string]bool) 153 unknown: 154 for _, c := range sccs { 155 for _, n := range c { 156 if _, known := state[n.(node).name]; known { 157 continue unknown 158 } 159 } 160 for _, n := range c { 161 n := n.(node) 162 state[n.name] = n.id > 0 163 } 164 } 165 166 return state, true 167 } 168 169 type node struct { 170 id int64 171 name string 172 } 173 174 func (n node) ID() int64 { return n.id } 175 func (n node) negated() node { return node{-n.id, n.name} } 176 177 func ExampleTarjanSCC_twoSAT() { 178 for i, s := range systems { 179 state, ok := twoSat(strings.NewReader(s)) 180 if !ok { 181 fmt.Printf("system %d is not satisfiable\n", i) 182 continue 183 } 184 var ps []string 185 for v, t := range state { 186 ps = append(ps, fmt.Sprintf("%s:%t", v, t)) 187 } 188 sort.Strings(ps) 189 fmt.Printf("system %d is satisfiable: %s\n", i, strings.Join(ps, " ")) 190 } 191 192 // Output: 193 // system 0 is not satisfiable 194 // system 1 is satisfiable: π₯_a:true π₯_b:true π₯_c:true π₯_d:true π₯_e:true π₯_f:true π₯_g:false π₯_h:true π₯_i:true π₯_j:false π₯_k:true π₯_l:true 195 // system 2 is satisfiable: Gonum:true fun:true 196 }