Skip to content

Commit

Permalink
fix: the graph to show mandatory passthrough transformers on a new li…
Browse files Browse the repository at this point in the history
…ne (#1014)

Signed-off-by: Harikrishnan Balagopal <[email protected]>
  • Loading branch information
HarikrishnanBalagopal authored Apr 5, 2023
1 parent 6e92f3a commit 2e2aa30
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 41 deletions.
150 changes: 113 additions & 37 deletions graph/layout.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright IBM Corporation 2022
* Copyright IBM Corporation 2023
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -37,7 +37,7 @@ func GetNodesAndEdges(graph graphtypes.Graph) ([]graphtypes.Node, []graphtypes.E
}
node := graphtypes.Node{
Id: fmt.Sprintf("v-%d", vertex.Id),
Position: graphtypes.Position{X: 0, Y: vertex.Iteration}, // store the iteration in the Y coordinate
Position: graphtypes.Position{X: 0, Y: vertex.Iteration}, // store the iteration in the Y coordinate (sub iterations in X coordinate)
Data: graphtypes.Data{Label: vertex.Name, PathMappings: pathMappings},
}
if vertex.Id == 0 {
Expand All @@ -62,6 +62,31 @@ func GetNodesAndEdges(graph graphtypes.Graph) ([]graphtypes.Node, []graphtypes.E
return nodes, edges
}

type graphIterAndSubIter struct {
Iter int
Sub int
}

type graphSortIters struct {
Items []graphIterAndSubIter
}

// Len is the number of elements in the collection.
func (x *graphSortIters) Len() int {
return len(x.Items)
}

// Less reports whether the element with index i
// must sort before the element with index j.
func (x *graphSortIters) Less(i, j int) bool {
return x.Items[i].Iter < x.Items[j].Iter || (x.Items[i].Iter == x.Items[j].Iter && x.Items[i].Sub < x.Items[j].Sub)
}

// Swap swaps the elements with indexes i and j.
func (x *graphSortIters) Swap(i, j int) {
x.Items[i], x.Items[j] = x.Items[j], x.Items[i]
}

// BfsUpdatePositions updates the positions of the nodes using a layered layout algorithm that utilizes Breadth First Search.
func BfsUpdatePositions(nodes []graphtypes.Node, edges []graphtypes.EdgeT) {
adjMat := map[string]map[string]struct{}{}
Expand All @@ -72,39 +97,56 @@ func BfsUpdatePositions(nodes []graphtypes.Node, edges []graphtypes.EdgeT) {
adjMat[edge.Source][edge.Target] = struct{}{}
}

// the bread first search algorithm
queue := []string{"v-0"}
iterSubIterToY := map[graphIterAndSubIter]int{}
{
// depth first search to calculate sub-iterations
subIters := map[graphIterAndSubIter]struct{}{}
visited := map[string]struct{}{"v-0": {}}
dfsRecursive(nodes, subIters, visited, adjMat, "v-0", -1)
sortIters := graphSortIters{}
for subIter := range subIters {
sortIters.Items = append(sortIters.Items, subIter)
}
sort.Stable(&sortIters)
for i, x := range sortIters.Items {
iterSubIterToY[x] = i * 200
}
}

visited := map[string]struct{}{"v-0": {}}
xsPerIteration := map[int]int{}
for len(queue) > 0 {
// pop a vertex from the queue
current := queue[0]
queue = queue[1:]
// add its neighbours to the queue for processing later
neighbours := adjMat[current]
sortedNotVisitedNeighbours := []string{}
for neighbour := range neighbours {
if _, ok := visited[neighbour]; !ok {
sortedNotVisitedNeighbours = append(sortedNotVisitedNeighbours, neighbour)
visited[neighbour] = struct{}{}
iterSubIterToX := map[graphIterAndSubIter]int{}
{
// the bread first search algorithm
queue := []string{"v-0"}
for len(queue) > 0 {
// pop a vertex from the queue
current := queue[0]
queue = queue[1:]
// add its neighbours to the queue for processing later
neighbours := adjMat[current]
sortedNotVisitedNeighbours := []string{}
for neighbour := range neighbours {
if _, ok := visited[neighbour]; !ok {
sortedNotVisitedNeighbours = append(sortedNotVisitedNeighbours, neighbour)
visited[neighbour] = struct{}{}
}
}
sort.StringSlice(sortedNotVisitedNeighbours).Sort()
queue = append(queue, sortedNotVisitedNeighbours...)
// calculate the new position for the current node
idx := common.FindIndex(nodes, func(n graphtypes.Node) bool { return n.Id == current })
if idx < 0 {
logrus.Errorf("failed to find a node for the vertex with id '%s'", current)
continue
}
node := nodes[idx]
ii := graphIterAndSubIter{Iter: node.Position.Y, Sub: node.Position.X}
newX := iterSubIterToX[ii]
iterSubIterToX[ii] = newX + 200
node.Position.X = newX
node.Position.Y = iterSubIterToY[ii]
nodes[idx] = node
}
sort.StringSlice(sortedNotVisitedNeighbours).Sort()
queue = append(queue, sortedNotVisitedNeighbours...)
// calculate the new position for the current node
idx := common.FindIndex(nodes, func(n graphtypes.Node) bool { return n.Id == current })
if idx < 0 {
logrus.Errorf("failed to find a node for the vertex with id '%s'", current)
continue
}
node := nodes[idx]
iteration := node.Position.Y
newX := xsPerIteration[iteration]
xsPerIteration[iteration] = newX + 200
newY := iteration * 200
node.Position.X = newX
node.Position.Y = newY
nodes[idx] = node
}

// handle islands
Expand All @@ -115,12 +157,46 @@ func BfsUpdatePositions(nodes []graphtypes.Node, edges []graphtypes.EdgeT) {
logrus.Errorf("found an unvisited node: %+v", node)
visited[node.Id] = struct{}{}
// calculate the new position for the current node
iteration := node.Position.Y
newX := xsPerIteration[iteration]
xsPerIteration[iteration] = newX + 200
newY := iteration * 200
ii := graphIterAndSubIter{Iter: node.Position.Y, Sub: node.Position.X}
newX := iterSubIterToX[ii]
iterSubIterToX[ii] = newX + 200
node.Position.X = newX
node.Position.Y = newY
node.Position.Y = iterSubIterToY[ii]
nodes[i] = node
}
}

func dfsRecursive(
nodes []graphtypes.Node,
subIters map[graphIterAndSubIter]struct{},
visited map[string]struct{},
adjMat map[string]map[string]struct{},
current string,
parentIdx int,
) {
visited[current] = struct{}{}
currentIdx := common.FindIndex(nodes, func(n graphtypes.Node) bool { return n.Id == current })
if currentIdx < 0 {
logrus.Errorf("failed to find the vertex with id '%s' in the list of nodes", current)
return
}
// calculate sub-iteration
currentNode := nodes[currentIdx]
if parentIdx >= 0 {
parentNode := nodes[parentIdx]
parentIteration := parentNode.Position.Y
currentIteration := currentNode.Position.Y
if parentIteration == currentIteration {
// store the sub iteration in the X coordinate
currentNode.Position.X = parentNode.Position.X + 1
nodes[currentIdx] = currentNode
}
}
subIters[graphIterAndSubIter{Iter: currentNode.Position.Y, Sub: currentNode.Position.X}] = struct{}{}
neighbours := adjMat[current]
for neighbour := range neighbours {
if _, ok := visited[neighbour]; !ok {
dfsRecursive(nodes, subIters, visited, adjMat, neighbour, currentIdx)
}
}
}
9 changes: 5 additions & 4 deletions types/graph/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@ type Graph struct {

// Vertex is a single transformer.
type Vertex struct {
Id int `json:"id"`
Iteration int `json:"iteration"`
Name string `json:"name"`
Data map[string]interface{} `json:"data,omitempty"`
Id int `json:"id"`
Iteration int `json:"iteration"`
SubIteration int `json:"subiteration"`
Name string `json:"name"`
Data map[string]interface{} `json:"data,omitempty"`
}

// Edge is an artifact.
Expand Down

0 comments on commit 2e2aa30

Please sign in to comment.