mirror of
https://github.com/gonum/gonum.git
synced 2025-10-05 23:26:52 +08:00
242 lines
6.2 KiB
Go
242 lines
6.2 KiB
Go
// Copyright ©2015 The Gonum Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package gen
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"gonum.org/v1/gonum/graph"
|
|
)
|
|
|
|
// GraphBuilder is a graph that can have nodes and edges added.
|
|
type GraphBuilder interface {
|
|
HasEdgeBetween(xid, yid int64) bool
|
|
graph.Builder
|
|
}
|
|
|
|
func abs(a int) int {
|
|
if a < 0 {
|
|
return -a
|
|
}
|
|
return a
|
|
}
|
|
|
|
// NodeIDGraphBuild is a graph that can create new nodes with
|
|
// specified IDs.
|
|
type NodeIDGraphBuilder interface {
|
|
graph.Builder
|
|
graph.NodeWithIDer
|
|
}
|
|
|
|
// IDer is a mapping from an index to a node ID.
|
|
type IDer interface {
|
|
// Len returns the length of the set of node IDs.
|
|
Len() int
|
|
|
|
// ID returns the ID of the indexed node.
|
|
// ID must be a bijective function. No check
|
|
// is made for this property.
|
|
ID(int) int64
|
|
}
|
|
|
|
// IDRange is an IDer that provides a set of IDs in [First, Last].
|
|
type IDRange struct{ First, Last int64 }
|
|
|
|
func (r IDRange) Len() int { return int(r.Last - r.First + 1) }
|
|
func (r IDRange) ID(i int) int64 { return r.First + int64(i) }
|
|
|
|
// IDSet is an IDer providing an explicit set of IDs.
|
|
type IDSet []int64
|
|
|
|
func (s IDSet) Len() int { return len(s) }
|
|
func (s IDSet) ID(i int) int64 { return s[i] }
|
|
|
|
// Complete constructs a complete graph in dst using nodes with the given IDs.
|
|
// If any ID appears twice in ids, Complete will panic.
|
|
func Complete(dst NodeIDGraphBuilder, ids IDer) {
|
|
switch ids.Len() {
|
|
case 0:
|
|
return
|
|
case 1:
|
|
u, new := dst.NodeWithID(ids.ID(0))
|
|
if new {
|
|
dst.AddNode(u)
|
|
}
|
|
return
|
|
}
|
|
for i := 0; i < ids.Len(); i++ {
|
|
uid := ids.ID(i)
|
|
u, _ := dst.NodeWithID(uid)
|
|
for j := i + 1; j < ids.Len(); j++ {
|
|
vid := ids.ID(j)
|
|
if uid == vid {
|
|
panic(fmt.Errorf("gen: node ID collision i=%d j=%d: id=%d", i, j, uid))
|
|
}
|
|
v, _ := dst.NodeWithID(vid)
|
|
dst.SetEdge(dst.NewEdge(u, v))
|
|
}
|
|
}
|
|
}
|
|
|
|
// Cycle constructs a cycle in dst using the node IDs in cycle.
|
|
// If dst is a directed graph, edges are directed from earlier nodes to later
|
|
// nodes in cycle. If any ID appears twice in cycle, Cycle will panic.
|
|
func Cycle(dst NodeIDGraphBuilder, cycle IDer) {
|
|
switch cycle.Len() {
|
|
case 0:
|
|
return
|
|
case 1:
|
|
u, new := dst.NodeWithID(cycle.ID(0))
|
|
if new {
|
|
dst.AddNode(u)
|
|
}
|
|
return
|
|
}
|
|
err := check(cycle)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
cycleNoCheck(dst, cycle)
|
|
}
|
|
|
|
func cycleNoCheck(dst NodeIDGraphBuilder, cycle IDer) {
|
|
for i := 0; i < cycle.Len(); i++ {
|
|
uid := cycle.ID(i)
|
|
vid := cycle.ID((i + 1) % cycle.Len())
|
|
u, _ := dst.NodeWithID(uid)
|
|
v, _ := dst.NodeWithID(vid)
|
|
dst.SetEdge(dst.NewEdge(u, v))
|
|
}
|
|
}
|
|
|
|
// Path constructs a path graph in dst with
|
|
// If dst is a directed graph, edges are directed from earlier nodes to later
|
|
// nodes in path. If any ID appears twice in path, Path will panic.
|
|
func Path(dst NodeIDGraphBuilder, path IDer) {
|
|
switch path.Len() {
|
|
case 0:
|
|
return
|
|
case 1:
|
|
u, new := dst.NodeWithID(path.ID(0))
|
|
if new {
|
|
dst.AddNode(u)
|
|
}
|
|
return
|
|
}
|
|
err := check(path)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
for i := 0; i < path.Len()-1; i++ {
|
|
uid := path.ID(i)
|
|
vid := path.ID(i + 1)
|
|
u, _ := dst.NodeWithID(uid)
|
|
v, _ := dst.NodeWithID(vid)
|
|
dst.SetEdge(dst.NewEdge(u, v))
|
|
}
|
|
}
|
|
|
|
// Star constructs a star graph in dst with edges between the center node ID to
|
|
// node with IDs specified in leaves.
|
|
// If dst is a directed graph, edges are directed from the center node to the
|
|
// leaves. If any ID appears twice in leaves and center, Star will panic.
|
|
func Star(dst NodeIDGraphBuilder, center int64, leaves IDer) {
|
|
c, new := dst.NodeWithID(center)
|
|
if new {
|
|
dst.AddNode(c)
|
|
}
|
|
if leaves.Len() == 0 {
|
|
return
|
|
}
|
|
err := check(leaves, center)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
for i := 0; i < leaves.Len(); i++ {
|
|
id := leaves.ID(i)
|
|
n, _ := dst.NodeWithID(id)
|
|
dst.SetEdge(dst.NewEdge(c, n))
|
|
}
|
|
}
|
|
|
|
// Wheel constructs a wheel graph in dst with edges from the center
|
|
// node ID to node with IDs specified in cycle and between nodes with IDs
|
|
// adjacent in the cycle.
|
|
// If dst is a directed graph, edges are directed from the center node to the
|
|
// cycle and from earlier nodes to later nodes in cycle. If any ID appears
|
|
// twice in cycle and center, Wheel will panic.
|
|
func Wheel(dst NodeIDGraphBuilder, center int64, cycle IDer) {
|
|
Star(dst, center, cycle)
|
|
if cycle.Len() <= 1 {
|
|
return
|
|
}
|
|
cycleNoCheck(dst, cycle)
|
|
}
|
|
|
|
// Tree constructs an n-ary tree breadth-first in dst with the given fan-out, n.
|
|
// If the number of nodes does not equal \sum_{i=0}^h n^i, where h is an integer
|
|
// indicating the height of the tree, a partial tree will be constructed with not
|
|
// all nodes having zero or n children, and not all leaves being h from the root.
|
|
// If the number of nodes is greater than one, n must be non-zero and
|
|
// less than the number of nodes, otherwise Tree will panic.
|
|
// If dst is a directed graph, edges are directed from the root to the leaves.
|
|
// If any ID appears more than once in nodes, Tree will panic.
|
|
func Tree(dst NodeIDGraphBuilder, n int, nodes IDer) {
|
|
switch nodes.Len() {
|
|
case 0:
|
|
return
|
|
case 1:
|
|
if u, new := dst.NodeWithID(nodes.ID(0)); new {
|
|
dst.AddNode(u)
|
|
}
|
|
return
|
|
}
|
|
|
|
if n < 1 || nodes.Len() <= n {
|
|
panic("gen: invalid fan-out")
|
|
}
|
|
|
|
err := check(nodes)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
j := 0
|
|
for i := 0; j < nodes.Len(); i++ {
|
|
u, _ := dst.NodeWithID(nodes.ID(i))
|
|
for j = n*i + 1; j <= n*i+n && j < nodes.Len(); j++ {
|
|
v, _ := dst.NodeWithID(nodes.ID(j))
|
|
dst.SetEdge(dst.NewEdge(u, v))
|
|
}
|
|
}
|
|
}
|
|
|
|
// check confirms that no node ID exists more than once in ids and extra.
|
|
func check(ids IDer, extra ...int64) error {
|
|
seen := make(map[int64]int, ids.Len()+len(extra))
|
|
for j := 0; j < ids.Len(); j++ {
|
|
uid := ids.ID(j)
|
|
if prev, exists := seen[uid]; exists {
|
|
return fmt.Errorf("gen: node ID collision i=%d j=%d: id=%d", prev, j, uid)
|
|
}
|
|
seen[uid] = j
|
|
}
|
|
lenIDs := ids.Len()
|
|
for j, uid := range extra {
|
|
if prev, exists := seen[uid]; exists {
|
|
if prev < lenIDs {
|
|
if len(extra) == 1 {
|
|
return fmt.Errorf("gen: node ID collision i=%d with extra: id=%d", prev, uid)
|
|
}
|
|
return fmt.Errorf("gen: node ID collision i=%d with extra j=%d: id=%d", prev, j, uid)
|
|
}
|
|
prev -= lenIDs
|
|
return fmt.Errorf("gen: extra node ID collision i=%d j=%d: id=%d", prev, j, uid)
|
|
}
|
|
seen[uid] = j + lenIDs
|
|
}
|
|
return nil
|
|
}
|