replace parser with actions/workflow-parser
This commit is contained in:
406
vendor/github.com/soniakeys/graph/adj.go
generated
vendored
Normal file
406
vendor/github.com/soniakeys/graph/adj.go
generated
vendored
Normal file
@@ -0,0 +1,406 @@
|
||||
// Copyright 2014 Sonia Keys
|
||||
// License MIT: https://opensource.org/licenses/MIT
|
||||
|
||||
package graph
|
||||
|
||||
// adj.go contains methods on AdjacencyList and LabeledAdjacencyList.
|
||||
//
|
||||
// AdjacencyList methods are placed first and are alphabetized.
|
||||
// LabeledAdjacencyList methods follow, also alphabetized.
|
||||
// Only exported methods need be alphabetized; non-exported methods can
|
||||
// be left near their use.
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/soniakeys/bits"
|
||||
)
|
||||
|
||||
// NI is a "node int"
|
||||
//
|
||||
// It is a node number or node ID. NIs are used extensively as slice indexes.
|
||||
// NIs typically account for a significant fraction of the memory footprint of
|
||||
// a graph.
|
||||
type NI int32
|
||||
|
||||
// AnyParallel identifies if a graph contains parallel arcs, multiple arcs
|
||||
// that lead from a node to the same node.
|
||||
//
|
||||
// If the graph has parallel arcs, the results fr and to represent an example
|
||||
// where there are parallel arcs from node `fr` to node `to`.
|
||||
//
|
||||
// If there are no parallel arcs, the method returns false -1 -1.
|
||||
//
|
||||
// Multiple loops on a node count as parallel arcs.
|
||||
//
|
||||
// See also alt.AnyParallelMap, which can perform better for some large
|
||||
// or dense graphs.
|
||||
func (g AdjacencyList) AnyParallel() (has bool, fr, to NI) {
|
||||
var t []NI
|
||||
for n, to := range g {
|
||||
if len(to) == 0 {
|
||||
continue
|
||||
}
|
||||
// different code in the labeled version, so no code gen.
|
||||
t = append(t[:0], to...)
|
||||
sort.Slice(t, func(i, j int) bool { return t[i] < t[j] })
|
||||
t0 := t[0]
|
||||
for _, to := range t[1:] {
|
||||
if to == t0 {
|
||||
return true, NI(n), t0
|
||||
}
|
||||
t0 = to
|
||||
}
|
||||
}
|
||||
return false, -1, -1
|
||||
}
|
||||
|
||||
// Complement returns the arc-complement of a simple graph.
|
||||
//
|
||||
// The result will have an arc for every pair of distinct nodes where there
|
||||
// is not an arc in g. The complement is valid for both directed and
|
||||
// undirected graphs. If g is undirected, the complement will be undirected.
|
||||
// The result will always be a simple graph, having no loops or parallel arcs.
|
||||
func (g AdjacencyList) Complement() AdjacencyList {
|
||||
c := make(AdjacencyList, len(g))
|
||||
b := bits.New(len(g))
|
||||
for n, to := range g {
|
||||
b.ClearAll()
|
||||
for _, to := range to {
|
||||
b.SetBit(int(to), 1)
|
||||
}
|
||||
b.SetBit(n, 1)
|
||||
ct := make([]NI, len(g)-b.OnesCount())
|
||||
i := 0
|
||||
b.IterateZeros(func(to int) bool {
|
||||
ct[i] = NI(to)
|
||||
i++
|
||||
return true
|
||||
})
|
||||
c[n] = ct
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// IsUndirected returns true if g represents an undirected graph.
|
||||
//
|
||||
// Returns true when all non-loop arcs are paired in reciprocal pairs.
|
||||
// Otherwise returns false and an example unpaired arc.
|
||||
func (g AdjacencyList) IsUndirected() (u bool, from, to NI) {
|
||||
// similar code in dot/writeUndirected
|
||||
unpaired := make(AdjacencyList, len(g))
|
||||
for fr, to := range g {
|
||||
arc: // for each arc in g
|
||||
for _, to := range to {
|
||||
if to == NI(fr) {
|
||||
continue // loop
|
||||
}
|
||||
// search unpaired arcs
|
||||
ut := unpaired[to]
|
||||
for i, u := range ut {
|
||||
if u == NI(fr) { // found reciprocal
|
||||
last := len(ut) - 1
|
||||
ut[i] = ut[last]
|
||||
unpaired[to] = ut[:last]
|
||||
continue arc
|
||||
}
|
||||
}
|
||||
// reciprocal not found
|
||||
unpaired[fr] = append(unpaired[fr], to)
|
||||
}
|
||||
}
|
||||
for fr, to := range unpaired {
|
||||
if len(to) > 0 {
|
||||
return false, NI(fr), to[0]
|
||||
}
|
||||
}
|
||||
return true, -1, -1
|
||||
}
|
||||
|
||||
// SortArcLists sorts the arc lists of each node of receiver g.
|
||||
//
|
||||
// Nodes are not relabeled and the graph remains equivalent.
|
||||
func (g AdjacencyList) SortArcLists() {
|
||||
for _, to := range g {
|
||||
sort.Slice(to, func(i, j int) bool { return to[i] < to[j] })
|
||||
}
|
||||
}
|
||||
|
||||
// ------- Labeled methods below -------
|
||||
|
||||
// ArcsAsEdges constructs an edge list with an edge for each arc, including
|
||||
// reciprocals.
|
||||
//
|
||||
// This is a simple way to construct an edge list for algorithms that allow
|
||||
// the duplication represented by the reciprocal arcs. (e.g. Kruskal)
|
||||
//
|
||||
// See also LabeledUndirected.Edges for the edge list without this duplication.
|
||||
func (g LabeledAdjacencyList) ArcsAsEdges() (el []LabeledEdge) {
|
||||
for fr, to := range g {
|
||||
for _, to := range to {
|
||||
el = append(el, LabeledEdge{Edge{NI(fr), to.To}, to.Label})
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DistanceMatrix constructs a distance matrix corresponding to the arcs
|
||||
// of graph g and weight function w.
|
||||
//
|
||||
// An arc from f to t with WeightFunc return w is represented by d[f][t] == w.
|
||||
// In case of parallel arcs, the lowest weight is stored. The distance from
|
||||
// any node to itself d[n][n] is 0, unless the node has a loop with a negative
|
||||
// weight. If g has no arc from f to distinct t, +Inf is stored for d[f][t].
|
||||
//
|
||||
// The returned DistanceMatrix is suitable for DistanceMatrix.FloydWarshall.
|
||||
func (g LabeledAdjacencyList) DistanceMatrix(w WeightFunc) (d DistanceMatrix) {
|
||||
d = newDM(len(g))
|
||||
for fr, to := range g {
|
||||
for _, to := range to {
|
||||
// < to pick min of parallel arcs (also nicely ignores NaN)
|
||||
if wt := w(to.Label); wt < d[fr][to.To] {
|
||||
d[fr][to.To] = wt
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// HasArcLabel returns true if g has any arc from node `fr` to node `to`
|
||||
// with label `l`.
|
||||
//
|
||||
// Also returned is the index within the slice of arcs from node `fr`.
|
||||
// If no arc from `fr` to `to` with label `l` is present, HasArcLabel returns
|
||||
// false, -1.
|
||||
func (g LabeledAdjacencyList) HasArcLabel(fr, to NI, l LI) (bool, int) {
|
||||
t := Half{to, l}
|
||||
for x, h := range g[fr] {
|
||||
if h == t {
|
||||
return true, x
|
||||
}
|
||||
}
|
||||
return false, -1
|
||||
}
|
||||
|
||||
// AnyParallel identifies if a graph contains parallel arcs, multiple arcs
|
||||
// that lead from a node to the same node.
|
||||
//
|
||||
// If the graph has parallel arcs, the results fr and to represent an example
|
||||
// where there are parallel arcs from node `fr` to node `to`.
|
||||
//
|
||||
// If there are no parallel arcs, the method returns -1 -1.
|
||||
//
|
||||
// Multiple loops on a node count as parallel arcs.
|
||||
//
|
||||
// See also alt.AnyParallelMap, which can perform better for some large
|
||||
// or dense graphs.
|
||||
func (g LabeledAdjacencyList) AnyParallel() (has bool, fr, to NI) {
|
||||
var t []NI
|
||||
for n, to := range g {
|
||||
if len(to) == 0 {
|
||||
continue
|
||||
}
|
||||
// slightly different code needed here compared to AdjacencyList
|
||||
t = t[:0]
|
||||
for _, to := range to {
|
||||
t = append(t, to.To)
|
||||
}
|
||||
sort.Slice(t, func(i, j int) bool { return t[i] < t[j] })
|
||||
t0 := t[0]
|
||||
for _, to := range t[1:] {
|
||||
if to == t0 {
|
||||
return true, NI(n), t0
|
||||
}
|
||||
t0 = to
|
||||
}
|
||||
}
|
||||
return false, -1, -1
|
||||
}
|
||||
|
||||
// AnyParallelLabel identifies if a graph contains parallel arcs with the same
|
||||
// label.
|
||||
//
|
||||
// If the graph has parallel arcs with the same label, the results fr and to
|
||||
// represent an example where there are parallel arcs from node `fr`
|
||||
// to node `to`.
|
||||
//
|
||||
// If there are no parallel arcs, the method returns false -1 Half{}.
|
||||
//
|
||||
// Multiple loops on a node count as parallel arcs.
|
||||
func (g LabeledAdjacencyList) AnyParallelLabel() (has bool, fr NI, to Half) {
|
||||
var t []Half
|
||||
for n, to := range g {
|
||||
if len(to) == 0 {
|
||||
continue
|
||||
}
|
||||
// slightly different code needed here compared to AdjacencyList
|
||||
t = t[:0]
|
||||
for _, to := range to {
|
||||
t = append(t, to)
|
||||
}
|
||||
sort.Slice(t, func(i, j int) bool {
|
||||
return t[i].To < t[j].To ||
|
||||
t[i].To == t[j].To && t[i].Label < t[j].Label
|
||||
})
|
||||
t0 := t[0]
|
||||
for _, to := range t[1:] {
|
||||
if to == t0 {
|
||||
return true, NI(n), t0
|
||||
}
|
||||
t0 = to
|
||||
}
|
||||
}
|
||||
return false, -1, Half{}
|
||||
}
|
||||
|
||||
// IsUndirected returns true if g represents an undirected graph.
|
||||
//
|
||||
// Returns true when all non-loop arcs are paired in reciprocal pairs with
|
||||
// matching labels. Otherwise returns false and an example unpaired arc.
|
||||
//
|
||||
// Note the requirement that reciprocal pairs have matching labels is
|
||||
// an additional test not present in the otherwise equivalent unlabeled version
|
||||
// of IsUndirected.
|
||||
func (g LabeledAdjacencyList) IsUndirected() (u bool, from NI, to Half) {
|
||||
// similar code in LabeledAdjacencyList.Edges
|
||||
unpaired := make(LabeledAdjacencyList, len(g))
|
||||
for fr, to := range g {
|
||||
arc: // for each arc in g
|
||||
for _, to := range to {
|
||||
if to.To == NI(fr) {
|
||||
continue // loop
|
||||
}
|
||||
// search unpaired arcs
|
||||
ut := unpaired[to.To]
|
||||
for i, u := range ut {
|
||||
if u.To == NI(fr) && u.Label == to.Label { // found reciprocal
|
||||
last := len(ut) - 1
|
||||
ut[i] = ut[last]
|
||||
unpaired[to.To] = ut[:last]
|
||||
continue arc
|
||||
}
|
||||
}
|
||||
// reciprocal not found
|
||||
unpaired[fr] = append(unpaired[fr], to)
|
||||
}
|
||||
}
|
||||
for fr, to := range unpaired {
|
||||
if len(to) > 0 {
|
||||
return false, NI(fr), to[0]
|
||||
}
|
||||
}
|
||||
return true, -1, to
|
||||
}
|
||||
|
||||
// ArcLabels constructs the multiset of LIs present in g.
|
||||
func (g LabeledAdjacencyList) ArcLabels() map[LI]int {
|
||||
s := map[LI]int{}
|
||||
for _, to := range g {
|
||||
for _, to := range to {
|
||||
s[to.Label]++
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// NegativeArc returns true if the receiver graph contains a negative arc.
|
||||
func (g LabeledAdjacencyList) NegativeArc(w WeightFunc) bool {
|
||||
for _, nbs := range g {
|
||||
for _, nb := range nbs {
|
||||
if w(nb.Label) < 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ParallelArcsLabel identifies all arcs from node `fr` to node `to` with label `l`.
|
||||
//
|
||||
// The returned slice contains an element for each arc from node `fr` to node `to`
|
||||
// with label `l`. The element value is the index within the slice of arcs from node
|
||||
// `fr`.
|
||||
//
|
||||
// See also the method HasArcLabel, which stops after finding a single arc.
|
||||
func (g LabeledAdjacencyList) ParallelArcsLabel(fr, to NI, l LI) (p []int) {
|
||||
t := Half{to, l}
|
||||
for x, h := range g[fr] {
|
||||
if h == t {
|
||||
p = append(p, x)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Unlabeled constructs the unlabeled graph corresponding to g.
|
||||
func (g LabeledAdjacencyList) Unlabeled() AdjacencyList {
|
||||
a := make(AdjacencyList, len(g))
|
||||
for n, nbs := range g {
|
||||
to := make([]NI, len(nbs))
|
||||
for i, nb := range nbs {
|
||||
to[i] = nb.To
|
||||
}
|
||||
a[n] = to
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// WeightedArcsAsEdges constructs a WeightedEdgeList object from the receiver.
|
||||
//
|
||||
// Internally it calls g.ArcsAsEdges() to obtain the Edges member.
|
||||
// See LabeledAdjacencyList.ArcsAsEdges().
|
||||
func (g LabeledAdjacencyList) WeightedArcsAsEdges(w WeightFunc) *WeightedEdgeList {
|
||||
return &WeightedEdgeList{
|
||||
Order: g.Order(),
|
||||
WeightFunc: w,
|
||||
Edges: g.ArcsAsEdges(),
|
||||
}
|
||||
}
|
||||
|
||||
// WeightedInDegree computes the weighted in-degree of each node in g
|
||||
// for a given weight function w.
|
||||
//
|
||||
// The weighted in-degree of a node is the sum of weights of arcs going to
|
||||
// the node.
|
||||
//
|
||||
// A weighted degree of a node is often termed the "strength" of a node.
|
||||
//
|
||||
// See note for undirected graphs at LabeledAdjacencyList.WeightedOutDegree.
|
||||
func (g LabeledAdjacencyList) WeightedInDegree(w WeightFunc) []float64 {
|
||||
ind := make([]float64, len(g))
|
||||
for _, to := range g {
|
||||
for _, to := range to {
|
||||
ind[to.To] += w(to.Label)
|
||||
}
|
||||
}
|
||||
return ind
|
||||
}
|
||||
|
||||
// WeightedOutDegree computes the weighted out-degree of the specified node
|
||||
// for a given weight function w.
|
||||
//
|
||||
// The weighted out-degree of a node is the sum of weights of arcs going from
|
||||
// the node.
|
||||
//
|
||||
// A weighted degree of a node is often termed the "strength" of a node.
|
||||
//
|
||||
// Note for undirected graphs, the WeightedOutDegree result for a node will
|
||||
// equal the WeightedInDegree for the node. You can use WeightedInDegree if
|
||||
// you have need for the weighted degrees of all nodes or use WeightedOutDegree
|
||||
// to compute the weighted degrees of individual nodes. In either case loops
|
||||
// are counted just once, unlike the (unweighted) UndirectedDegree methods.
|
||||
func (g LabeledAdjacencyList) WeightedOutDegree(n NI, w WeightFunc) (d float64) {
|
||||
for _, to := range g[n] {
|
||||
d += w(to.Label)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// More about loops and strength: I didn't see consensus on this especially
|
||||
// in the case of undirected graphs. Some sources said to add in-degree and
|
||||
// out-degree, which would seemingly double both loops and non-loops.
|
||||
// Some said to double loops. Some said sum the edge weights and had no
|
||||
// comment on loops. R of course makes everything an option. The meaning
|
||||
// of "strength" where loops exist is unclear. So while I could write an
|
||||
// UndirectedWeighted degree function that doubles loops but not edges,
|
||||
// I'm going to just leave this for now.
|
Reference in New Issue
Block a user