1
// use std::io::Read;
2
use std::io::Write;
3
use std::path::PathBuf;
4

            
5
use color_eyre::{
6
    eyre::{eyre, WrapErr},
7
    Result,
8
};
9
use ndarray::{Array2, Axis};
10
use petgraph::dot::Dot;
11
use petgraph::{graph::NodeIndex, Direction, Graph};
12
use rayon::prelude::*;
13

            
14
use crate::{args::{GraphArgs, Selection, StandardArgs}, structs::HapVariant};
15
use crate::graphs::DecayGraph;
16
use crate::io::{read_variable_data_file, read_sample_ids};
17
use crate::read_vcf::read_vcf_to_matrix;
18
use crate::structs::{HaploNode, PhasedMatrix};
19
use crate::utils::{
20
    push_to_output, select_carrier_haplotypes, select_only_longest_haplotypes,
21
    shared_lengths_by_majority,
22
};
23
use crate::core::{open_csv_writer, parse_snp_coord};
24

            
25
#[doc(hidden)]
26
24
#[tracing::instrument]
27
12
pub fn run(
28
    args: StandardArgs,
29
    graph_args: GraphArgs,
30
    variable_data: Option<PathBuf>,
31
    variable_names: Option<Vec<String>>,
32
    decoy_samples: Option<PathBuf>,
33
    save_branch_haplotypes: bool,
34
) -> Result<()> {
35
    if args.selection == Selection::Unphased {
36
        return Err(eyre!("Running with unphased data is not supported."))
37
    }
38

            
39
    let (contig, variant_pos) = parse_snp_coord(&args.coords)?;
40

            
41
    let mut vcf = read_vcf_to_matrix(&args, contig, variant_pos, None)?;
42

            
43
    //These are options
44
    let decoy_samples = read_sample_ids(&decoy_samples)?;
45

            
46
    // Set clinical data
47
    if let Some(path) = variable_data {
48
        vcf.set_variable_data(read_variable_data_file(path)?)?;
49
    }
50

            
51
    // CSV header
52
    let mut header = vec![];
53
    if let Some(variables) = variable_names.clone() {
54
        let h: Vec<String> = vec!["side", "pos", "1st_n", "2nd_n"]
55
            .iter()
56
48
            .map(|s| s.to_string())
57
            .collect();
58
        header.extend(h);
59

            
60
        for var in variables {
61
            header.push(format!("{var}_1st"));
62
            header.push(format!("{var}_2nd"));
63
            header.push(format!("{var}_t_test"));
64
        }
65
        tracing::debug!("{header:?}");
66
    }
67

            
68
    if args.selection == Selection::OnlyAlts || args.selection == Selection::OnlyRefs {
69
        vcf = select_carrier_haplotypes(vcf, variant_pos, &args.coords, &args.selection)
70
    }
71

            
72
    if args.selection == Selection::OnlyLongest {
73
        let shared_lengths = shared_lengths_by_majority(&vcf, vcf.variant_idx());
74
        vcf = select_only_longest_haplotypes(&shared_lengths, vcf);
75
    }
76

            
77
    let vec = vec![LocDirection::Left, LocDirection::Right];
78
    vec.par_iter()
79
24
        .map(|direction| -> Result<()> {
80
24
            let g = tree_petgraph(&vcf, direction, &decoy_samples, save_branch_haplotypes)?;
81

            
82
24
            if save_branch_haplotypes {
83
24
                let mut nodes = g.neighbors_directed(NodeIndex::new(0), Direction::Outgoing);
84
24
                if let Some(node) = nodes.next() {
85
24
                    let haplonode = g.node_weight(node).unwrap();
86
24

            
87
24
                    let mut common_haplo_csv = args.output.clone();
88
24
                    push_to_output(
89
24
                        &args,
90
24
                        &mut common_haplo_csv,
91
24
                        &format!("recursive_mbah_{direction}_common_haplotype"),
92
24
                        "txt",
93
24
                    );
94

            
95
24
                    let mut file = std::fs::File::create(&common_haplo_csv)
96
24
                        .wrap_err(eyre!("Path {common_haplo_csv:?}"))?;
97
24
                    file.write_all(haplonode.haplotype().as_bytes())?;
98
                }
99
            }
100

            
101
            // Write to dot
102
24
            let mut decay_dot = args.output.clone();
103
24
            push_to_output(
104
24
                &args,
105
24
                &mut decay_dot,
106
24
                &format!("recursive_mbah_{direction}"),
107
24
                "dot",
108
24
            );
109

            
110
24
            let mut f = std::fs::File::create(decay_dot)?;
111
24
            f.write_all(format!("{}", Dot::new(&g)).as_bytes())?;
112

            
113
            // Write to json
114
24
            let mut decay_json = args.output.clone();
115
24
            push_to_output(
116
24
                &args,
117
24
                &mut decay_json,
118
24
                &format!("recursive_mbah_{direction}"),
119
24
                "json",
120
24
            );
121

            
122
24
            let f = std::fs::File::create(&decay_json).wrap_err(eyre!("Path {decay_json:?}"))?;
123
24
            serde_json::to_writer(f, &g)?;
124

            
125
            // Debug
126
            // let mut file = std::fs::File::open(format!("{out}_{direction}.json"))?;
127
            // let mut data = String::new();
128
            // file.read_to_string(&mut data)?;
129
            // let g: Graph<HaploNode, i64> = serde_json::from_str(&data)?;
130
            // end debug
131

            
132
            // Create decay graph svg
133
24
            let mut dg = DecayGraph::new(&g, &vcf, graph_args.clone());
134

            
135
24
            if let Some(variables) = &variable_names {
136
24
                dg.variables = Some(variables.clone());
137
24
                write_clinical_data(
138
24
                    &args,
139
24
                    &vcf,
140
24
                    &dg,
141
24
                    variables,
142
24
                    direction,
143
24
                    args.output.clone(),
144
24
                    &header,
145
24
                )?;
146
            }
147

            
148
24
            dg.draw_graph()?;
149
24
            let mut decay_graph = args.output.clone();
150
24
            push_to_output(
151
24
                &args,
152
24
                &mut decay_graph,
153
24
                &format!("recursive_mbah_{direction}"),
154
24
                "svg",
155
24
            );
156
24

            
157
24
            svg::save(decay_graph, &dg.document)?;
158
24
            Ok(())
159
24
        })
160
        .collect::<Result<()>>()?;
161
    Ok(())
162
}
163

            
164
24
fn write_clinical_data(
165
24
    args: &StandardArgs,
166
24
    vcf: &PhasedMatrix,
167
24
    dg: &DecayGraph,
168
24
    variables: &[String],
169
24
    direction: &LocDirection,
170
24
    mut output: PathBuf,
171
24
    header: &Vec<String>,
172
24
) -> Result<()> {
173
24
    push_to_output(
174
24
        args,
175
24
        &mut output,
176
24
        &format!("recursive_mbah_{direction}_clinical_data"),
177
24
        "csv",
178
24
    );
179

            
180
24
    let mut writer = open_csv_writer(output)?;
181
24
    writer.write_record(header)?;
182

            
183
24
    let nodes = dg.get_majority_nodes().unwrap();
184
450
    for node in nodes {
185
426
        let children: Vec<NodeIndex> = dg.g.neighbors_directed(node, Direction::Outgoing).collect();
186
426

            
187
426
        if children.len() == 2 {
188
402
            let c1 = dg.g.node_weight(children[0]).unwrap();
189
402
            let c2 = dg.g.node_weight(children[1]).unwrap();
190
402
            if c1.indexes_len > 5 && c2.indexes_len > 5 {
191
6
                let v1 = vcf.get_variable_data_vecs(&c1.indexes, variables)?.unwrap();
192
6
                let v2 = vcf.get_variable_data_vecs(&c2.indexes, variables)?.unwrap();
193
6
                let node1 = vcf.get_variable_data_mean(&c1.indexes, variables)?.unwrap();
194
6
                let node2 = vcf.get_variable_data_mean(&c2.indexes, variables)?.unwrap();
195
6
                let mut csv_row = vec![
196
6
                    direction.to_string(),
197
6
                    c1.pos.to_string(),
198
6
                    c1.indexes_len.to_string(),
199
6
                    c2.indexes_len.to_string(),
200
6
                ];
201
6
                for i in 0..node1.len() {
202
6
                    csv_row.push(format!("{:.2}", node1[i]));
203
6
                    csv_row.push(format!("{:.2}", node2[i]));
204
6
                    let t_test = crate::utils::two_tail_welch_t_test(&v1[i], &v2[i]);
205
6
                    csv_row.push(format!("{t_test:.5}"));
206
6
                }
207
6
                writer.write_record(csv_row)?;
208
396
            }
209
24
        }
210
    }
211
24
    Ok(())
212
24
}
213

            
214
#[doc(hidden)]
215
#[derive(PartialEq, Eq, PartialOrd, Ord)]
216
pub enum LocDirection {
217
    Left,
218
    Right,
219
}
220

            
221
impl std::fmt::Display for LocDirection {
222
126
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
223
126
        match self {
224
66
            LocDirection::Right => write!(f, "right"),
225
60
            LocDirection::Left => write!(f, "left"),
226
        }
227
126
    }
228
}
229

            
230
#[doc(hidden)]
231
48
pub fn tree_petgraph(
232
48
    vcf: &PhasedMatrix,
233
48
    direction: &LocDirection,
234
48
    decoy_samples: &Option<Vec<String>>,
235
48
    save_branch_haplotypes: bool,
236
48
) -> Result<Graph<HaploNode, i64>> {
237
48
    let indexes: Vec<usize> = (0..vcf.nsamples()).collect();
238
48
    let mut g = Graph::<HaploNode, i64>::new();
239
48

            
240
48
    g.add_node(HaploNode {
241
48
        samples: vcf.get_sample_names(&indexes),
242
48
        indexes_len: indexes.len(),
243
48
        pos: 0,
244
48
        haplotype: vcf.find_haplotype_for_sample("", vcf.variant_idx..vcf.variant_idx, 0),
245
48
        decoys: match decoy_samples {
246
            Some(decoys) => find_decoys(vcf, &indexes, decoys),
247
48
            None => None,
248
        },
249
48
        indexes,
250
48
    });
251
48

            
252
48
    match direction {
253
        LocDirection::Right => {
254
576
            for pos in vcf.variant_idx..vcf.matrix.ncols() {
255
576
                let node_idxs = g.node_indices();
256
13284
                for idx in node_idxs {
257
12708
                    let node = g.node_weight(idx).unwrap();
258
12708
                    let count = g.neighbors_directed(idx, Direction::Outgoing).count();
259
12708
                    if count == 0 && node.indexes.len() > 1 {
260
690
                        g = check_if_break(vcf, idx, pos, g, direction, decoy_samples, save_branch_haplotypes)?;
261
12018
                    }
262
                }
263
            }
264
        }
265

            
266
        LocDirection::Left => {
267
930
            for pos in (0..vcf.variant_idx).rev() {
268
930
                let node_idxs = g.node_indices();
269
24000
                for idx in node_idxs {
270
23070
                    let node = g.node_weight(idx).unwrap();
271
23070
                    let count = g.neighbors_directed(idx, Direction::Outgoing).count();
272
23070
                    if count == 0 && node.indexes.len() > 1 {
273
840
                        g = check_if_break(vcf, idx, pos, g, direction, decoy_samples, save_branch_haplotypes)?;
274
22230
                    }
275
                }
276
            }
277
        }
278
    }
279

            
280
48
    Ok(g)
281
48
}
282

            
283
#[doc(hidden)]
284
1530
fn check_if_break(
285
1530
    vcf: &PhasedMatrix,
286
1530
    idx: NodeIndex,
287
1530
    pos: usize,
288
1530
    mut g: Graph<HaploNode, i64>,
289
1530
    direction: &LocDirection,
290
1530
    decoy_samples: &Option<Vec<String>>,
291
1530
    save_branch_haplotypes: bool,
292
1530
) -> Result<Graph<HaploNode, i64>> {
293
1530
    let node = g.node_weight(idx).unwrap();
294
1530
    let submatrix = vcf.matrix.select(Axis(0), &node.indexes);
295
1530
    if let Some((node1, node2)) = find_breaks(submatrix, pos, node, vcf, direction, decoy_samples, save_branch_haplotypes)?
296
1128
    {
297
1128
        let node_idx1 = g.add_node(node1);
298
1128
        let node_idx2 = g.add_node(node2);
299
1128
        let distance = vcf.variant_idx_pos() - vcf.get_pos(pos);
300
1128
        g.add_edge(idx, node_idx1, distance.abs());
301
1128
        g.add_edge(idx, node_idx2, distance.abs());
302
1128
    }
303
1530
    Ok(g)
304
1530
}
305

            
306
#[doc(hidden)]
307
1530
fn find_breaks(
308
1530
    matrix: Array2<u8>,
309
1530
    pos: usize,
310
1530
    node: &HaploNode,
311
1530
    vcf: &PhasedMatrix,
312
1530
    direction: &LocDirection,
313
1530
    decoy_samples: &Option<Vec<String>>,
314
1530
    save_branch_haplotypes: bool,
315
1530
) -> Result<Option<(HaploNode, HaploNode)>> {
316
1530
    let zeroes: Vec<usize> = matrix
317
1530
        .slice(ndarray::s![.., pos])
318
1530
        .iter()
319
1530
        .zip(node.indexes.clone())
320
19764
        .filter_map(|(n, i)| if *n == 0 { Some(i) } else { None })
321
1530
        .collect();
322
1530
    let ones: Vec<usize> = matrix
323
1530
        .slice(ndarray::s![.., pos])
324
1530
        .iter()
325
1530
        .zip(node.indexes.clone())
326
19764
        .filter_map(|(n, i)| if *n == 1 { Some(i) } else { None })
327
1530
        .collect();
328
1530

            
329
1530
    let distance = vcf.variant_idx_pos() - vcf.get_pos(pos);
330
1530

            
331
1530
    if !zeroes.is_empty() && !ones.is_empty() {
332
1128
        let node1 = HaploNode {
333
1128
            samples: vcf.get_sample_names(&zeroes),
334
1128
            pos: distance.unsigned_abs() as usize,
335
1128
            haplotype: match save_branch_haplotypes {
336
1128
                true => find_haplotype_for_haplonode(vcf, zeroes[0], pos, direction),
337
                false => vec![]
338
            },
339
1128
            indexes_len: zeroes.len(),
340
1128
            decoys: match decoy_samples {
341
                Some(decoys) => find_decoys(vcf, &zeroes, decoys),
342
1128
                None => None,
343
            },
344
1128
            indexes: zeroes,
345
        };
346
1128
        let node2 = HaploNode {
347
1128
            samples: vcf.get_sample_names(&ones),
348
1128
            pos: distance.unsigned_abs() as usize,
349
1128
            haplotype: match save_branch_haplotypes {
350
1128
                true => find_haplotype_for_haplonode(vcf, ones[0], pos, direction),
351
                false => vec![]
352
            },
353
1128
            indexes_len: ones.len(),
354
1128
            decoys: match decoy_samples {
355
                Some(decoys) => find_decoys(vcf, &ones, decoys),
356
1128
                None => None,
357
            },
358
1128
            indexes: ones,
359
1128
        };
360
1128
        return Ok(Some((node1, node2)));
361
402
    }
362
402
    Ok(None)
363
1530
}
364

            
365
fn find_haplotype_for_haplonode(
366
    vcf: &PhasedMatrix, idx: usize, pos: usize, direction: &LocDirection
367
) -> Vec<HapVariant> {
368
    vcf.find_haplotype_for_sample(
369
2256
        vcf.get_contig(),
370
2256
        match &direction {
371
804
            LocDirection::Right => vcf.variant_idx..pos,
372
1452
            LocDirection::Left => 1 + pos..vcf.variant_idx,
373
        },
374
2256
        idx)
375
2256
}
376

            
377

            
378
fn find_decoys(
379
    vcf: &PhasedMatrix,
380
    indexes: &[usize],
381
    decoy_samples: &[String],
382
) -> Option<Vec<String>> {
383
    let names = vcf.get_sample_names(indexes);
384
    let count = names.iter().filter(|n| decoy_samples.contains(n)).count();
385
    if count == 0 {
386
        None
387
    } else {
388
        Some(names)
389
    }
390
}