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  }