gonum.org/v1/gonum@v0.14.0/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  	"gonum.org/v1/gonum/graph/simple"
    16  	"gonum.org/v1/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  }