source: tags/arb-6.0/SL/NEIGHBOURJOIN/NJ.cxx

Last change on this file was 11824, checked in by westram, 11 years ago
  • document differences between 32- and 64bit versions of ARB NJ (neighbourjoin)
    • difference is somehow related to effect of toggling TEST_FORWARD_ORDER
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.5 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : NJ.cxx                                            //
4//   Purpose   :                                                   //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include "NJ.hxx"
12#include <neighbourjoin.hxx>
13#include <arbdbt.h>
14#include <arb_diff.h>
15
16PH_NEIGHBOUR_DIST::PH_NEIGHBOUR_DIST()
17{
18    memset((char *)this, 0, sizeof(PH_NEIGHBOUR_DIST));
19}
20
21
22void PH_NEIGHBOURJOINING::remove_taxa_from_dist_list(long i) { // O(n/2)
23    long a, j;
24    PH_NEIGHBOUR_DIST *nd;
25    for (a=0; a<swap_size; a++) {
26        j = swap_tab[a];
27        if (i==j) continue;
28        if (j<i) {
29            nd = &(dist_matrix[i][j]);
30        }
31        else {
32            nd = &(dist_matrix[j][i]);
33        }
34        nd->remove();
35        net_divergence[j] -= nd->val;   // corr net divergence
36    }
37}
38void PH_NEIGHBOURJOINING::add_taxa_to_dist_list(long i) // O(n/2)
39{
40    long a;
41    long pos, j;
42    PH_NEIGHBOUR_DIST *nd;
43    AP_FLOAT my_nd = 0.0;
44    for (a=0; a<swap_size; a++) {
45        j = swap_tab[a];
46        if (i==j) continue;
47        if (j<i) {
48            nd = &(dist_matrix[i][j]);
49        }
50        else {
51            nd = &(dist_matrix[j][i]);
52        }
53        ph_assert(!nd->previous);
54        pos = (int)(nd->val*dist_list_corr);
55        if (pos >= dist_list_size) {
56            pos = dist_list_size-1;
57        } else if (pos<0)
58            pos = 0;
59        nd->add(&(dist_list[pos]));
60
61        net_divergence[j] += nd->val;   // corr net divergence
62        my_nd += nd->val;
63    }
64    net_divergence[i] = my_nd;
65}
66
67AP_FLOAT PH_NEIGHBOURJOINING::get_max_net_divergence()      // O(n/2)
68{
69    long a, i;
70    AP_FLOAT max = 0.0;
71    for (a=0; a<swap_size; a++) {
72        i = swap_tab[a];
73        if (net_divergence[i] > max) max = net_divergence[i];
74    }
75    return max;
76}
77
78void PH_NEIGHBOURJOINING::remove_taxa_from_swap_tab(long i) // O(n/2)
79{
80    long a;
81    long *source, *dest;
82    source = dest = swap_tab;
83    for (a=0; a<swap_size; a++) {
84        if (swap_tab[a] == i) {
85            source++;
86        }
87        else {
88            *(dest++) = *(source++);
89        }
90    }
91    swap_size --;
92}
93
94PH_NEIGHBOURJOINING::PH_NEIGHBOURJOINING(const AP_smatrix& smatrix) {
95    size = smatrix.size();
96
97    // init swap tab
98    swap_size = size;
99    swap_tab  = new long[size];
100    for (long i=0; i<swap_size; i++) swap_tab[i] = i;
101
102    net_divergence = (AP_FLOAT *)calloc(sizeof(AP_FLOAT), (size_t)size);
103
104    dist_list_size = size;                                  // hope to be the best
105    dist_list      = new PH_NEIGHBOUR_DIST[dist_list_size]; // the roots, no elems
106    dist_list_corr = (dist_list_size-2.0)/smatrix.get_max_value();
107
108    dist_matrix = new PH_NEIGHBOUR_DIST*[size];
109    for (long i=0; i<size; i++) {
110        dist_matrix[i] = new PH_NEIGHBOUR_DIST[i];
111        for (long j=0; j<i; j++) {
112            dist_matrix[i][j].val = smatrix.fast_get(i, j);
113            dist_matrix[i][j].i = i;
114            dist_matrix[i][j].j = j;
115        }
116    }
117    for (long i=0; i<size; i++) {
118        swap_size = i;              // to calculate the correct net divergence..
119        add_taxa_to_dist_list(i);   // ..add to dist list and add n.d.
120    }
121    swap_size = size;
122}
123
124PH_NEIGHBOURJOINING::~PH_NEIGHBOURJOINING() {
125    for (long i=0; i<size; i++) delete [] dist_matrix[i];
126    delete [] dist_matrix;
127    delete [] dist_list;
128    free(net_divergence);
129    delete [] swap_tab;
130}
131
132AP_FLOAT PH_NEIGHBOURJOINING::get_min_ij(long& mini, long& minj) { // O(n*n/speedup)
133    // returns minval (only used by test inspection)
134
135    AP_FLOAT maxri = get_max_net_divergence(); // O(n/2)
136    PH_NEIGHBOUR_DIST *dl;
137    long stat  = 0;
138    AP_FLOAT x;
139    AP_FLOAT minval;
140    minval = 100000.0;
141    AP_FLOAT N_1   = 1.0/(swap_size-2.0);
142    maxri = maxri*2.0*N_1;
143    long pos;
144
145    get_last_ij(mini, minj);
146
147    for (pos=0; pos<dist_list_size; pos++) {
148        if (minval < pos/dist_list_corr - maxri) break;
149        // no way to get a better minimum
150        dl = dist_list[pos].next;   // first entry does not contain information
151        for (; dl; dl=dl->next) {
152            x = (net_divergence[dl->i] + net_divergence[dl->j])*N_1;
153            if (dl->val-x<minval) {
154                minval = dl->val -x;
155                minj = dl->i;
156                mini = dl->j;
157            }
158            stat++;
159        }
160    }
161    return minval;
162}
163
164void PH_NEIGHBOURJOINING::join_nodes(long i, long j, AP_FLOAT &leftl, AP_FLOAT& rightl)
165{
166    PH_NEIGHBOUR_DIST **d = dist_matrix;
167    AP_FLOAT dji;
168
169    AP_FLOAT dist = get_dist(i, j);
170
171    leftl = dist*.5 + (net_divergence[i] - net_divergence[j])*.5/
172        (swap_size - 2.0);
173    rightl = dist - leftl;
174
175    remove_taxa_from_dist_list(j);
176    remove_taxa_from_swap_tab(j);
177    remove_taxa_from_dist_list(i);
178
179    long a, k;
180    dji = d[j][i].val;
181    for (a=0; a<swap_size; a++) {
182        k = swap_tab[a];
183        if (k==i) continue; // k == j not possible
184        if (k>i) {
185            if (k>j) {
186                d[k][i].val = .5*(d[k][i].val + d[k][j].val - dji);
187            }
188            else {
189                d[k][i].val = .5*(d[k][i].val + d[j][k].val - dji);
190            }
191        }
192        else {
193            d[i][k].val = 0.5 * (d[i][k].val + d[j][k].val - dji);
194        }
195    }
196    add_taxa_to_dist_list(i);
197}
198
199void PH_NEIGHBOURJOINING::get_last_ij(long& i, long& j)
200{
201    i = swap_tab[0];
202    j = swap_tab[1];
203}
204
205AP_FLOAT PH_NEIGHBOURJOINING::get_dist(long i, long j)
206{
207    return dist_matrix[j][i].val;
208}
209
210GBT_TREE *neighbourjoining(const char *const *names, const AP_smatrix& smatrix) { // @@@ pass ConstStrArray
211    // structure_size >= sizeof(GBT_TREE);
212    // lower triangular matrix
213    // size: size of matrix
214
215    PH_NEIGHBOURJOINING   nj(smatrix);
216    GBT_TREE            **nodes = (GBT_TREE **)calloc(sizeof(GBT_TREE *), smatrix.size());
217
218    for (size_t i=0; i<smatrix.size(); i++) {
219        nodes[i] = new GBT_TREE;
220        nodes[i]->name = strdup(names[i]);
221        nodes[i]->is_leaf = true;
222    }
223
224    for (size_t i=0; i<smatrix.size()-2; i++) {
225        long a, b;
226        nj.get_min_ij(a, b);
227
228        AP_FLOAT ll, rl;
229        nj.join_nodes(a, b, ll, rl);
230
231        GBT_TREE *father = new GBT_TREE;
232        father->leftson  = nodes[a];
233        father->rightson = nodes[b];
234        father->leftlen  = ll;
235        father->rightlen = rl;
236        nodes[a]->father = father;
237        nodes[b]->father = father;
238        nodes[a]         = father;
239    }
240
241    {
242        long a, b;
243        nj.get_last_ij(a, b);
244
245        AP_FLOAT dist = nj.get_dist(a, b);
246
247        AP_FLOAT ll = dist*0.5;
248        AP_FLOAT rl = dist*0.5;
249
250        GBT_TREE *father = new GBT_TREE;
251        father->leftson = nodes[a];
252        father->rightson = nodes[b];
253        father->leftlen = ll;
254        father->rightlen = rl;
255        nodes[a]->father = father;
256        nodes[b]->father = father;
257
258        free(nodes);
259        return father;
260    }
261}
262
263// --------------------------------------------------------------------------------
264
265#ifdef UNIT_TESTS
266#ifndef TEST_UNIT_H
267#include <test_unit.h>
268#endif
269
270static const AP_FLOAT EPSILON = 0.0001;
271
272static arb_test::match_expectation min_ij_equals(PH_NEIGHBOURJOINING& nj, long expected_i, long expected_j, AP_FLOAT expected_minval) {
273    using namespace   arb_test;
274    expectation_group expected;
275
276    long     i, j;
277    AP_FLOAT minval = nj.get_min_ij(i, j);
278
279    expected.add(that(i).is_equal_to(expected_i));
280    expected.add(that(j).is_equal_to(expected_j));
281    expected.add(that(minval).fulfills(epsilon_similar(EPSILON), expected_minval));
282
283    return all().ofgroup(expected);
284}
285
286#define TEST_EXPECT_MIN_IJ(nj,i,j,minval) TEST_EXPECTATION(min_ij_equals(nj,i,j,minval))
287
288void TEST_neighbourjoining() {
289    const size_t SIZE = 4;
290
291#define TEST_FORWARD_ORDER // @@@ changing the order of nodes here changes the resulting trees
292                           // i do not understand, if that means there is sth wrong or not..
293
294#if defined(TEST_FORWARD_ORDER)
295    const char *names[SIZE] = { "A", "B", "C", "D"};
296    enum { A, B, C, D };
297#else // !defined(TEST_FORWARD_ORDER)
298    const char *names[SIZE] = { "D", "C", "B", "A"};
299    enum { D, C, B, A };
300#endif
301
302    for (int test = 1; test <= 2; ++test) {
303        AP_smatrix sym_matrix(SIZE);
304
305        // Note: values used here are distances
306        for (size_t i = 0; i < SIZE; ++i) sym_matrix.set(i, i, 0.0);
307
308        sym_matrix.set(A, B, 0.0765); sym_matrix.set(A, C, 0.1619); sym_matrix.set(A, D, 0.2266);
309        sym_matrix.set(B, C, 0.1278); sym_matrix.set(B, D, 0.2061);
310
311        switch (test) {
312            case 1: sym_matrix.set(C, D, 0.1646); break;
313            case 2: sym_matrix.set(C, D, 0.30); break;
314            default: arb_assert(0); break;
315        }
316
317        // check net_divergence values:
318        {
319            PH_NEIGHBOURJOINING nj(sym_matrix);
320
321            TEST_EXPECT_SIMILAR(nj.get_net_divergence(A), 0.4650, EPSILON);
322            TEST_EXPECT_SIMILAR(nj.get_net_divergence(B), 0.4104, EPSILON);
323            switch (test) {
324                case 1:
325                    TEST_EXPECT_SIMILAR(nj.get_net_divergence(C), 0.4543, EPSILON);
326                    TEST_EXPECT_SIMILAR(nj.get_net_divergence(D), 0.5973, EPSILON);
327
328#define EXPECTED_MIN_IJ -0.361200
329#if defined(TEST_FORWARD_ORDER)
330                    TEST_EXPECT_MIN_IJ(nj, A, B, EXPECTED_MIN_IJ);
331#else // !defined(TEST_FORWARD_ORDER)
332                    TEST_EXPECT_MIN_IJ(nj, D, C, EXPECTED_MIN_IJ);
333#endif
334#undef EXPECTED_MIN_IJ
335                    break;
336                case 2:
337                    TEST_EXPECT_SIMILAR(nj.get_net_divergence(C), 0.5897, EPSILON);
338                    TEST_EXPECT_SIMILAR(nj.get_net_divergence(D), 0.7327, EPSILON);
339
340#define EXPECTED_MIN_IJ -0.372250
341#if defined(TEST_FORWARD_ORDER)
342#if defined(ARB_64)
343                    TEST_EXPECT_MIN_IJ(nj, B, C, EXPECTED_MIN_IJ);
344#else // !defined(ARB_64)
345                    TEST_EXPECT_MIN_IJ(nj, A, D, EXPECTED_MIN_IJ); // @@@ similar to 64-bit w/o TEST_FORWARD_ORDER
346#endif
347#else // !defined(TEST_FORWARD_ORDER)
348                    TEST_EXPECT_MIN_IJ(nj, D, A, EXPECTED_MIN_IJ); // @@@ no differences between 32-/64-bit version w/o TEST_FORWARD_ORDER
349#endif
350#undef EXPECTED_MIN_IJ
351                    break;
352                default: arb_assert(0); break;
353            }
354
355        }
356
357        GBT_TREE *tree = neighbourjoining(names, sym_matrix);
358
359        switch (test) {
360#if defined(TEST_FORWARD_ORDER)
361#if defined(ARB_64)
362            case 1: TEST_EXPECT_NEWICK(nSIMPLE, tree, "(((A,B),C),D);"); break;
363            case 2: TEST_EXPECT_NEWICK(nSIMPLE, tree, "((A,(B,C)),D);"); break;
364#else // !defined(ARB_64)
365                // @@@ 32bit version behaves different
366            case 1: TEST_EXPECT_NEWICK(nSIMPLE, tree, "(((A,B),D),C);"); break;
367            case 2: TEST_EXPECT_NEWICK(nSIMPLE, tree, "(((A,D),B),C);"); break; // similar to 64-bit w/o TEST_FORWARD_ORDER
368#endif
369#else // !defined(TEST_FORWARD_ORDER)
370                // @@@ no differences between 32-/64-bit version w/o TEST_FORWARD_ORDER
371            case 1: TEST_EXPECT_NEWICK(nSIMPLE, tree, "(((D,C),A),B);"); break;
372            case 2: TEST_EXPECT_NEWICK(nSIMPLE, tree, "(((D,A),B),C);"); break;
373#endif
374            default: arb_assert(0); break;
375        }
376
377        delete tree;
378    }
379}
380
381#endif // UNIT_TESTS
382
383// --------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.