github.com/gopherd/gonum@v0.0.4/graph/product/product_example_test.go (about) 1 // Copyright ©2019 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 product_test 6 7 import ( 8 "fmt" 9 "os" 10 "sort" 11 "text/tabwriter" 12 13 "github.com/gopherd/gonum/graph" 14 "github.com/gopherd/gonum/graph/product" 15 "github.com/gopherd/gonum/graph/simple" 16 "github.com/gopherd/gonum/graph/topo" 17 ) 18 19 // atom is a graph.Node representing an atom in a molecule. 20 type atom struct { 21 name string // name is the name of the atom. 22 pos int // pos is the position number of the atom. 23 id int64 24 } 25 26 // ID satisfies the graph.Node interface. 27 func (n atom) ID() int64 { return n.id } 28 29 func ExampleModular_subgraphIsomorphism() { 30 // The modular product can be used to find subgraph isomorphisms. 31 // See https://doi.org/10.1016/0020-0190(76)90049-1 and for a 32 // theoretical perspective, https://doi.org/10.1145/990524.990529. 33 34 // We can find the common structure between two organic molecules. 35 // For example the purines adenine and guanine from nucleic acids. 36 37 // Make a graph for adenine. 38 adenine := simple.NewUndirectedGraph() 39 for _, bond := range []simple.Edge{ 40 // Purine nucleus. 41 {F: atom{name: "N", pos: 1, id: 0}, T: atom{name: "C", pos: 2, id: 1}}, 42 {F: atom{name: "N", pos: 1, id: 0}, T: atom{name: "C", pos: 6, id: 5}}, 43 {F: atom{name: "C", pos: 2, id: 1}, T: atom{name: "N", pos: 3, id: 2}}, 44 {F: atom{name: "N", pos: 3, id: 2}, T: atom{name: "C", pos: 4, id: 3}}, 45 {F: atom{name: "C", pos: 4, id: 3}, T: atom{name: "C", pos: 5, id: 4}}, 46 {F: atom{name: "C", pos: 4, id: 3}, T: atom{name: "N", pos: 9, id: 8}}, 47 {F: atom{name: "C", pos: 5, id: 4}, T: atom{name: "C", pos: 6, id: 5}}, 48 {F: atom{name: "C", pos: 5, id: 4}, T: atom{name: "N", pos: 7, id: 6}}, 49 {F: atom{name: "N", pos: 7, id: 6}, T: atom{name: "C", pos: 8, id: 7}}, 50 {F: atom{name: "C", pos: 8, id: 7}, T: atom{name: "N", pos: 9, id: 8}}, 51 52 // Amino modification in adenine. 53 // 54 // Note that the position number of the N is non-standard. 55 {F: atom{name: "C", pos: 6, id: 5}, T: atom{name: "N", pos: 10, id: 9}}, 56 } { 57 adenine.SetEdge(bond) 58 } 59 60 // Make a graph for guanine. 61 // 62 // Note that node IDs here have no intersection with 63 // the adenine graph to show that they are not being 64 // used to map between the graphs. 65 guanine := simple.NewUndirectedGraph() 66 for _, bond := range []simple.Edge{ 67 // Purine nucleus. 68 {F: atom{name: "N", pos: 1, id: 10}, T: atom{name: "C", pos: 2, id: 11}}, 69 {F: atom{name: "N", pos: 1, id: 10}, T: atom{name: "C", pos: 6, id: 15}}, 70 {F: atom{name: "C", pos: 2, id: 11}, T: atom{name: "N", pos: 3, id: 12}}, 71 {F: atom{name: "N", pos: 3, id: 12}, T: atom{name: "C", pos: 4, id: 13}}, 72 {F: atom{name: "C", pos: 4, id: 13}, T: atom{name: "C", pos: 5, id: 14}}, 73 {F: atom{name: "C", pos: 4, id: 13}, T: atom{name: "N", pos: 9, id: 18}}, 74 {F: atom{name: "C", pos: 5, id: 14}, T: atom{name: "C", pos: 6, id: 15}}, 75 {F: atom{name: "C", pos: 5, id: 14}, T: atom{name: "N", pos: 7, id: 16}}, 76 {F: atom{name: "N", pos: 7, id: 16}, T: atom{name: "C", pos: 8, id: 17}}, 77 {F: atom{name: "C", pos: 8, id: 17}, T: atom{name: "N", pos: 9, id: 18}}, 78 79 // Amino and keto modifications in guanine. 80 // 81 // Note that the position number of the N and O is non-standard. 82 {F: atom{name: "C", pos: 2, id: 11}, T: atom{name: "N", pos: 11, id: 19}}, 83 {F: atom{name: "C", pos: 6, id: 15}, T: atom{name: "O", pos: 10, id: 20}}, 84 } { 85 guanine.SetEdge(bond) 86 } 87 88 // Produce the modular product of the two graphs. 89 p := simple.NewUndirectedGraph() 90 product.Modular(p, adenine, guanine) 91 92 // Find the maximal cliques in the modular product. 93 mc := topo.BronKerbosch(p) 94 95 // Report the largest. 96 sort.Sort(byLength(mc)) 97 max := len(mc[0]) 98 w := tabwriter.NewWriter(os.Stdout, 5, 0, 0, ' ', tabwriter.AlignRight) 99 fmt.Println(" Adenine Guanine") 100 fmt.Fprintln(w, "Atom\tPos\tAtom\tPos\t") 101 for _, c := range mc { 102 if len(c) < max { 103 break 104 } 105 for _, p := range c { 106 // Extract the mapping between the 107 // inputs from the product. 108 p := p.(product.Node) 109 adenine := p.A.(atom) 110 guanine := p.B.(atom) 111 fmt.Fprintf(w, "%s\t%d\t%s\t%d\t\n", adenine.name, adenine.pos, guanine.name, guanine.pos) 112 } 113 } 114 w.Flush() 115 116 // Unordered output: 117 // Adenine Guanine 118 // Atom Pos Atom Pos 119 // N 3 N 3 120 // N 7 N 7 121 // N 10 O 10 122 // C 6 C 6 123 // C 2 C 2 124 // C 8 C 8 125 // C 5 C 5 126 // N 9 N 9 127 // N 1 N 1 128 // C 4 C 4 129 } 130 131 // byLength implements the sort.Interface, sorting the slices 132 // descending by length. 133 type byLength [][]graph.Node 134 135 func (n byLength) Len() int { return len(n) } 136 func (n byLength) Less(i, j int) bool { return len(n[i]) > len(n[j]) } 137 func (n byLength) Swap(i, j int) { n[i], n[j] = n[j], n[i] }