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