Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev 29/upgrade go 1.23 #30

Merged
merged 11 commits into from
Oct 1, 2024
8 changes: 1 addition & 7 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
module github.com/nulab/autog

go 1.22.4

toolchain go1.22.7
go 1.23.1

require github.com/stretchr/testify v1.8.4

replace github.com/vibridi/graphify => ../../vibridi/graphify

require (
github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/vibridi/graphify v0.0.0-20240926092405-5791dfe773cb // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
31 changes: 0 additions & 31 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,41 +1,10 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY=
github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk=
github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b h1:slYM766cy2nI3BwyRiyQj/Ud48djTMtMebDqepE95rw=
github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/vibridi/graphify v0.0.0-20240926092405-5791dfe773cb h1:vJaelmTCdGCWuUUmERFznS6843ewGv4MGKv8nOETyt4=
github.com/vibridi/graphify v0.0.0-20240926092405-5791dfe773cb/go.mod h1:ClQsJC5L+MO0eABQoEJBx8EXSxi++0VrUuIYXcYeViY=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
3 changes: 0 additions & 3 deletions internal/geom/spline.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,6 @@ func FitSpline(path []P, tanv1, tanv2 P, barriers []Segment) []ctrlp {
upperps := FitSpline(path[:k+1], tanv1, tanw, barriers)
lowerps := FitSpline(path[k:], tanw, tanv2, barriers)

// if upperps != nil && lowerps != nil {
// return append(upperps, lowerps...)
// }
return append(upperps, lowerps...)
}

Expand Down
35 changes: 19 additions & 16 deletions internal/graph/dgraph.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package graph

import (
"iter"
"strings"
)

type DGraph struct {
Nodes []*Node
Edges EdgeList
Layers Layers
Layers []*Layer
}

func (g *DGraph) Populate(*DGraph) {
Expand All @@ -25,28 +26,30 @@ func (g *DGraph) GetEdges() []*Edge {
return nil
}

// todo: sources and sinks don't yet account for isolated nodes with a self-loop

// Sources returns a list of nodes with no incoming edges
func (g *DGraph) Sources() []*Node {
var sources []*Node
for _, n := range g.Nodes {
if len(n.In) == 0 {
sources = append(sources, n)
// Sources returns a sequence of nodes with no incoming edges
func (g *DGraph) Sources() iter.Seq[*Node] {
return func(yield func(*Node) bool) {
for _, n := range g.Nodes {
if n.Indeg() == 0 {
if !yield(n) {
return
}
}
}
}
return sources
}

// Sinks returns a list of nodes with no outgoing edges
func (g *DGraph) Sinks() []*Node {
var sinks []*Node
for _, n := range g.Nodes {
if len(n.Out) == 0 {
sinks = append(sinks, n)
func (g *DGraph) Sinks() iter.Seq[*Node] {
return func(yield func(*Node) bool) {
for _, n := range g.Nodes {
if n.Outdeg() == 0 {
if !yield(n) {
return
}
}
}
}
return sinks
}

func (g *DGraph) String() string {
Expand Down
38 changes: 23 additions & 15 deletions internal/graph/edge.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,22 +74,30 @@ func (e *Edge) Crosses(f *Edge) bool {
(etop.LayerPos > ftop.LayerPos && ebtm.LayerPos < fbtm.LayerPos)
}

// Type returns the edge type as follows:
// - 0: both of e's adjacent nodes are concrete nodes
// - 1: exactly one of e's adjacent nodes is virtual
// - 2: both of e's adjacent nodes are virtual
func (e *Edge) Type() int {
// todo: might return an enum instead
if !e.From.IsVirtual && !e.To.IsVirtual {
return 0
}
if e.From.IsVirtual != e.To.IsVirtual {
return 1
}
if e.From.IsVirtual && e.To.IsVirtual {
return 2
// EdgeType encodes information about the nodes adjacent to an edge,
type EdgeType uint8

const (
// EdgeTypeConcrete indicates a type 0 edge whose adjacent nodes are both non-virtual.
EdgeTypeConcrete EdgeType = iota
// EdgeTypeHybrid indicates a type 1 edge whose adjacent nodes are one virtual and one non-virtual.
EdgeTypeHybrid
// EdgeTypeVirtual indicates a type 2 edge whose adjacent nodes are both virtual.
EdgeTypeVirtual
)

// Type returns the edge's EdgeType
func (e *Edge) Type() EdgeType {
switch {
case !e.From.IsVirtual && !e.To.IsVirtual:
return EdgeTypeConcrete
case e.From.IsVirtual != e.To.IsVirtual:
return EdgeTypeHybrid
case e.From.IsVirtual && e.To.IsVirtual:
return EdgeTypeVirtual
default:
panic("edge type cases aren't exhaustive")
}
panic("edge type cases aren't exhaustive")
}

func (e *Edge) String() string {
Expand Down
2 changes: 0 additions & 2 deletions internal/graph/maps.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ type NodeSet = hashmap[*Node, bool]

type EdgeSet = hashmap[*Edge, bool]

type Layers = hashmap[int, *Layer]

func (m hashmap[K, V]) Clone() hashmap[K, V] {
return maps.Clone(m)
}
Expand Down
32 changes: 15 additions & 17 deletions internal/graph/node_iter.go
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
package graph

import "iter"

func (n *Node) VisitEdges(visit func(*Edge)) {
fn, next := n.allEdges()
for next {
next = fn(visit)
for e := range n.allEdges() {
visit(e)
}
}

func (n *Node) allEdges() (visitor func(func(*Edge)) bool, next bool) {
i := 0
visitor = func(yield func(*Edge)) bool {
if i >= len(n.In)+len(n.Out) {
return false
}
if i < len(n.In) {
yield(n.In[i])
} else {
yield(n.Out[i-len(n.In)])
func (n *Node) allEdges() iter.Seq[*Edge] {
return func(yield func(*Edge) bool) {
for i, b := 0, true; i < n.Deg(); i++ {
if i < n.Indeg() {
b = yield(n.In[i])
} else {
b = yield(n.Out[i-n.Indeg()])
}
if !b {
return
}
}
i++
return true
}
next = true
return
}
27 changes: 16 additions & 11 deletions internal/graph/node_iter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,27 @@ import (
)

func TestVisitEdges(t *testing.T) {
t.Skip()
ids := strings.Split("abcde", "")
es := []*Edge{}
for i := range ids {
es = append(es, &Edge{edge: edge{Delta: i}})
n := &Node{ID: "N"}
n.In = []*Edge{
{edge: edge{From: &Node{ID: "A1"}, To: n}},
{edge: edge{From: &Node{ID: "A2"}, To: n}},
{edge: edge{From: &Node{ID: "A3"}, To: n}},
}

n := &Node{
In: es[:3],
Out: es[3:],
n.Out = []*Edge{
{edge: edge{From: n, To: &Node{ID: "B1"}}},
{edge: edge{From: n, To: &Node{ID: "B2"}}},
{edge: edge{From: n, To: &Node{ID: "B3"}}},
{edge: edge{From: n, To: &Node{ID: "B4"}}},
}

i := 0
n.VisitEdges(func(e *Edge) {
assert.Equal(t, ids[i], i)
if i < 3 {
assert.True(t, strings.HasPrefix(e.From.ID, "A"))
} else {
assert.True(t, strings.HasPrefix(e.To.ID, "B"))
}
i++
})
assert.Equal(t, 5, i)
assert.Equal(t, 7, i)
}
11 changes: 3 additions & 8 deletions internal/phase1/dfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,19 @@ func execDepthFirst(g *graph.DGraph) {
active: make(graph.NodeSet),
}

// get list of source nodes (nodes with no incoming edge)
sources := g.Sources()

for _, node := range sources {
// process nodes with no incoming edge first
for node := range g.Sources() {
p.visit(node)
}

nodeCount := len(g.Nodes)
for i := 0; i < nodeCount; i++ {
node := g.Nodes[i]
for _, node := range g.Nodes {
if !p.visited[node] {
p.visit(node)
}
}

for _, e := range p.reversable {
e.Reverse()
// g.IsCyclic = true
}
}

Expand Down
5 changes: 3 additions & 2 deletions internal/phase1/greedy.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package phase1
import (
"math"
"math/rand"
"slices"
"time"

"github.com/nulab/autog/internal/graph"
Expand Down Expand Up @@ -37,8 +38,8 @@ func execGreedy(g *graph.DGraph, params graph.Params) {

nodeCount := len(g.Nodes)

sources := g.Sources()
sinks := g.Sinks()
sources := slices.Collect(g.Sources())
sinks := slices.Collect(g.Sinks())

// here ELK accounts for edge priority: particular edges that the user doesn't want to reverse
// can be assigned a non-zero priority; this will artificially increase the node's in-/out-degrees.
Expand Down
35 changes: 18 additions & 17 deletions internal/phase2/alg_process.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,27 +24,28 @@ func (alg Alg) Process(g *graph.DGraph, params graph.Params) {
}

initLayers:
m := map[int]*graph.Layer{}
size := 0
for _, n := range g.Nodes {
layer := m[n.Layer]
if layer == nil {
layer = &graph.Layer{Index: n.Layer}
}
layer.Nodes = append(layer.Nodes, n)
m[n.Layer] = layer
size = max(size, n.Layer)
}
g.Layers = m
fillLayers(g)
}
size += 1 // the highest layer index must fit in the slice too

ls := make([]*graph.Layer, size)

func fillLayers(g *graph.DGraph) {
highest := 0
for i := range g.Layers {
highest = max(highest, i)
for _, n := range g.Nodes {
l := ls[n.Layer]
if l == nil {
l = &graph.Layer{Index: n.Layer}
}
l.Nodes = append(l.Nodes, n)
ls[n.Layer] = l
}
for i := 0; i < highest; i++ {
_, ok := g.Layers[i]
if !ok {
g.Layers = ls

// fill layers
for i := range size {
l := g.Layers[i]
if l == nil {
g.Layers[i] = &graph.Layer{Index: i}
}
}
Expand Down
5 changes: 2 additions & 3 deletions internal/phase2/network_simplex.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package phase2

import (
"math"
"slices"

"github.com/nulab/autog/internal/graph"
)
Expand All @@ -11,8 +12,6 @@ type networkSimplexProcessor struct {
low graph.NodeIntMap // Gansner et al.: lowest postorder traversal number among nodes reachable from the input node
}

// todo: exec alg on single connected components?

// this implements a graph node layering algorithm, based on:
// - "Emden R. Gansner, Eleftherios Koutsofios, Stephen C. North, Kiem-Phong Vo, A technique for
// drawing directed graphs. Software Engineering 19(3), pp. 214-230, 1993."
Expand Down Expand Up @@ -129,7 +128,7 @@ func (p *networkSimplexProcessor) initLayers(g *graph.DGraph) {
}

// sources have layer 0
sources := g.Sources()
sources := slices.Collect(g.Sources())

for len(sources) > 0 {
n := sources[0]
Expand Down
Loading