gonum.org/v1/gonum@v0.14.0/graph/product/product_ext_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 10 "gonum.org/v1/gonum/graph" 11 "gonum.org/v1/gonum/graph/iterator" 12 "gonum.org/v1/gonum/graph/product" 13 "gonum.org/v1/gonum/graph/simple" 14 "gonum.org/v1/gonum/graph/topo" 15 ) 16 17 // person is a graph.Node representing a person. 18 type person struct { 19 name string // name is the name of the person. 20 id int64 21 } 22 23 // ID satisfies the graph.Node interface. 24 func (n person) ID() int64 { return n.id } 25 26 func ExampleModularExt_subgraphIsomorphism() { 27 // Extended attributes of the graph can be used to refine 28 // subgraph isomorphism identification. By filtering edge 29 // agreement by weight we can identify social network 30 // motifs within a larger graph. 31 // 32 // This example extracts sources of conflict from the 33 // relationships of Julius Caesar, Mark Antony and 34 // Cleopatra. 35 36 // Make a graph describing people's relationships. 37 // 38 // Edge weight indicates love/animosity. 39 people := simple.NewDirectedGraph() 40 for _, relationship := range []simple.WeightedEdge{ 41 {F: person{name: "Julius Caesar", id: 0}, T: person{name: "Cleopatra", id: 1}, W: 1}, 42 {F: person{name: "Cleopatra", id: 1}, T: person{name: "Julius Caesar", id: 0}, W: 1}, 43 {F: person{name: "Julius Caesar", id: 0}, T: person{name: "Cornelia", id: 3}, W: 1}, 44 {F: person{name: "Cornelia", id: 3}, T: person{name: "Julius Caesar", id: 0}, W: 1}, 45 {F: person{name: "Mark Antony", id: 2}, T: person{name: "Cleopatra", id: 1}, W: 1}, 46 {F: person{name: "Cleopatra", id: 1}, T: person{name: "Mark Antony", id: 2}, W: 1}, 47 {F: person{name: "Fulvia", id: 4}, T: person{name: "Mark Antony", id: 2}, W: 1}, 48 {F: person{name: "Fulvia", id: 4}, T: person{name: "Cleopatra", id: 1}, W: -1}, 49 {F: person{name: "Octavia", id: 5}, T: person{name: "Mark Antony", id: 2}, W: 1}, 50 {F: person{name: "Octavia", id: 5}, T: person{name: "Cleopatra", id: 1}, W: -1}, 51 } { 52 people.SetEdge(relationship) 53 } 54 55 // Make a graph for the query pattern: a love triangle. 56 pattern := simple.NewDirectedGraph() 57 for _, relationsip := range []simple.WeightedEdge{ 58 {F: person{name: "A", id: -1}, T: person{name: "B", id: -2}, W: 1}, 59 {F: person{name: "B", id: -2}, T: person{name: "A", id: -1}, W: 1}, 60 {F: person{name: "C", id: -3}, T: person{name: "A", id: -1}, W: -1}, 61 {F: person{name: "C", id: -3}, T: person{name: "B", id: -2}, W: 1}, 62 } { 63 pattern.SetEdge(relationsip) 64 } 65 66 // Produce the modular product of the two graphs. 67 p := simple.NewDirectedGraph() 68 product.ModularExt(p, people, pattern, func(a, b graph.Edge) bool { 69 return a.(simple.WeightedEdge).Weight() == b.(simple.WeightedEdge).Weight() 70 }) 71 72 // Find the maximal cliques in the undirected induction 73 // of the modular product. 74 mc := topo.BronKerbosch(undirected{p}) 75 76 // Report the cliques that are identical in order to the pattern. 77 fmt.Println("Person — Relationship position:") 78 for _, c := range mc { 79 if len(c) != pattern.Nodes().Len() { 80 continue 81 } 82 for _, p := range c { 83 // Extract the mapping between the 84 // inputs from the product. 85 p := p.(product.Node) 86 people := p.A.(person) 87 pattern := p.B.(person) 88 fmt.Printf(" %s — %s\n", people.name, pattern.name) 89 } 90 fmt.Println() 91 } 92 93 // Unordered output: 94 // Person — Relationship position: 95 // Cleopatra — A 96 // Mark Antony — B 97 // Octavia — C 98 // 99 // Cleopatra — A 100 // Mark Antony — B 101 // Fulvia — C 102 } 103 104 // undirected converts a directed graph to an undirected graph 105 // with edges between nodes only where directed edges exist in 106 // both directions in the original graph. 107 type undirected struct { 108 graph.Directed 109 } 110 111 func (g undirected) From(uid int64) graph.Nodes { 112 nodes := graph.NodesOf(g.Directed.From(uid)) 113 for i := 0; i < len(nodes); { 114 if g.Directed.Edge(nodes[i].ID(), uid) != nil { 115 i++ 116 } else { 117 nodes[i], nodes = nodes[len(nodes)-1], nodes[:len(nodes)-1] 118 } 119 } 120 return iterator.NewOrderedNodes(nodes) 121 } 122 func (g undirected) Edge(xid, yid int64) graph.Edge { 123 e := g.Directed.Edge(xid, yid) 124 if e != nil && g.Directed.Edge(yid, xid) != nil { 125 return e 126 } 127 return nil 128 } 129 func (g undirected) EdgeBetween(xid, yid int64) graph.Edge { 130 return g.Edge(xid, yid) 131 }