// NeighborJoinD is the same as NeighborJoin but is destructive on the receiver. // // It saves a little memory if you have no further use for the distance matrix. func (dm DistanceMatrix) NeighborJoinD() (u graph.LabeledUndirected, wt []float64) { td := make([]float64, len(dm)) // total-distance vector nx := make([]graph.NI, len(dm)) // node number corresponding to dist matrix index for i := range dm { nx[i] = graph.NI(i) } // closest clusters (min value in dm) // return smaller index (j) first closest := func() (jMin, iMin int) { min := math.Inf(1) iMin = -1 jMin = -1 for i := 1; i < len(dm); i++ { for j := 0; j < i; j++ { d := float64(len(dm)-2)*dm[i][j] - td[i] - td[j] if d < min { min = d iMin = i jMin = j } } } return } // wt is edge weight from parent (limb length) var tree graph.LabeledAdjacencyList var nj func(graph.NI) nj = func(m graph.NI) { // m is next internal node number if len(dm) == 2 { wt = make([]float64, 1, m-1) wt[0] = dm[0][1] tree = make(graph.LabeledAdjacencyList, m) n0 := nx[0] n1 := nx[1] tree[n0] = []graph.Half{{To: n1}} tree[n1] = []graph.Half{{To: n0}} return } // compute or recompute TotalDistance for k, dk := range dm { t := 0. for _, d := range dk { t += d } td[k] = t } d1, d2 := closest() Δ := (td[d2] - td[d1]) / float64(len(dm)-2) d21 := dm[d2][d1] ll2 := .5 * (d21 + Δ) ll1 := .5 * (d21 - Δ) n1 := nx[d1] n2 := nx[d2] di1 := dm[d1] // rows in distance matrix di2 := dm[d2] // replace d1 with mean distance for j, dij := range di1 { mn := .5 * (dij + di2[j] - d21) if j == d1 && mn != 0 { panic("uh uh, prolly skip this one...") } di1[j] = mn dm[j][d1] = mn } // d1 has been replaced, delete d2 copy(dm[d2:], dm[d2+1:]) dm = dm[:len(dm)-1] for i, di := range dm { copy(di[d2:], di[d2+1:]) dm[i] = di[:len(di)-1] } nx[d1] = m copy(nx[d2:], nx[d2+1:]) nx = nx[:len(dm)] // recurse nj(m + 1) // join limbs to tree wx1 := graph.LI(len(wt)) wx2 := wx1 + 1 wt = append(wt, ll1, ll2) tree[m] = append(tree[m], graph.Half{n1, wx1}, graph.Half{n2, wx2}) tree[n1] = append(tree[n1], graph.Half{m, wx1}) tree[n2] = append(tree[n2], graph.Half{m, wx2}) return } nj(graph.NI(len(dm))) return graph.LabeledUndirected{tree}, wt }
// AdditiveTree constructs an unrooted tree from an additive distance matrix. // // DistanceMatrix d must be additive. Use provably additive matrices or // use DistanceMatrix.Additive() to verify the additive property. // // Result is an unrooted tree, not necessarily binary, as an undirected graph. // The first len(d) nodes are the leaves represented by the distance matrix. // Internal nodes follow. // // Time complexity is O(n^2) in the number of leaves. func (d DistanceMatrix) AdditiveTree() (u graph.LabeledUndirected, edgeWts []float64) { // interpretation of the presented recursive algorithm. ideas of // things to try: 1: construct result as a parent list rather than // a child tree. 2: drop the recursion. 3. make tree always binary. t := make(graph.LabeledAdjacencyList, len(d), len(d)+len(d)-2) var ap func(int) ap = func(n int) { if n == 1 { edgeWts = []float64{d[0][1]} t[0] = []graph.Half{{1, 0}} t[1] = []graph.Half{{0, 0}} return } nLen, i, k := d.limbWeightSubMatrix(n) x := d[i][n] - nLen ap(n - 1) // f() finds and returns connection node v. // method: df search to find i from k, find connection point on the // way out. // create connection node v if needed, return v if found, -1 if not. var vis graph.Bits var f func(n graph.NI) graph.NI f = func(n graph.NI) graph.NI { if int(n) == i { return n } vis.SetBit(n, 1) for tx, to := range t[n] { if vis.Bit(to.To) == 1 { continue } p := f(to.To) switch { case p < 0: // not found yet continue case x == 0: // p is connection node return p case x < edgeWts[to.Label]: // new node at dist x from to.To // plan is to recycle the existing half edges between // n and to.To to go to new node v. The edge(n, v) // gets to keep the recycled edge label with weight // reduced by x. The edge(to.To, v) gets a new edge label // with weight x. v := graph.NI(len(t)) // new node t[n][tx].To = v // redirect half edgeWts[to.Label] -= x // reduce wt y := graph.LI(len(edgeWts)) // new label for edge(to.To, v) edgeWts = append(edgeWts, x) // now find reciprocal half from to.To back to n for fx, from := range t[to.To] { if from.To == n { // here it is // recycle it to go to v now. t[to.To][fx] = graph.Half{v, y} break } } t = append(t, []graph.Half{{n, to.Label}, {to.To, y}}) x = 0 return v default: // continue back out x -= edgeWts[to.Label] return n } } return -1 } vis.Clear() v := f(graph.NI(k)) y := graph.LI(len(edgeWts)) edgeWts = append(edgeWts, nLen) t[n] = []graph.Half{{v, y}} t[v] = append(t[v], graph.Half{graph.NI(n), y}) } ap(len(d) - 1) return graph.LabeledUndirected{t}, edgeWts }