github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/graph/product/product.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
     6  
     7  import (
     8  	"sort"
     9  
    10  	"github.com/jingcheng-WU/gonum/graph"
    11  	"github.com/jingcheng-WU/gonum/graph/internal/ordered"
    12  	"github.com/jingcheng-WU/gonum/stat/combin"
    13  )
    14  
    15  // Node is a product of two graph nodes.
    16  // All graph products return this type directly via relevant graph.Graph method
    17  // call, or indirectly via calls to graph.Edge methods from returned edges.
    18  type Node struct {
    19  	UID int64
    20  
    21  	// A and B hold the nodes from graph a and b in a
    22  	// graph product constructed by a product function.
    23  	A, B graph.Node
    24  }
    25  
    26  // ID implements the graph.Node interface.
    27  func (n Node) ID() int64 { return n.UID }
    28  
    29  // Cartesian constructs the Cartesian product of a and b in dst.
    30  //
    31  // The Cartesian product of G₁ and G₂, G₁□G₂ has edges (u₁, u₂)~(v₁, v₂) when
    32  // (u₁=v₁ and u₂~v₂) or (u₁~v₁ and u₂=v₂). The Cartesian product has size m₂n₁+m₁n₂
    33  // where m is the size of the input graphs and n is their order.
    34  func Cartesian(dst graph.Builder, a, b graph.Graph) {
    35  	aNodes, bNodes, product := cartesianNodes(a, b)
    36  	if len(product) == 0 {
    37  		return
    38  	}
    39  
    40  	indexOfA := indexOf(aNodes)
    41  	indexOfB := indexOf(bNodes)
    42  
    43  	for _, p := range product {
    44  		dst.AddNode(p)
    45  	}
    46  
    47  	dims := []int{len(aNodes), len(bNodes)}
    48  	for i, uA := range aNodes {
    49  		for j, uB := range bNodes {
    50  			toB := b.From(uB.ID())
    51  			for toB.Next() {
    52  				dst.SetEdge(dst.NewEdge(
    53  					product[combin.IdxFor([]int{i, j}, dims)],
    54  					product[combin.IdxFor([]int{i, indexOfB[toB.Node().ID()]}, dims)],
    55  				))
    56  			}
    57  
    58  			toA := a.From(uA.ID())
    59  			for toA.Next() {
    60  				dst.SetEdge(dst.NewEdge(
    61  					product[combin.IdxFor([]int{i, j}, dims)],
    62  					product[combin.IdxFor([]int{indexOfA[toA.Node().ID()], j}, dims)],
    63  				))
    64  			}
    65  		}
    66  	}
    67  }
    68  
    69  // Tensor constructs the Tensor product of a and b in dst.
    70  //
    71  // The Tensor product of G₁ and G₂, G₁⨯G₂ has edges (u₁, u₂)~(v₁, v₂) when
    72  // u₁~v₁ and u₂~v₂. The Tensor product has size 2m₁m₂ where m is the size
    73  // of the input graphs.
    74  func Tensor(dst graph.Builder, a, b graph.Graph) {
    75  	aNodes, bNodes, product := cartesianNodes(a, b)
    76  	if len(product) == 0 {
    77  		return
    78  	}
    79  
    80  	indexOfA := indexOf(aNodes)
    81  	indexOfB := indexOf(bNodes)
    82  
    83  	for _, p := range product {
    84  		dst.AddNode(p)
    85  	}
    86  
    87  	dims := []int{len(aNodes), len(bNodes)}
    88  	for i, uA := range aNodes {
    89  		toA := a.From(uA.ID())
    90  		for toA.Next() {
    91  			j := indexOfA[toA.Node().ID()]
    92  			for k, uB := range bNodes {
    93  				toB := b.From(uB.ID())
    94  				for toB.Next() {
    95  					dst.SetEdge(dst.NewEdge(
    96  						product[combin.IdxFor([]int{i, k}, dims)],
    97  						product[combin.IdxFor([]int{j, indexOfB[toB.Node().ID()]}, dims)],
    98  					))
    99  				}
   100  			}
   101  		}
   102  	}
   103  }
   104  
   105  // Lexicographical constructs the Lexicographical product of a and b in dst.
   106  //
   107  // The Lexicographical product of G₁ and G₂, G₁·G₂ has edges (u₁, u₂)~(v₁, v₂) when
   108  // u₁~v₁ or (u₁=v₁ and u₂~v₂). The Lexicographical product has size m₂n₁+m₁n₂²
   109  // where m is the size of the input graphs and n is their order.
   110  func Lexicographical(dst graph.Builder, a, b graph.Graph) {
   111  	aNodes, bNodes, product := cartesianNodes(a, b)
   112  	if len(product) == 0 {
   113  		return
   114  	}
   115  
   116  	indexOfA := indexOf(aNodes)
   117  	indexOfB := indexOf(bNodes)
   118  
   119  	for _, p := range product {
   120  		dst.AddNode(p)
   121  	}
   122  
   123  	dims := []int{len(aNodes), len(bNodes)}
   124  	p := make([]int, 2)
   125  	for i, uA := range aNodes {
   126  		toA := a.From(uA.ID())
   127  		for toA.Next() {
   128  			j := indexOfA[toA.Node().ID()]
   129  			gen := combin.NewCartesianGenerator([]int{len(bNodes), len(bNodes)})
   130  			for gen.Next() {
   131  				p = gen.Product(p)
   132  				dst.SetEdge(dst.NewEdge(
   133  					product[combin.IdxFor([]int{i, p[0]}, dims)],
   134  					product[combin.IdxFor([]int{j, p[1]}, dims)],
   135  				))
   136  			}
   137  		}
   138  
   139  		for j, uB := range bNodes {
   140  			toB := b.From(uB.ID())
   141  			for toB.Next() {
   142  				dst.SetEdge(dst.NewEdge(
   143  					product[combin.IdxFor([]int{i, j}, dims)],
   144  					product[combin.IdxFor([]int{i, indexOfB[toB.Node().ID()]}, dims)],
   145  				))
   146  			}
   147  		}
   148  	}
   149  }
   150  
   151  // Strong constructs the Strong product of a and b in dst.
   152  //
   153  // The Strong product of G₁ and G₂, G₁⊠G₂ has edges (u₁, u₂)~(v₁, v₂) when
   154  // (u₁=v₁ and u₂~v₂) or (u₁~v₁ and u₂=v₂) or (u₁~v₁ and u₂~v₂). The Strong
   155  // product has size n₁m₂+n₂m₁+2m₁m₂ where m is the size of the input graphs
   156  // and n is their order.
   157  func Strong(dst graph.Builder, a, b graph.Graph) {
   158  	aNodes, bNodes, product := cartesianNodes(a, b)
   159  	if len(product) == 0 {
   160  		return
   161  	}
   162  
   163  	indexOfA := indexOf(aNodes)
   164  	indexOfB := indexOf(bNodes)
   165  
   166  	for _, p := range product {
   167  		dst.AddNode(p)
   168  	}
   169  
   170  	dims := []int{len(aNodes), len(bNodes)}
   171  	for i, uA := range aNodes {
   172  		for j, uB := range bNodes {
   173  			toB := b.From(uB.ID())
   174  			for toB.Next() {
   175  				dst.SetEdge(dst.NewEdge(
   176  					product[combin.IdxFor([]int{i, j}, dims)],
   177  					product[combin.IdxFor([]int{i, indexOfB[toB.Node().ID()]}, dims)],
   178  				))
   179  			}
   180  
   181  			toA := a.From(uA.ID())
   182  			for toA.Next() {
   183  				dst.SetEdge(dst.NewEdge(
   184  					product[combin.IdxFor([]int{i, j}, dims)],
   185  					product[combin.IdxFor([]int{indexOfA[toA.Node().ID()], j}, dims)],
   186  				))
   187  			}
   188  		}
   189  
   190  		toA := a.From(uA.ID())
   191  		for toA.Next() {
   192  			for j, uB := range bNodes {
   193  				toB := b.From(uB.ID())
   194  				for toB.Next() {
   195  					dst.SetEdge(dst.NewEdge(
   196  						product[combin.IdxFor([]int{i, j}, dims)],
   197  						product[combin.IdxFor([]int{indexOfA[toA.Node().ID()], indexOfB[toB.Node().ID()]}, dims)],
   198  					))
   199  				}
   200  			}
   201  		}
   202  	}
   203  }
   204  
   205  // CoNormal constructs the Co-normal product of a and b in dst.
   206  //
   207  // The Co-normal product of G₁ and G₂, G₁*G₂ (or G₁[G₂]) has edges (u₁, u₂)~(v₁, v₂)
   208  // when u₁~v₁ or u₂~v₂. The Co-normal product is non-commutative.
   209  func CoNormal(dst graph.Builder, a, b graph.Graph) {
   210  	aNodes, bNodes, product := cartesianNodes(a, b)
   211  	if len(product) == 0 {
   212  		return
   213  	}
   214  
   215  	indexOfA := indexOf(aNodes)
   216  	indexOfB := indexOf(bNodes)
   217  
   218  	for _, p := range product {
   219  		dst.AddNode(p)
   220  	}
   221  
   222  	dims := []int{len(aNodes), len(bNodes)}
   223  	p := make([]int, 2)
   224  	for i, u := range aNodes {
   225  		to := a.From(u.ID())
   226  		for to.Next() {
   227  			j := indexOfA[to.Node().ID()]
   228  			gen := combin.NewCartesianGenerator([]int{len(bNodes), len(bNodes)})
   229  			for gen.Next() {
   230  				p = gen.Product(p)
   231  				dst.SetEdge(dst.NewEdge(
   232  					product[combin.IdxFor([]int{i, p[0]}, dims)],
   233  					product[combin.IdxFor([]int{j, p[1]}, dims)],
   234  				))
   235  			}
   236  		}
   237  	}
   238  	for i, u := range bNodes {
   239  		to := b.From(u.ID())
   240  		for to.Next() {
   241  			j := indexOfB[to.Node().ID()]
   242  			gen := combin.NewCartesianGenerator([]int{len(aNodes), len(aNodes)})
   243  			for gen.Next() {
   244  				p = gen.Product(p)
   245  				dst.SetEdge(dst.NewEdge(
   246  					product[combin.IdxFor([]int{p[0], i}, dims)],
   247  					product[combin.IdxFor([]int{p[1], j}, dims)],
   248  				))
   249  			}
   250  		}
   251  	}
   252  }
   253  
   254  // Modular constructs the Modular product of a and b in dst.
   255  //
   256  // The Modular product of G₁ and G₂, G₁◊G₂ has edges (u₁, u₂)~(v₁, v₂)
   257  // when (u₁~v₁ and u₂~v₂) or (u₁≁v₁ and u₂≁v₂), and (u₁≠v₁ and u₂≠v₂).
   258  //
   259  // Modular is O(n^2) time where n is the order of the Cartesian product
   260  // of a and b.
   261  func Modular(dst graph.Builder, a, b graph.Graph) {
   262  	_, _, product := cartesianNodes(a, b)
   263  	if len(product) == 0 {
   264  		return
   265  	}
   266  
   267  	for _, p := range product {
   268  		dst.AddNode(p)
   269  	}
   270  
   271  	_, aUndirected := a.(graph.Undirected)
   272  	_, bUndirected := b.(graph.Undirected)
   273  	_, dstUndirected := dst.(graph.Undirected)
   274  	undirected := aUndirected && bUndirected && dstUndirected
   275  
   276  	n := len(product)
   277  	if undirected {
   278  		n--
   279  	}
   280  	for i, u := range product[:n] {
   281  		var m int
   282  		if undirected {
   283  			m = i + 1
   284  		}
   285  		for _, v := range product[m:] {
   286  			if u.A.ID() == v.A.ID() || u.B.ID() == v.B.ID() {
   287  				// No self-loops.
   288  				continue
   289  			}
   290  			inA := a.Edge(u.A.ID(), v.A.ID()) != nil
   291  			inB := b.Edge(u.B.ID(), v.B.ID()) != nil
   292  			if inA == inB {
   293  				dst.SetEdge(dst.NewEdge(u, v))
   294  			}
   295  		}
   296  	}
   297  }
   298  
   299  // ModularExt constructs the Modular product of a and b in dst with
   300  // additional control over assessing edge agreement.
   301  //
   302  // In addition to the modular product conditions, agree(u₁v₁, u₂v₂) must
   303  // return true when (u₁~v₁ and u₂~v₂) for an edge to be added between
   304  // (u₁, u₂) and (v₁, v₂) in dst. If agree is nil, Modular is called.
   305  //
   306  // ModularExt is O(n^2) time where n is the order of the Cartesian product
   307  // of a and b.
   308  func ModularExt(dst graph.Builder, a, b graph.Graph, agree func(eA, eB graph.Edge) bool) {
   309  	if agree == nil {
   310  		Modular(dst, a, b)
   311  		return
   312  	}
   313  
   314  	_, _, product := cartesianNodes(a, b)
   315  	if len(product) == 0 {
   316  		return
   317  	}
   318  
   319  	for _, p := range product {
   320  		dst.AddNode(p)
   321  	}
   322  
   323  	_, aUndirected := a.(graph.Undirected)
   324  	_, bUndirected := b.(graph.Undirected)
   325  	_, dstUndirected := dst.(graph.Undirected)
   326  	undirected := aUndirected && bUndirected && dstUndirected
   327  
   328  	n := len(product)
   329  	if undirected {
   330  		n--
   331  	}
   332  	for i, u := range product[:n] {
   333  		var m int
   334  		if undirected {
   335  			m = i + 1
   336  		}
   337  		for _, v := range product[m:] {
   338  			if u.A.ID() == v.A.ID() || u.B.ID() == v.B.ID() {
   339  				// No self-loops.
   340  				continue
   341  			}
   342  			eA := a.Edge(u.A.ID(), v.A.ID())
   343  			eB := b.Edge(u.B.ID(), v.B.ID())
   344  			inA := eA != nil
   345  			inB := eB != nil
   346  			if (inA && inB && agree(eA, eB)) || (!inA && !inB) {
   347  				dst.SetEdge(dst.NewEdge(u, v))
   348  			}
   349  		}
   350  	}
   351  }
   352  
   353  // cartesianNodes returns the Cartesian product of the nodes in a and b.
   354  func cartesianNodes(a, b graph.Graph) (aNodes, bNodes []graph.Node, product []Node) {
   355  	aNodes = lexicalNodes(a)
   356  	bNodes = lexicalNodes(b)
   357  
   358  	lens := []int{len(aNodes), len(bNodes)}
   359  	product = make([]Node, combin.Card(lens))
   360  	gen := combin.NewCartesianGenerator(lens)
   361  	p := make([]int, 2)
   362  	for id := int64(0); gen.Next(); id++ {
   363  		p = gen.Product(p)
   364  		product[id] = Node{UID: id, A: aNodes[p[0]], B: bNodes[p[1]]}
   365  	}
   366  	return aNodes, bNodes, product
   367  }
   368  
   369  // lexicalNodes returns the nodes in g sorted lexically by node ID.
   370  func lexicalNodes(g graph.Graph) []graph.Node {
   371  	nodes := graph.NodesOf(g.Nodes())
   372  	sort.Sort(ordered.ByID(nodes))
   373  	return nodes
   374  }
   375  
   376  func indexOf(nodes []graph.Node) map[int64]int {
   377  	idx := make(map[int64]int, len(nodes))
   378  	for i, n := range nodes {
   379  		idx[n.ID()] = i
   380  	}
   381  	return idx
   382  }