github.com/vertgenlab/gonomics@v1.0.0/genomeGraph/sort.go (about)

     1  package genomeGraph
     2  
     3  // SortGraph will reorder nodes in a graph such that the order and Ids of the output graph are topologically sorted.
     4  func SortGraph(g *GenomeGraph) *GenomeGraph {
     5  	answer := &GenomeGraph{}
     6  	answer.Nodes = make([]Node, len(g.Nodes))
     7  	order := GetSortOrder(g)
     8  	for sortedIdx, originalIdx := range order {
     9  		answer.Nodes[sortedIdx] = g.Nodes[originalIdx]
    10  		answer.Nodes[sortedIdx].Id = uint32(sortedIdx)
    11  	}
    12  	return answer
    13  }
    14  
    15  // GetSortOrder will perform a breadth first search (BFS) on a graph and return an output slice where output[sortedIdx] = originalIdx.
    16  func GetSortOrder(g *GenomeGraph) []uint32 {
    17  	return breadthFirstSearch(g.Nodes)
    18  }
    19  
    20  // TODO: design function to get start positions only
    21  // breadthFirstSearch performs a breadth first search on a graph and returns a slice correlating the sort order to the original order.
    22  func breadthFirstSearch(nodes []Node) []uint32 {
    23  	answer := make([]uint32, 0)
    24  	var inDegree int
    25  	var nodeId uint32
    26  	inDegreeTable := make(map[uint32]int)
    27  
    28  	// Updated nodes is going to keep track of each node
    29  	// which has had a change to it's inDegree
    30  	// We will use this to loop through the graph
    31  	// visiting each group of connected nodes in order
    32  	// and by searching within each group with a
    33  	// breadth-first approach
    34  	updatedNodes := make([]*Node, 0)
    35  
    36  	subGraphs := BreakNonContiguousGraph(nodes)
    37  
    38  	// loop through each contiguous subGraph
    39  	for _, nodeSet := range subGraphs {
    40  		updatedNodes = nil
    41  		inDegreeTable = make(map[uint32]int)
    42  		for i := 0; i < len(nodeSet); i++ {
    43  			inDegreeTable[nodeSet[i].Id] = len(nodeSet[i].Prev)
    44  		}
    45  
    46  		// Find all nodes that start with inDegree zero and add to updatedNodes
    47  		for nodeId, inDegree = range inDegreeTable {
    48  			if inDegree == 0 {
    49  				updatedNodes = append(updatedNodes, &nodes[nodeId])
    50  			}
    51  		}
    52  
    53  		for k := 0; k < len(updatedNodes); k++ {
    54  			answer = append(answer, updatedNodes[k].Id)
    55  			delete(inDegreeTable, updatedNodes[k].Id)
    56  			updateTable(inDegreeTable, updatedNodes[k], &updatedNodes)
    57  		}
    58  	}
    59  	return answer
    60  }
    61  
    62  // updateTable updates the table of node in degrees.
    63  func updateTable(inDegreeTable map[uint32]int, node *Node, updatedNodes *[]*Node) {
    64  	for i := 0; i < len(node.Next); i++ {
    65  		inDegreeTable[node.Next[i].Dest.Id]--
    66  		if inDegreeTable[node.Next[i].Dest.Id] == 0 {
    67  			*updatedNodes = append(*updatedNodes, node.Next[i].Dest)
    68  		}
    69  	}
    70  }
    71  
    72  // TODO: possible to order nodes while breaking discontiguous graphs???
    73  // BreakNonContiguousGraph will return a slice of graphs ([]*Node) such that each graph in the slice is contiguous.
    74  func BreakNonContiguousGraph(g []Node) [][]*Node {
    75  	answer := make([][]*Node, 0)
    76  	var contiguousGraph []*Node
    77  	inDegreeTable := make(map[uint32]int)
    78  	visited := make([]bool, len(g))
    79  	var inDegree int
    80  	var nodeId uint32
    81  
    82  	for i := 0; i < len(g); i++ {
    83  		inDegreeTable[g[i].Id] = len(g[i].Prev)
    84  	}
    85  
    86  	for nodeId, inDegree = range inDegreeTable {
    87  		if inDegree == 0 && !visited[nodeId] {
    88  			contiguousGraph = make([]*Node, 1)
    89  			contiguousGraph[0] = &g[nodeId]
    90  			visited[nodeId] = true
    91  			traceGraph(&g[nodeId], visited, &contiguousGraph)
    92  			answer = append(answer, contiguousGraph)
    93  		}
    94  	}
    95  
    96  	return answer
    97  }
    98  
    99  // traceGraph is a helper function that traverses graph and keeps track of which nodes have been visited.
   100  func traceGraph(startNode *Node, visited []bool, answer *[]*Node) {
   101  	var i int = 0
   102  
   103  	for i = 0; i < len(startNode.Next); i++ {
   104  		if !visited[startNode.Next[i].Dest.Id] {
   105  			*answer = append(*answer, startNode.Next[i].Dest)
   106  			visited[startNode.Next[i].Dest.Id] = true
   107  			traceGraph(startNode.Next[i].Dest, visited, answer)
   108  		}
   109  	}
   110  
   111  	for i = 0; i < len(startNode.Prev); i++ {
   112  		if !visited[startNode.Prev[i].Dest.Id] {
   113  			*answer = append(*answer, startNode.Prev[i].Dest)
   114  			visited[startNode.Prev[i].Dest.Id] = true
   115  			traceGraph(startNode.Prev[i].Dest, visited, answer)
   116  		}
   117  	}
   118  }