source: tags/ms_r16q3/SL/NEIGHBOURJOIN/NJ.cxx

Last change on this file was 15176, checked in by westram, 8 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.6 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 <TreeNode.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    ARB_calloc(net_divergence, 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
210TreeNode *neighbourjoining(const char *const *names, const AP_smatrix& smatrix, TreeRoot *troot) { // @@@ pass ConstStrArray
211    // structure_size >= sizeof(TreeNode);
212    // lower triangular matrix
213    // size: size of matrix
214
215    PH_NEIGHBOURJOINING   nj(smatrix);
216    TreeNode            **nodes = ARB_calloc<TreeNode*>(smatrix.size());
217
218    for (size_t i=0; i<smatrix.size(); i++) {
219        nodes[i] = troot->makeNode();
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        TreeNode *father = troot->makeNode();
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        TreeNode *father    = troot->makeNode();
251
252        father->leftson  = nodes[a];
253        father->rightson = nodes[b];
254
255        father->leftlen  = ll;
256        father->rightlen = rl;
257
258        nodes[a]->father = father;
259        nodes[b]->father = father;
260
261        free(nodes);
262
263        father->get_tree_root()->change_root(NULL, father);
264        return father;
265    }
266}
267
268// --------------------------------------------------------------------------------
269
270#ifdef UNIT_TESTS
271#ifndef TEST_UNIT_H
272#include <test_unit.h>
273#endif
274
275static const AP_FLOAT EPSILON = 0.0001;
276
277static arb_test::match_expectation min_ij_equals(PH_NEIGHBOURJOINING& nj, long expected_i, long expected_j, AP_FLOAT expected_minval) {
278    using namespace   arb_test;
279    expectation_group expected;
280
281    long     i, j;
282    AP_FLOAT minval = nj.get_min_ij(i, j);
283
284    expected.add(that(i).is_equal_to(expected_i));
285    expected.add(that(j).is_equal_to(expected_j));
286    expected.add(that(minval).fulfills(epsilon_similar(EPSILON), expected_minval));
287
288    return all().ofgroup(expected);
289}
290
291#define TEST_EXPECT_MIN_IJ(nj,i,j,minval) TEST_EXPECTATION(min_ij_equals(nj,i,j,minval))
292
293void TEST_neighbourjoining() {
294    const size_t SIZE = 4;
295
296#define TEST_FORWARD_ORDER // @@@ changing the order of nodes here changes the resulting trees
297                           // i do not understand, if that means there is sth wrong or not..
298
299#if defined(TEST_FORWARD_ORDER)
300    const char *names[SIZE] = { "A", "B", "C", "D"};
301    enum { A, B, C, D };
302#else // !defined(TEST_FORWARD_ORDER)
303    const char *names[SIZE] = { "D", "C", "B", "A"};
304    enum { D, C, B, A };
305#endif
306
307    for (int test = 1; test <= 2; ++test) {
308        AP_smatrix sym_matrix(SIZE);
309
310        // Note: values used here are distances
311        for (size_t i = 0; i < SIZE; ++i) sym_matrix.set(i, i, 0.0);
312
313        sym_matrix.set(A, B, 0.0765); sym_matrix.set(A, C, 0.1619); sym_matrix.set(A, D, 0.2266);
314        sym_matrix.set(B, C, 0.1278); sym_matrix.set(B, D, 0.2061);
315
316        switch (test) {
317            case 1: sym_matrix.set(C, D, 0.1646); break;
318            case 2: sym_matrix.set(C, D, 0.30); break;
319            default: arb_assert(0); break;
320        }
321
322        // check net_divergence values:
323        {
324            PH_NEIGHBOURJOINING nj(sym_matrix);
325
326            TEST_EXPECT_SIMILAR(nj.get_net_divergence(A), 0.4650, EPSILON);
327            TEST_EXPECT_SIMILAR(nj.get_net_divergence(B), 0.4104, EPSILON);
328            switch (test) {
329                case 1:
330                    TEST_EXPECT_SIMILAR(nj.get_net_divergence(C), 0.4543, EPSILON);
331                    TEST_EXPECT_SIMILAR(nj.get_net_divergence(D), 0.5973, EPSILON);
332
333#define EXPECTED_MIN_IJ -0.361200
334#if defined(TEST_FORWARD_ORDER)
335                    TEST_EXPECT_MIN_IJ(nj, A, B, EXPECTED_MIN_IJ);
336#else // !defined(TEST_FORWARD_ORDER)
337                    TEST_EXPECT_MIN_IJ(nj, D, C, EXPECTED_MIN_IJ);
338#endif
339#undef EXPECTED_MIN_IJ
340                    break;
341                case 2:
342                    TEST_EXPECT_SIMILAR(nj.get_net_divergence(C), 0.5897, EPSILON);
343                    TEST_EXPECT_SIMILAR(nj.get_net_divergence(D), 0.7327, EPSILON);
344
345#define EXPECTED_MIN_IJ -0.372250
346#if defined(TEST_FORWARD_ORDER)
347#if defined(ARB_64)
348                    TEST_EXPECT_MIN_IJ(nj, B, C, EXPECTED_MIN_IJ);
349#else // !defined(ARB_64)
350                    TEST_EXPECT_MIN_IJ(nj, A, D, EXPECTED_MIN_IJ); // @@@ similar to 64-bit w/o TEST_FORWARD_ORDER
351#endif
352#else // !defined(TEST_FORWARD_ORDER)
353                    TEST_EXPECT_MIN_IJ(nj, D, A, EXPECTED_MIN_IJ); // @@@ no differences between 32-/64-bit version w/o TEST_FORWARD_ORDER
354#endif
355#undef EXPECTED_MIN_IJ
356                    break;
357                default: arb_assert(0); break;
358            }
359
360        }
361
362        TreeNode *tree = neighbourjoining(names, sym_matrix, new SimpleRoot);
363
364        switch (test) {
365#if defined(TEST_FORWARD_ORDER)
366#if defined(ARB_64)
367            case 1: TEST_EXPECT_NEWICK(nSIMPLE, tree, "(((A,B),C),D);"); break;
368            case 2: TEST_EXPECT_NEWICK(nSIMPLE, tree, "((A,(B,C)),D);"); break;
369#else // !defined(ARB_64)
370                // @@@ 32bit version behaves different
371            case 1: TEST_EXPECT_NEWICK(nSIMPLE, tree, "(((A,B),D),C);"); break;
372            case 2: TEST_EXPECT_NEWICK(nSIMPLE, tree, "(((A,D),B),C);"); break; // similar to 64-bit w/o TEST_FORWARD_ORDER
373#endif
374#else // !defined(TEST_FORWARD_ORDER)
375                // @@@ no differences between 32-/64-bit version w/o TEST_FORWARD_ORDER
376            case 1: TEST_EXPECT_NEWICK(nSIMPLE, tree, "(((D,C),A),B);"); break;
377            case 2: TEST_EXPECT_NEWICK(nSIMPLE, tree, "(((D,A),B),C);"); break;
378#endif
379            default: arb_assert(0); break;
380        }
381
382        destroy(tree);
383    }
384}
385TEST_PUBLISH(TEST_neighbourjoining);
386
387#endif // UNIT_TESTS
388
389// --------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.