func TestTakeMin(t *testing.T) { var set intsets.Sparse set.Insert(456) set.Insert(123) set.Insert(789) set.Insert(-123) var got int for i, want := range []int{-123, 123, 456, 789} { if !set.TakeMin(&got) || got != want { t.Errorf("TakeMin #%d: got %d, want %d", i, got, want) } } if set.TakeMin(&got) { t.Errorf("%s.TakeMin returned true", &set) } if err := set.Check(); err != nil { t.Fatalf("check: %s: %#v", err, &set) } }
func computeChainFrom(from, to canvas.Node) (chain []canvas.NodeIfc) { // For each link, there is a chain of modules to be invoked: the // ingress policy modules, the egress policy modules, and the final // forwarding nexthop. // // To compute the chain in each direction, the following algorithm is // followed: // Let T and F represent the set of groups for the 'to' and 'from' // nodes, respectively. // The leaving set L is the set difference between F and T. // L := F - T // The entering set E is the set difference between T and F // E := T - F // // For the directed edge from:to, the chain is built as follows: // For each module e in E, invoke the ingress policy (e.ifc[1]) // For each module l in L, invoke the egress policy (l.ifc[2]) // // The directed edge to:from is calculated by calling this function // with to/from reversed. var e, l, x intsets.Sparse l.Difference(from.Groups(), to.Groups()) e.Difference(to.Groups(), from.Groups()) var id int x.Copy(&e) for x.TakeMin(&id) { chain = append(chain, canvas.NodeIfc{id, 1}) } x.Copy(&l) for x.TakeMin(&id) { chain = append(chain, canvas.NodeIfc{id, 2}) } return chain }
// deltaQ returns the highest gain in modularity attainable by moving // n from its current community to another connected community and // the index of the chosen destination. The index into the localMover's // communities field is returned in src if n is in communities. func (l *localMover) deltaQ(n graph.Node) (deltaQ float64, dst int, src commIdx) { id := n.ID() a_aa := l.weight(n, n) k_a := l.edgeWeightOf[id] m2 := l.m2 gamma := l.resolution // Find communites connected to n. var connected intsets.Sparse // The following for loop is equivalent to: // // for _, v := range l.g.From(n) { // connected.Insert(l.memberships[v.ID()]) // } // // This is done to avoid an allocation. for _, vid := range l.g.edges[id] { connected.Insert(l.memberships[vid]) } // Insert the node's own community. connected.Insert(l.memberships[id]) // Calculate the highest modularity gain // from moving into another community and // keep the index of that community. var dQremove float64 dQadd, dst, src := math.Inf(-1), -1, commIdx{-1, -1} var i int for connected.TakeMin(&i) { c := l.communities[i] var k_aC, sigma_totC float64 // C is a substitution for ^𝛼 or ^𝛽. var removal bool for j, u := range c { uid := u.ID() if uid == id { if src.community != -1 { panic("community: multiple sources") } src = commIdx{i, j} removal = true } k_aC += l.weight(n, u) // sigma_totC could be kept for each community // and updated for moves, changing the calculation // of sigma_totC here from O(n_c) to O(1), but // in practice the time savings do not appear // to be compelling and do not make up for the // increase in code complexity and space required. sigma_totC += l.edgeWeightOf[uid] } // See louvain.tex for a derivation of these equations. switch { case removal: // The community c was the current community, // so calculate the change due to removal. dQremove = 2*(k_aC /*^𝛼*/ -a_aa) - 2*gamma*k_a*(sigma_totC /*^𝛼*/ -k_a)/m2 default: // Otherwise calculate the change due to an addition // to c and retain if it is the current best. dQ := 2*k_aC /*^𝛽*/ - 2*gamma*k_a*sigma_totC /*^𝛽*/ /m2 if dQ > dQadd { dQadd = dQ dst = i } } } return (dQadd - dQremove) / m2, dst, src }