Skip to content

Commit

Permalink
[p1-greedy] Add option to control determinism of greedy cycle breaker (
Browse files Browse the repository at this point in the history
…fixes #12)
  • Loading branch information
vibridi committed Sep 27, 2024
1 parent 46dcfad commit d470725
Show file tree
Hide file tree
Showing 6 changed files with 24 additions and 7 deletions.
1 change: 1 addition & 0 deletions autolayout_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ var defaultOptions = options{
p4: positioning.SinkColoring,
p5: routing.Polyline,
params: graph.Params{
GreedyCycleBreakerRandomNodeChoice: false,
NetworkSimplexThoroughness: 28,
NetworkSimplexMaxIterFactor: 0,
NetworkSimplexBalance: graph.OptionNsBalanceV,
Expand Down
6 changes: 6 additions & 0 deletions autolayout_options_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ import (
imonitor "github.com/nulab/autog/internal/monitor"
)

func WithNonDeterministicGreedyCycleBreaker() Option {
return func(o *options) {
o.params.GreedyCycleBreakerRandomNodeChoice = true
}
}

func WithNetworkSimplexThoroughness(thoroughness uint) Option {
return func(o *options) {
o.params.NetworkSimplexThoroughness = thoroughness
Expand Down
2 changes: 2 additions & 0 deletions autolayout_options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ func TestOptions(t *testing.T) {
WithBrandesKoepfLayout(2),
WithNodeFixedSize(100.0, 100.0),
WithNodeSize(map[string]graph.Size{"N1": {W: 20, H: 20}}),
WithNonDeterministicGreedyCycleBreaker(),
)

assert.Equal(t, phase1.DepthFirst, opts.p1)
Expand All @@ -39,6 +40,7 @@ func TestOptions(t *testing.T) {
assert.Nil(t, opts.monitor)
assert.NotNil(t, opts.params.NodeFixedSizeFunc)
assert.NotNil(t, opts.params.NodeSizeFunc)
assert.True(t, opts.params.GreedyCycleBreakerRandomNodeChoice)

assert.Equal(t, CycleBreakingGreedy, phase1.Greedy)
assert.Equal(t, CycleBreakingDepthFirst, phase1.DepthFirst)
Expand Down
6 changes: 6 additions & 0 deletions internal/graph/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ type Params struct {
// Sets a width and height to individual non-virtual nodes
NodeSizeFunc func(n *Node)

// ---- phase1 options ---

// Controls whether the next maximum outflow node in the greedy cycle breaker is picked at random.
// When this option is true, the greedy cycle breaker behaves non-deterministically.
GreedyCycleBreakerRandomNodeChoice bool

// ---- phase2 options ---

// Factor used in to determine the maximum number of iterations.
Expand Down
4 changes: 2 additions & 2 deletions internal/phase1/alg_process.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
)

// Process runs this cycle breaking algorithm on the input graph. The graph must be connected.
func (alg Alg) Process(g *graph.DGraph, _ graph.Params) {
func (alg Alg) Process(g *graph.DGraph, params graph.Params) {
imonitor.PrefixFor(alg)

// self-loop edges are removed from the edge list in a preprocessing step
Expand All @@ -23,7 +23,7 @@ func (alg Alg) Process(g *graph.DGraph, _ graph.Params) {
}
switch alg {
case Greedy:
execGreedy(g)
execGreedy(g, params)
case DepthFirst:
execDepthFirst(g)
default:
Expand Down
12 changes: 7 additions & 5 deletions internal/phase1/greedy.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ type greedyProcessor struct {
//
// The algorithm arranges the nodes of G in an arc diagram, with source nodes to the right and sink nodes to the left.
// Then it reverses edges that point right.
func execGreedy(g *graph.DGraph) {
func execGreedy(g *graph.DGraph, params graph.Params) {
p := greedyProcessor{
rnd: rand.New(rand.NewSource(time.Now().UnixNano())),
arcdiag: graph.NodeIntMap{},
Expand Down Expand Up @@ -97,7 +97,7 @@ func execGreedy(g *graph.DGraph) {
}

// randomly select a node from the ones with maximal outflow and put it left
n := p.pickRandom(maxOutflowNodes)
n := p.pickNode(maxOutflowNodes, params.GreedyCycleBreakerRandomNodeChoice)
p.arcdiag[n] = nextLeft
nextLeft++
p.updateNeighbors(n, &sources, &sinks)
Expand All @@ -123,9 +123,11 @@ func execGreedy(g *graph.DGraph) {
}
}

func (p *greedyProcessor) pickRandom(nodes []*graph.Node) *graph.Node {
return nodes[len(nodes)/2] // todo: deterministic for debugging
// return nodes[p.rnd.Intn(len(nodes))]
func (p *greedyProcessor) pickNode(nodes []*graph.Node, random bool) *graph.Node {
if random {
return nodes[p.rnd.Intn(len(nodes))]
}
return nodes[len(nodes)/2] // arbitrary deterministic choice
}

// Updates indegree and outdegree values of the neighbors of the given node,
Expand Down

0 comments on commit d470725

Please sign in to comment.