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 }