@sjcrh/proteinpaint-rust 2.195.0 → 2.196.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.toml +0 -11
- package/package.json +1 -1
- package/src/readH5.rs +0 -781
- package/src/readHDF5.rs +0 -1188
- package/src/validateHDF5.rs +0 -247
package/src/readHDF5.rs
DELETED
|
@@ -1,1188 +0,0 @@
|
|
|
1
|
-
//------------------------------------------------------------------------------
|
|
2
|
-
// readHDF5.rs - HDF5 Gene Expression Data Reader
|
|
3
|
-
//------------------------------------------------------------------------------
|
|
4
|
-
//
|
|
5
|
-
// Extracts gene expression values from HDF5 files in dense or sparse formats.
|
|
6
|
-
// Supports single genes with memory optimization and multiple genes with
|
|
7
|
-
// parallel processing.
|
|
8
|
-
//
|
|
9
|
-
// Features:
|
|
10
|
-
// - Auto format detection (dense/sparse)
|
|
11
|
-
// - Optimized single and multi-gene queries
|
|
12
|
-
// - Parallel processing for multiple genes
|
|
13
|
-
// - JSON output with timing metrics
|
|
14
|
-
//
|
|
15
|
-
// Usage:
|
|
16
|
-
// HDF5_DIR=/usr/local/Homebrew/Cellar/hdf5/1.14.3_1 &&
|
|
17
|
-
// echo $json='{"gene":"TP53","hdf5_file":"matrix.h5"}' | target/release/readHDF5
|
|
18
|
-
//------------------------------------------------------------------------------
|
|
19
|
-
use hdf5::types::{FixedAscii, VarLenAscii};
|
|
20
|
-
use hdf5::{File, Result};
|
|
21
|
-
use ndarray::Dim;
|
|
22
|
-
use ndarray::{s, Array1};
|
|
23
|
-
use rayon::prelude::*;
|
|
24
|
-
use serde_json::{json, Map, Value};
|
|
25
|
-
use std::io;
|
|
26
|
-
use std::sync::Arc;
|
|
27
|
-
use std::time::Instant;
|
|
28
|
-
|
|
29
|
-
/// Determines the format of an HDF5 gene expression file
|
|
30
|
-
///
|
|
31
|
-
/// Examines the structure of an HDF5 file to detect its format:
|
|
32
|
-
/// - "dense": Contains "counts", "gene_names", and "samples" datasets
|
|
33
|
-
/// - "sparse": Contains "data" group and "sample_names" dataset
|
|
34
|
-
/// - "unknown": Does not match either format
|
|
35
|
-
///
|
|
36
|
-
/// # Arguments
|
|
37
|
-
/// * `hdf5_filename` - Path to the HDF5 file to analyze
|
|
38
|
-
///
|
|
39
|
-
/// # Returns
|
|
40
|
-
/// The detected format as a static string: "dense", "sparse", or "unknown"
|
|
41
|
-
fn detect_hdf5_format(hdf5_filename: &str) -> Result<&'static str> {
|
|
42
|
-
let file = File::open(hdf5_filename)?;
|
|
43
|
-
|
|
44
|
-
// Check for dense format (has counts, gene_names, and samples datasets)
|
|
45
|
-
let has_counts = file.dataset("counts").is_ok();
|
|
46
|
-
let has_gene_names = file.dataset("gene_names").is_ok();
|
|
47
|
-
let has_samples = file.dataset("samples").is_ok();
|
|
48
|
-
|
|
49
|
-
// Check for sparse matrix format (has data group and sample_names)
|
|
50
|
-
let has_data_group = file.group("data").is_ok();
|
|
51
|
-
let has_sample_names = file.dataset("sample_names").is_ok();
|
|
52
|
-
|
|
53
|
-
if has_counts && has_gene_names && has_samples {
|
|
54
|
-
// eprintln!("Dense format detected");
|
|
55
|
-
Ok("dense")
|
|
56
|
-
} else if has_data_group && has_sample_names {
|
|
57
|
-
//eprintln!("Sparse format detected");
|
|
58
|
-
Ok("sparse")
|
|
59
|
-
} else {
|
|
60
|
-
eprintln!("Unknown format detected");
|
|
61
|
-
Ok("unknown")
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/// Unified function for querying gene expression data from an HDF5 file
|
|
66
|
-
///
|
|
67
|
-
/// Automatically detects file format (dense or sparse) and routes to the appropriate handler.
|
|
68
|
-
///
|
|
69
|
-
/// # Arguments
|
|
70
|
-
/// * `hdf5_filename` - Path to the HDF5 file containing gene expression data
|
|
71
|
-
/// * `gene_name` - Name of the gene whose expression values to extract
|
|
72
|
-
///
|
|
73
|
-
/// # Returns
|
|
74
|
-
/// Outputs gene expression data in JSON format to stdout
|
|
75
|
-
fn query_gene(hdf5_filename: String, gene_name: String) -> Result<()> {
|
|
76
|
-
// First, detect the file format
|
|
77
|
-
let file_format = detect_hdf5_format(&hdf5_filename)?;
|
|
78
|
-
|
|
79
|
-
// Query gene data based on format
|
|
80
|
-
match file_format {
|
|
81
|
-
"dense" => query_gene_dense(hdf5_filename, gene_name),
|
|
82
|
-
"sparse" => query_gene_sparse(hdf5_filename, gene_name),
|
|
83
|
-
_ => {
|
|
84
|
-
println!(
|
|
85
|
-
"{}",
|
|
86
|
-
serde_json::json!({
|
|
87
|
-
"status": "failure",
|
|
88
|
-
"message": "Cannot query gene in unknown file format. Please use .h5 format in either sparse or dense format.",
|
|
89
|
-
"file_path": hdf5_filename,
|
|
90
|
-
"gene": gene_name,
|
|
91
|
-
"format": "unknown"
|
|
92
|
-
})
|
|
93
|
-
);
|
|
94
|
-
Ok(())
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/// Reads expression data for a specific gene from a dense format HDF5 file
|
|
100
|
-
///
|
|
101
|
-
/// Dense format contains "gene_ids", "samples", and "counts" datasets.
|
|
102
|
-
///
|
|
103
|
-
/// # Arguments
|
|
104
|
-
/// * `hdf5_filename` - Path to the HDF5 file
|
|
105
|
-
/// * `gene_name` - Name of the gene to query
|
|
106
|
-
///
|
|
107
|
-
/// # Returns
|
|
108
|
-
/// Prints gene expression data in JSON format to stdout
|
|
109
|
-
///
|
|
110
|
-
/// # Error Handling
|
|
111
|
-
/// Handles file access issues, missing datasets, and gene not found scenarios
|
|
112
|
-
fn query_gene_dense(hdf5_filename: String, gene_name: String) -> Result<()> {
|
|
113
|
-
let file = match File::open(hdf5_filename) {
|
|
114
|
-
Ok(f) => f,
|
|
115
|
-
Err(err) => {
|
|
116
|
-
println!(
|
|
117
|
-
"{}",
|
|
118
|
-
serde_json::json!({
|
|
119
|
-
"status": "error",
|
|
120
|
-
"message": format!("Failed to open HDF5 file: {}", err)
|
|
121
|
-
})
|
|
122
|
-
);
|
|
123
|
-
return Ok(());
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
let genes_dataset = match file.dataset("gene_ids") {
|
|
128
|
-
Ok(ds) => ds,
|
|
129
|
-
Err(err) => {
|
|
130
|
-
println!(
|
|
131
|
-
"{}",
|
|
132
|
-
serde_json::json!({
|
|
133
|
-
"status": "error",
|
|
134
|
-
"message": format!("Failed to open gene_ids dataset {}", err)
|
|
135
|
-
})
|
|
136
|
-
);
|
|
137
|
-
return Ok(());
|
|
138
|
-
}
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
let genes_varlen = match genes_dataset.read_1d::<VarLenAscii>() {
|
|
142
|
-
Ok(g) => g,
|
|
143
|
-
Err(err) => {
|
|
144
|
-
println!(
|
|
145
|
-
"{}",
|
|
146
|
-
serde_json::json!({
|
|
147
|
-
"status": "error",
|
|
148
|
-
"message": format!("Failed to read gene names as VarLenAscii: {}", err)
|
|
149
|
-
})
|
|
150
|
-
);
|
|
151
|
-
return Ok(());
|
|
152
|
-
}
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
// Convert to Vec<String> for easier handling
|
|
156
|
-
let genes: Vec<String> = genes_varlen.iter().map(|g| g.to_string()).collect();
|
|
157
|
-
|
|
158
|
-
let samples_dataset = match file.dataset("samples") {
|
|
159
|
-
Ok(ds) => ds,
|
|
160
|
-
Err(err) => {
|
|
161
|
-
println!(
|
|
162
|
-
"{}",
|
|
163
|
-
serde_json::json!({
|
|
164
|
-
"status": "error",
|
|
165
|
-
"message": format!("Failed to open samples dataset{}", err)
|
|
166
|
-
})
|
|
167
|
-
);
|
|
168
|
-
return Ok(());
|
|
169
|
-
}
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
let samples_varlen = match samples_dataset.read_1d::<VarLenAscii>() {
|
|
173
|
-
Ok(s) => s,
|
|
174
|
-
Err(err) => {
|
|
175
|
-
println!(
|
|
176
|
-
"{}",
|
|
177
|
-
serde_json::json!({
|
|
178
|
-
"status": "error",
|
|
179
|
-
"message": format!("Failed to read samples as VarLenAscii: {}", err)
|
|
180
|
-
})
|
|
181
|
-
);
|
|
182
|
-
return Ok(());
|
|
183
|
-
}
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
// Convert to Vec<String> for easier handling
|
|
187
|
-
let samples: Vec<String> = samples_varlen.iter().map(|s| s.to_string()).collect();
|
|
188
|
-
|
|
189
|
-
// Find the index of the requested gene
|
|
190
|
-
let gene_index = match genes.iter().position(|x| *x == gene_name) {
|
|
191
|
-
Some(index) => index,
|
|
192
|
-
None => {
|
|
193
|
-
println!(
|
|
194
|
-
"{}",
|
|
195
|
-
serde_json::json!({
|
|
196
|
-
"status": "error",
|
|
197
|
-
"message": format!("Gene '{}' not found in the dataset", gene_name)
|
|
198
|
-
})
|
|
199
|
-
);
|
|
200
|
-
return Ok(());
|
|
201
|
-
}
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
let counts_dataset = match file.dataset("counts") {
|
|
205
|
-
Ok(ds) => ds,
|
|
206
|
-
Err(err) => {
|
|
207
|
-
println!(
|
|
208
|
-
"{}",
|
|
209
|
-
serde_json::json!({
|
|
210
|
-
"status": "error",
|
|
211
|
-
"message": format!("Failed to open counts dataset: {}", err)
|
|
212
|
-
})
|
|
213
|
-
);
|
|
214
|
-
return Ok(());
|
|
215
|
-
}
|
|
216
|
-
};
|
|
217
|
-
|
|
218
|
-
if gene_index >= counts_dataset.shape()[0] {
|
|
219
|
-
println!(
|
|
220
|
-
"{}",
|
|
221
|
-
serde_json::json!({
|
|
222
|
-
"status": "error",
|
|
223
|
-
"message": "Gene index is out of bounds for the dataset"
|
|
224
|
-
})
|
|
225
|
-
);
|
|
226
|
-
return Ok(());
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
let gene_expression: Array1<f64>;
|
|
230
|
-
|
|
231
|
-
// Method 1: Try to read a 1D slice directly (for 2D datasets)
|
|
232
|
-
match counts_dataset.read_slice_1d::<f64, _>(s![gene_index, ..]) {
|
|
233
|
-
Ok(data) => {
|
|
234
|
-
gene_expression = data;
|
|
235
|
-
}
|
|
236
|
-
Err(err1) => {
|
|
237
|
-
// Method 2: Try a different approach
|
|
238
|
-
let dataset_shape = counts_dataset.shape();
|
|
239
|
-
if dataset_shape.len() != 2 {
|
|
240
|
-
println!(
|
|
241
|
-
"{}",
|
|
242
|
-
serde_json::json!({
|
|
243
|
-
"status": "error",
|
|
244
|
-
"message": "Expected a 2D dataset for counts"
|
|
245
|
-
})
|
|
246
|
-
);
|
|
247
|
-
return Ok(());
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// Try reading the entire dataset and then extracting the row
|
|
251
|
-
match counts_dataset.read::<f64, Dim<[usize; 2]>>() {
|
|
252
|
-
Ok(all_data) => {
|
|
253
|
-
// Extract just the row we need
|
|
254
|
-
let row = all_data.slice(s![gene_index, ..]).to_owned();
|
|
255
|
-
gene_expression = row;
|
|
256
|
-
|
|
257
|
-
let mut output_string = String::from("{\"samples\":{");
|
|
258
|
-
|
|
259
|
-
// Create direct key-value pairs where sample names are the keys
|
|
260
|
-
for i in 0..gene_expression.len() {
|
|
261
|
-
// Add each sample name as a key pointing directly to its expression value
|
|
262
|
-
output_string += &format!("\"{}\":{}", samples[i].to_string(), gene_expression[i].to_string());
|
|
263
|
-
|
|
264
|
-
// Add comma if not the last item
|
|
265
|
-
if i < gene_expression.len() - 1 {
|
|
266
|
-
output_string += ",";
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// Close the JSON object
|
|
271
|
-
output_string += "}}";
|
|
272
|
-
}
|
|
273
|
-
Err(err2) => {
|
|
274
|
-
println!(
|
|
275
|
-
"{}",
|
|
276
|
-
serde_json::json!({
|
|
277
|
-
"status": "error",
|
|
278
|
-
"message": format!("Failed to read expression values: {:?}, {:?}", err1, err2)
|
|
279
|
-
})
|
|
280
|
-
);
|
|
281
|
-
return Ok(());
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
let mut samples_map = Map::new();
|
|
287
|
-
for (i, sample) in samples.iter().enumerate() {
|
|
288
|
-
if i < gene_expression.len() {
|
|
289
|
-
let value = if gene_expression[i].is_finite() {
|
|
290
|
-
Value::from(gene_expression[i])
|
|
291
|
-
} else {
|
|
292
|
-
Value::Null
|
|
293
|
-
};
|
|
294
|
-
|
|
295
|
-
samples_map.insert(sample.replace("\\", ""), value);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
let output_json = json!({
|
|
300
|
-
"gene": gene_name,
|
|
301
|
-
"dataId": gene_name,
|
|
302
|
-
"samples": samples_map
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
// Output the JSON directly
|
|
306
|
-
println!("{}", output_json);
|
|
307
|
-
|
|
308
|
-
Ok(())
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
/// Reads expression data for a specific gene from a sparse format HDF5 file
|
|
312
|
-
///
|
|
313
|
-
/// Extracts expression values from sparse matrix HDF5 files using Compressed
|
|
314
|
-
/// Sparse Column (CSC) structure.
|
|
315
|
-
///
|
|
316
|
-
/// # Arguments
|
|
317
|
-
/// * `hdf5_filename` - Path to the HDF5 file
|
|
318
|
-
/// * `gene_name` - Name of the gene to query
|
|
319
|
-
///
|
|
320
|
-
/// # Returns
|
|
321
|
-
/// Prints gene expression data as JSON to stdout with "output_string:" prefix.
|
|
322
|
-
/// Sample names are keys, expression values are values.
|
|
323
|
-
///
|
|
324
|
-
/// The sparse format includes:
|
|
325
|
-
/// - "data/dim" - Matrix dimensions
|
|
326
|
-
/// - "gene_names" - Gene identifiers
|
|
327
|
-
/// - "sample_names" - Sample identifiers
|
|
328
|
-
/// - "data/p", "data/i", "data/x" - CSC matrix components
|
|
329
|
-
fn query_gene_sparse(hdf5_filename: String, gene_name: String) -> Result<()> {
|
|
330
|
-
let file = File::open(&hdf5_filename)?;
|
|
331
|
-
let ds_dim = file.dataset("data/dim")?;
|
|
332
|
-
|
|
333
|
-
// Check the data type and read the dataset accordingly
|
|
334
|
-
let data_dim: Array1<_> = ds_dim.read::<usize, Dim<[usize; 1]>>()?;
|
|
335
|
-
let num_samples = data_dim[0]; // Number of total columns in the dataset
|
|
336
|
-
let num_genes = data_dim[1];
|
|
337
|
-
println!("num_samples:{}", num_samples);
|
|
338
|
-
println!("num_genes:{}", num_genes);
|
|
339
|
-
|
|
340
|
-
let now_genes = Instant::now();
|
|
341
|
-
let ds_genes = file.dataset("gene_names")?;
|
|
342
|
-
let genes = ds_genes.read_1d::<FixedAscii<104>>()?;
|
|
343
|
-
println!("Time for parsing genes:{:?}", now_genes.elapsed());
|
|
344
|
-
|
|
345
|
-
let now_samples = Instant::now();
|
|
346
|
-
let ds_samples = file.dataset("sample_names")?;
|
|
347
|
-
let samples = ds_samples.read_1d::<FixedAscii<104>>()?;
|
|
348
|
-
println!("Time for parsing samples:{:?}", now_samples.elapsed());
|
|
349
|
-
|
|
350
|
-
let gene_index = match genes.iter().position(|&x| x == gene_name) {
|
|
351
|
-
Some(index) => {
|
|
352
|
-
println!(
|
|
353
|
-
"The index of '{}' is {} in 0-based format (add 1 to compare with R output)",
|
|
354
|
-
gene_name, index
|
|
355
|
-
);
|
|
356
|
-
index
|
|
357
|
-
}
|
|
358
|
-
None => {
|
|
359
|
-
println!(
|
|
360
|
-
"{}",
|
|
361
|
-
serde_json::json!({
|
|
362
|
-
"status": "failure",
|
|
363
|
-
"message": format!("Gene '{}' not found in the HDF5 file '{}'", gene_name, &hdf5_filename),
|
|
364
|
-
"file_path": hdf5_filename,
|
|
365
|
-
"gene": gene_name
|
|
366
|
-
})
|
|
367
|
-
);
|
|
368
|
-
return Ok(());
|
|
369
|
-
}
|
|
370
|
-
};
|
|
371
|
-
|
|
372
|
-
// Find the number of columns that are populated for that gene
|
|
373
|
-
let now_p = Instant::now();
|
|
374
|
-
let ds_p = file.dataset("data/p")?;
|
|
375
|
-
let data_partial_p: Array1<usize> = ds_p.read_slice_1d(gene_index..gene_index + 2)?;
|
|
376
|
-
println!("Data_partial_p: {:?}", data_partial_p);
|
|
377
|
-
println!("Time for p dataset:{:?}", now_p.elapsed());
|
|
378
|
-
|
|
379
|
-
let array_start_point = data_partial_p[0];
|
|
380
|
-
let array_stop_point = data_partial_p[1];
|
|
381
|
-
let num_populated_cells = array_stop_point - array_start_point;
|
|
382
|
-
println!("Number of populated cells:{}", num_populated_cells);
|
|
383
|
-
|
|
384
|
-
// Find all columns indices that are populated for the given gene
|
|
385
|
-
let now_i = Instant::now();
|
|
386
|
-
let ds_i = file.dataset("data/i")?;
|
|
387
|
-
let populated_column_ids: Array1<usize> = ds_i.read_slice_1d(array_start_point..array_stop_point)?;
|
|
388
|
-
println!("Time for i dataset:{:?}", now_i.elapsed());
|
|
389
|
-
|
|
390
|
-
// Find all columns values that are populated for the given gene
|
|
391
|
-
let now_x = Instant::now();
|
|
392
|
-
let ds_x = file.dataset("data/x")?;
|
|
393
|
-
let populated_column_values: Array1<f64> = ds_x.read_slice_1d(array_start_point..array_stop_point)?;
|
|
394
|
-
println!("Time for x dataset:{:?}", now_x.elapsed());
|
|
395
|
-
|
|
396
|
-
// Generate the complete array from the sparse array
|
|
397
|
-
let mut gene_array: Array1<f64> = Array1::zeros(num_samples);
|
|
398
|
-
let time_generating_full_array = Instant::now();
|
|
399
|
-
|
|
400
|
-
// Fill in the values at the populated column indices
|
|
401
|
-
for (idx, &col_id) in populated_column_ids.iter().enumerate() {
|
|
402
|
-
gene_array[col_id] = populated_column_values[idx];
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
let mut output_string = "{".to_string();
|
|
406
|
-
for i in 0..gene_array.len() {
|
|
407
|
-
output_string += &format!(
|
|
408
|
-
"\"{}\":{}",
|
|
409
|
-
samples[i].to_string().replace("\\", ""),
|
|
410
|
-
gene_array[i].to_string()
|
|
411
|
-
);
|
|
412
|
-
|
|
413
|
-
if i != gene_array.len() - 1 {
|
|
414
|
-
output_string += &",";
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
output_string += &"}".to_string();
|
|
418
|
-
|
|
419
|
-
println!("Time generating full array:{:?}", time_generating_full_array.elapsed());
|
|
420
|
-
println!("output_string:{}", output_string);
|
|
421
|
-
|
|
422
|
-
Ok(())
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
/// Queries expression data for multiple genes from a dense format HDF5 file
|
|
426
|
-
///
|
|
427
|
-
/// Extracts expression values for multiple genes from a dense matrix HDF5 file,
|
|
428
|
-
/// optimizing for both single gene (linear search) and multi-gene (hashmap) queries.
|
|
429
|
-
///
|
|
430
|
-
/// # Arguments
|
|
431
|
-
/// * `hdf5_filename` - Path to the HDF5 file
|
|
432
|
-
/// * `gene_names` - Vector of gene names to query
|
|
433
|
-
///
|
|
434
|
-
/// # Returns
|
|
435
|
-
/// Prints a JSON object with expression data for all requested genes to stdout.
|
|
436
|
-
fn query_multiple_genes_dense(hdf5_filename: String, gene_names: Vec<String>) -> Result<()> {
|
|
437
|
-
let overall_start_time = Instant::now();
|
|
438
|
-
|
|
439
|
-
// Create timing map to store all timing data
|
|
440
|
-
let mut timings = Map::new();
|
|
441
|
-
|
|
442
|
-
let file = match File::open(&hdf5_filename) {
|
|
443
|
-
Ok(f) => f,
|
|
444
|
-
Err(err) => {
|
|
445
|
-
println!(
|
|
446
|
-
"{}",
|
|
447
|
-
serde_json::json!({
|
|
448
|
-
"status": "error",
|
|
449
|
-
"message": format!("Failed to open HDF5 file: {}", err)
|
|
450
|
-
})
|
|
451
|
-
);
|
|
452
|
-
return Ok(());
|
|
453
|
-
}
|
|
454
|
-
};
|
|
455
|
-
|
|
456
|
-
let genes_dataset = match file.dataset("gene_ids") {
|
|
457
|
-
Ok(ds) => ds,
|
|
458
|
-
Err(err) => {
|
|
459
|
-
println!(
|
|
460
|
-
"{}",
|
|
461
|
-
serde_json::json!({
|
|
462
|
-
"status": "error",
|
|
463
|
-
"message": format!("Failed to open gene_ids dataset: {}", err)
|
|
464
|
-
})
|
|
465
|
-
);
|
|
466
|
-
return Ok(());
|
|
467
|
-
}
|
|
468
|
-
};
|
|
469
|
-
|
|
470
|
-
let genes_varlen = match genes_dataset.read_1d::<VarLenAscii>() {
|
|
471
|
-
Ok(g) => g,
|
|
472
|
-
Err(err) => {
|
|
473
|
-
println!(
|
|
474
|
-
"{}",
|
|
475
|
-
serde_json::json!({
|
|
476
|
-
"status": "error",
|
|
477
|
-
"message": format!("Failed to read gene names as VarLenAscii: {}", err)
|
|
478
|
-
})
|
|
479
|
-
);
|
|
480
|
-
return Ok(());
|
|
481
|
-
}
|
|
482
|
-
};
|
|
483
|
-
|
|
484
|
-
let genes: Vec<String> = genes_varlen.iter().map(|g| g.to_string()).collect();
|
|
485
|
-
|
|
486
|
-
// Only create HashMap for multiple gene queries
|
|
487
|
-
let gene_to_index: Option<std::collections::HashMap<String, usize>> = if gene_names.len() > 1 {
|
|
488
|
-
let hashmap_start_time = Instant::now();
|
|
489
|
-
let mut map = std::collections::HashMap::with_capacity(genes.len());
|
|
490
|
-
for (idx, gene) in genes.iter().enumerate() {
|
|
491
|
-
map.insert(gene.clone(), idx);
|
|
492
|
-
}
|
|
493
|
-
timings.insert(
|
|
494
|
-
"build_hashmap_ms".to_string(),
|
|
495
|
-
Value::from(hashmap_start_time.elapsed().as_millis() as u64),
|
|
496
|
-
);
|
|
497
|
-
Some(map)
|
|
498
|
-
} else {
|
|
499
|
-
// Skip HashMap creation for single gene queries
|
|
500
|
-
None
|
|
501
|
-
};
|
|
502
|
-
|
|
503
|
-
let samples_dataset = match file.dataset("samples") {
|
|
504
|
-
Ok(ds) => ds,
|
|
505
|
-
Err(err) => {
|
|
506
|
-
println!(
|
|
507
|
-
"{}",
|
|
508
|
-
serde_json::json!({
|
|
509
|
-
"status": "error",
|
|
510
|
-
"message": format!("Failed to open samples dataset: {}", err)
|
|
511
|
-
})
|
|
512
|
-
);
|
|
513
|
-
return Ok(());
|
|
514
|
-
}
|
|
515
|
-
};
|
|
516
|
-
|
|
517
|
-
let samples_varlen = match samples_dataset.read_1d::<VarLenAscii>() {
|
|
518
|
-
Ok(s) => s,
|
|
519
|
-
Err(err) => {
|
|
520
|
-
println!(
|
|
521
|
-
"{}",
|
|
522
|
-
serde_json::json!({
|
|
523
|
-
"status": "error",
|
|
524
|
-
"message": format!("Failed to read samples as VarLenAscii: {}", err)
|
|
525
|
-
})
|
|
526
|
-
);
|
|
527
|
-
return Ok(());
|
|
528
|
-
}
|
|
529
|
-
};
|
|
530
|
-
|
|
531
|
-
let samples: Vec<String> = samples_varlen.iter().map(|s| s.to_string()).collect();
|
|
532
|
-
|
|
533
|
-
let counts_dataset = match file.dataset("counts") {
|
|
534
|
-
Ok(ds) => ds,
|
|
535
|
-
Err(err) => {
|
|
536
|
-
println!(
|
|
537
|
-
"{}",
|
|
538
|
-
serde_json::json!({
|
|
539
|
-
"status": "error",
|
|
540
|
-
"message": format!("Failed to open counts dataset: {}", err)
|
|
541
|
-
})
|
|
542
|
-
);
|
|
543
|
-
return Ok(());
|
|
544
|
-
}
|
|
545
|
-
};
|
|
546
|
-
|
|
547
|
-
// Create thread-local storage for results
|
|
548
|
-
let genes_map = Arc::new(std::sync::Mutex::new(Map::new()));
|
|
549
|
-
let gene_timings = Arc::new(std::sync::Mutex::new(Map::new()));
|
|
550
|
-
|
|
551
|
-
if gene_names.len() > 1 {
|
|
552
|
-
// For multiple genes: preload all data and use parallel processing
|
|
553
|
-
timings.insert("parallel_processing".to_string(), Value::from(true));
|
|
554
|
-
|
|
555
|
-
// Load all gene data upfront only when processing multiple genes
|
|
556
|
-
let all_data_start_time = Instant::now();
|
|
557
|
-
let all_gene_data = match counts_dataset.read::<f64, Dim<[usize; 2]>>() {
|
|
558
|
-
Ok(data) => {
|
|
559
|
-
timings.insert(
|
|
560
|
-
"read_all_gene_data_ms".to_string(),
|
|
561
|
-
Value::from(all_data_start_time.elapsed().as_millis() as u64),
|
|
562
|
-
);
|
|
563
|
-
Some(data)
|
|
564
|
-
}
|
|
565
|
-
Err(err) => {
|
|
566
|
-
// Failed to read all data at once, will fallback to per-gene reading
|
|
567
|
-
timings.insert(
|
|
568
|
-
"read_all_gene_data_error".to_string(),
|
|
569
|
-
Value::String(format!("{:?}", err)),
|
|
570
|
-
);
|
|
571
|
-
None
|
|
572
|
-
}
|
|
573
|
-
};
|
|
574
|
-
|
|
575
|
-
// Configurable thread count for testing
|
|
576
|
-
let thread_count = 2;
|
|
577
|
-
timings.insert("thread_count".to_string(), Value::from(thread_count));
|
|
578
|
-
|
|
579
|
-
// Create a scoped thread pool with specified number of threads
|
|
580
|
-
match rayon::ThreadPoolBuilder::new().num_threads(thread_count).build() {
|
|
581
|
-
Ok(pool) => {
|
|
582
|
-
// Use the pool for this specific work
|
|
583
|
-
pool.install(|| {
|
|
584
|
-
gene_names.par_iter().for_each(|gene_name| {
|
|
585
|
-
let gene_start_time = Instant::now();
|
|
586
|
-
|
|
587
|
-
// Use HashMap for O(1) lookup for multiple genes
|
|
588
|
-
let gene_index = match &gene_to_index {
|
|
589
|
-
Some(map) => map.get(gene_name).cloned(),
|
|
590
|
-
None => genes.iter().position(|x| *x == *gene_name),
|
|
591
|
-
};
|
|
592
|
-
|
|
593
|
-
match gene_index {
|
|
594
|
-
Some(gene_index) => {
|
|
595
|
-
// Make sure the gene index is valid for this dataset
|
|
596
|
-
if gene_index >= counts_dataset.shape()[0] {
|
|
597
|
-
let mut error_map = Map::new();
|
|
598
|
-
error_map.insert(
|
|
599
|
-
"error".to_string(),
|
|
600
|
-
Value::String("Gene index out of bounds".to_string()),
|
|
601
|
-
);
|
|
602
|
-
|
|
603
|
-
// Store the error result
|
|
604
|
-
let mut genes_map = genes_map.lock().unwrap();
|
|
605
|
-
genes_map.insert(gene_name.clone(), Value::Object(error_map));
|
|
606
|
-
} else {
|
|
607
|
-
// Use pre-loaded data if available
|
|
608
|
-
if let Some(ref all_data) = all_gene_data {
|
|
609
|
-
// Extract the row directly from pre-loaded data
|
|
610
|
-
let gene_expression = all_data.slice(s![gene_index, ..]);
|
|
611
|
-
|
|
612
|
-
// Create samples map for this gene
|
|
613
|
-
let mut samples_map = Map::new();
|
|
614
|
-
for (i, sample) in samples.iter().enumerate() {
|
|
615
|
-
if i < gene_expression.len() {
|
|
616
|
-
// Handle potential NaN or infinity values
|
|
617
|
-
let value = if gene_expression[i].is_finite() {
|
|
618
|
-
Value::from(gene_expression[i])
|
|
619
|
-
} else {
|
|
620
|
-
Value::Null
|
|
621
|
-
};
|
|
622
|
-
|
|
623
|
-
samples_map.insert(sample.replace("\\", ""), value);
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
// Create gene data and store it
|
|
628
|
-
let gene_data = json!({
|
|
629
|
-
"dataId": gene_name,
|
|
630
|
-
"samples": samples_map
|
|
631
|
-
});
|
|
632
|
-
|
|
633
|
-
let mut genes_map = genes_map.lock().unwrap();
|
|
634
|
-
genes_map.insert(gene_name.clone(), gene_data);
|
|
635
|
-
} else {
|
|
636
|
-
// Fallback to per-gene reading if bulk load failed
|
|
637
|
-
match counts_dataset.read_slice_1d::<f64, _>(s![gene_index, ..]) {
|
|
638
|
-
Ok(gene_expression) => {
|
|
639
|
-
// Create samples map for this gene
|
|
640
|
-
let mut samples_map = Map::new();
|
|
641
|
-
for (i, sample) in samples.iter().enumerate() {
|
|
642
|
-
if i < gene_expression.len() {
|
|
643
|
-
// Handle potential NaN or infinity values
|
|
644
|
-
let value = if gene_expression[i].is_finite() {
|
|
645
|
-
Value::from(gene_expression[i])
|
|
646
|
-
} else {
|
|
647
|
-
Value::Null
|
|
648
|
-
};
|
|
649
|
-
|
|
650
|
-
samples_map.insert(sample.replace("\\", ""), value);
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
// Create gene data and store it
|
|
655
|
-
let gene_data = json!({
|
|
656
|
-
"dataId": gene_name,
|
|
657
|
-
"samples": samples_map
|
|
658
|
-
});
|
|
659
|
-
|
|
660
|
-
let mut genes_map = genes_map.lock().unwrap();
|
|
661
|
-
genes_map.insert(gene_name.clone(), gene_data);
|
|
662
|
-
}
|
|
663
|
-
Err(err1) => {
|
|
664
|
-
let mut error_map = Map::new();
|
|
665
|
-
error_map.insert(
|
|
666
|
-
"error".to_string(),
|
|
667
|
-
Value::String(format!(
|
|
668
|
-
"Failed to read expression values: {:?}",
|
|
669
|
-
err1
|
|
670
|
-
)),
|
|
671
|
-
);
|
|
672
|
-
|
|
673
|
-
let mut genes_map = genes_map.lock().unwrap();
|
|
674
|
-
genes_map.insert(gene_name.clone(), Value::Object(error_map));
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
None => {
|
|
681
|
-
// Gene not found
|
|
682
|
-
let mut error_map = Map::new();
|
|
683
|
-
error_map.insert(
|
|
684
|
-
"error".to_string(),
|
|
685
|
-
Value::String("Gene not found in dataset".to_string()),
|
|
686
|
-
);
|
|
687
|
-
|
|
688
|
-
let mut genes_map = genes_map.lock().unwrap();
|
|
689
|
-
genes_map.insert(gene_name.clone(), Value::Object(error_map));
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
// Record timing
|
|
694
|
-
let elapsed_time = gene_start_time.elapsed().as_millis() as u64;
|
|
695
|
-
let mut gene_timings = gene_timings.lock().unwrap();
|
|
696
|
-
gene_timings.insert(gene_name.clone(), Value::from(elapsed_time));
|
|
697
|
-
});
|
|
698
|
-
});
|
|
699
|
-
}
|
|
700
|
-
Err(err) => {
|
|
701
|
-
// If thread pool creation fails, fall back to sequential processing
|
|
702
|
-
timings.insert(
|
|
703
|
-
"thread_pool_error".to_string(),
|
|
704
|
-
Value::String(format!("Failed to create thread pool: {:?}", err)),
|
|
705
|
-
);
|
|
706
|
-
|
|
707
|
-
process_genes_sequentially(
|
|
708
|
-
&gene_names,
|
|
709
|
-
&genes,
|
|
710
|
-
&gene_to_index,
|
|
711
|
-
&counts_dataset,
|
|
712
|
-
&all_gene_data,
|
|
713
|
-
&samples,
|
|
714
|
-
&genes_map,
|
|
715
|
-
);
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
} else if gene_names.len() == 1 {
|
|
719
|
-
let gene_name = &gene_names[0];
|
|
720
|
-
|
|
721
|
-
match genes.iter().position(|x| *x == *gene_name) {
|
|
722
|
-
Some(gene_index) => {
|
|
723
|
-
if gene_index >= counts_dataset.shape()[0] {
|
|
724
|
-
let mut error_map = Map::new();
|
|
725
|
-
error_map.insert(
|
|
726
|
-
"error".to_string(),
|
|
727
|
-
Value::String("Gene index out of bounds".to_string()),
|
|
728
|
-
);
|
|
729
|
-
|
|
730
|
-
let mut genes_map = genes_map.lock().unwrap();
|
|
731
|
-
genes_map.insert(gene_name.clone(), Value::Object(error_map));
|
|
732
|
-
} else {
|
|
733
|
-
// Read just this single gene's data directly
|
|
734
|
-
match counts_dataset.read_slice_1d::<f64, _>(s![gene_index, ..]) {
|
|
735
|
-
Ok(gene_expression) => {
|
|
736
|
-
// Create samples map for this gene
|
|
737
|
-
let mut samples_map = Map::new();
|
|
738
|
-
for (i, sample) in samples.iter().enumerate() {
|
|
739
|
-
if i < gene_expression.len() {
|
|
740
|
-
// Handle potential NaN or infinity values
|
|
741
|
-
let value = if gene_expression[i].is_finite() {
|
|
742
|
-
Value::from(gene_expression[i])
|
|
743
|
-
} else {
|
|
744
|
-
Value::Null
|
|
745
|
-
};
|
|
746
|
-
|
|
747
|
-
samples_map.insert(sample.replace("\\", ""), value);
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
let gene_data = json!({
|
|
752
|
-
"dataId": gene_name,
|
|
753
|
-
"samples": samples_map
|
|
754
|
-
});
|
|
755
|
-
|
|
756
|
-
let mut genes_map = genes_map.lock().unwrap();
|
|
757
|
-
genes_map.insert(gene_name.clone(), gene_data);
|
|
758
|
-
}
|
|
759
|
-
Err(err) => {
|
|
760
|
-
let mut error_map = Map::new();
|
|
761
|
-
error_map.insert(
|
|
762
|
-
"error".to_string(),
|
|
763
|
-
Value::String(format!("Failed to read expression values: {:?}", err)),
|
|
764
|
-
);
|
|
765
|
-
|
|
766
|
-
let mut genes_map = genes_map.lock().unwrap();
|
|
767
|
-
genes_map.insert(gene_name.clone(), Value::Object(error_map));
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
None => {
|
|
773
|
-
let mut error_map = Map::new();
|
|
774
|
-
error_map.insert(
|
|
775
|
-
"error".to_string(),
|
|
776
|
-
Value::String("Gene not found in dataset".to_string()),
|
|
777
|
-
);
|
|
778
|
-
|
|
779
|
-
let mut genes_map = genes_map.lock().unwrap();
|
|
780
|
-
genes_map.insert(gene_name.clone(), Value::Object(error_map));
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
// Get the final maps from the Arc<Mutex<>>
|
|
786
|
-
let genes_map = Arc::try_unwrap(genes_map).unwrap().into_inner().unwrap();
|
|
787
|
-
|
|
788
|
-
let output_json = json!({
|
|
789
|
-
"genes": genes_map,
|
|
790
|
-
"timings": timings,
|
|
791
|
-
"total_time_ms": overall_start_time.elapsed().as_millis() as u64
|
|
792
|
-
});
|
|
793
|
-
|
|
794
|
-
println!("{}", output_json);
|
|
795
|
-
|
|
796
|
-
Ok(())
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
// Helper function to process genes sequentially with optional HashMap lookup
|
|
800
|
-
fn process_genes_sequentially(
|
|
801
|
-
gene_names: &Vec<String>,
|
|
802
|
-
genes: &Vec<String>,
|
|
803
|
-
gene_to_index: &Option<std::collections::HashMap<String, usize>>,
|
|
804
|
-
counts_dataset: &hdf5::Dataset,
|
|
805
|
-
all_gene_data: &Option<ndarray::ArrayBase<ndarray::OwnedRepr<f64>, ndarray::Dim<[usize; 2]>>>,
|
|
806
|
-
samples: &Vec<String>,
|
|
807
|
-
genes_map: &Arc<std::sync::Mutex<Map<String, Value>>>,
|
|
808
|
-
) {
|
|
809
|
-
for gene_name in gene_names {
|
|
810
|
-
// Find the index of the requested gene, using HashMap if available
|
|
811
|
-
let gene_index = match gene_to_index {
|
|
812
|
-
Some(map) => map.get(gene_name).cloned(),
|
|
813
|
-
None => genes.iter().position(|x| *x == *gene_name),
|
|
814
|
-
};
|
|
815
|
-
|
|
816
|
-
match gene_index {
|
|
817
|
-
Some(gene_index) => {
|
|
818
|
-
// Make sure the gene index is valid for this dataset
|
|
819
|
-
if gene_index >= counts_dataset.shape()[0] {
|
|
820
|
-
let mut error_map = Map::new();
|
|
821
|
-
error_map.insert(
|
|
822
|
-
"error".to_string(),
|
|
823
|
-
Value::String("Gene index out of bounds".to_string()),
|
|
824
|
-
);
|
|
825
|
-
|
|
826
|
-
// Store the error result
|
|
827
|
-
let mut genes_map = genes_map.lock().unwrap();
|
|
828
|
-
genes_map.insert(gene_name.clone(), Value::Object(error_map));
|
|
829
|
-
} else {
|
|
830
|
-
// Use pre-loaded data if available
|
|
831
|
-
if let Some(ref all_data) = all_gene_data {
|
|
832
|
-
let gene_expression = all_data.slice(s![gene_index, ..]);
|
|
833
|
-
|
|
834
|
-
// Create samples map for this gene
|
|
835
|
-
let mut samples_map = Map::new();
|
|
836
|
-
for (i, sample) in samples.iter().enumerate() {
|
|
837
|
-
if i < gene_expression.len() {
|
|
838
|
-
let value = if gene_expression[i].is_finite() {
|
|
839
|
-
Value::from(gene_expression[i])
|
|
840
|
-
} else {
|
|
841
|
-
Value::Null
|
|
842
|
-
};
|
|
843
|
-
|
|
844
|
-
samples_map.insert(sample.replace("\\", ""), value);
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
let gene_data = json!({
|
|
849
|
-
"dataId": gene_name,
|
|
850
|
-
"samples": samples_map
|
|
851
|
-
});
|
|
852
|
-
|
|
853
|
-
let mut genes_map = genes_map.lock().unwrap();
|
|
854
|
-
genes_map.insert(gene_name.clone(), gene_data);
|
|
855
|
-
} else {
|
|
856
|
-
// Fallback to per-gene reading if bulk load failed
|
|
857
|
-
match counts_dataset.read_slice_1d::<f64, _>(s![gene_index, ..]) {
|
|
858
|
-
Ok(gene_expression) => {
|
|
859
|
-
// Create samples map for this gene
|
|
860
|
-
let mut samples_map = Map::new();
|
|
861
|
-
for (i, sample) in samples.iter().enumerate() {
|
|
862
|
-
if i < gene_expression.len() {
|
|
863
|
-
let value = if gene_expression[i].is_finite() {
|
|
864
|
-
Value::from(gene_expression[i])
|
|
865
|
-
} else {
|
|
866
|
-
Value::Null
|
|
867
|
-
};
|
|
868
|
-
|
|
869
|
-
samples_map.insert(sample.replace("\\", ""), value);
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
let gene_data = json!({
|
|
874
|
-
"dataId": gene_name,
|
|
875
|
-
"samples": samples_map
|
|
876
|
-
});
|
|
877
|
-
|
|
878
|
-
let mut genes_map = genes_map.lock().unwrap();
|
|
879
|
-
genes_map.insert(gene_name.clone(), gene_data);
|
|
880
|
-
}
|
|
881
|
-
Err(err1) => {
|
|
882
|
-
let mut error_map = Map::new();
|
|
883
|
-
error_map.insert(
|
|
884
|
-
"error".to_string(),
|
|
885
|
-
Value::String(format!("Failed to read expression values: {:?}", err1)),
|
|
886
|
-
);
|
|
887
|
-
|
|
888
|
-
let mut genes_map = genes_map.lock().unwrap();
|
|
889
|
-
genes_map.insert(gene_name.clone(), Value::Object(error_map));
|
|
890
|
-
}
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
}
|
|
895
|
-
None => {
|
|
896
|
-
let mut error_map = Map::new();
|
|
897
|
-
error_map.insert(
|
|
898
|
-
"error".to_string(),
|
|
899
|
-
Value::String("Gene not found in dataset".to_string()),
|
|
900
|
-
);
|
|
901
|
-
|
|
902
|
-
let mut genes_map = genes_map.lock().unwrap();
|
|
903
|
-
genes_map.insert(gene_name.clone(), Value::Object(error_map));
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
}
|
|
908
|
-
/// Queries expression data for multiple genes from a sparse format HDF5 file
|
|
909
|
-
///
|
|
910
|
-
/// This function extracts expression values for multiple specified genes from an HDF5 file
|
|
911
|
-
/// that uses a sparse matrix representation. It optimizes the query by reading shared datasets only once.
|
|
912
|
-
///
|
|
913
|
-
/// # Arguments
|
|
914
|
-
///
|
|
915
|
-
/// * `hdf5_filename` - Path to the HDF5 file
|
|
916
|
-
/// * `gene_names` - Vector of gene names to query
|
|
917
|
-
///
|
|
918
|
-
/// # Returns
|
|
919
|
-
///
|
|
920
|
-
/// A result indicating success or error. On success, the function prints a JSON object
|
|
921
|
-
/// containing expression data for all requested genes to stdout.
|
|
922
|
-
fn query_multiple_genes_sparse(hdf5_filename: String, gene_names: Vec<String>) -> Result<()> {
|
|
923
|
-
let overall_start_time = Instant::now();
|
|
924
|
-
|
|
925
|
-
// Create timing map
|
|
926
|
-
let mut timings = Map::new();
|
|
927
|
-
timings.insert("gene_count".to_string(), Value::from(gene_names.len()));
|
|
928
|
-
timings.insert("format".to_string(), Value::String("sparse".to_string()));
|
|
929
|
-
|
|
930
|
-
// Open file and read datasets
|
|
931
|
-
let file_open_start = Instant::now();
|
|
932
|
-
let file = File::open(&hdf5_filename)?;
|
|
933
|
-
timings.insert(
|
|
934
|
-
"file_open_ms".to_string(),
|
|
935
|
-
Value::from(file_open_start.elapsed().as_millis() as u64),
|
|
936
|
-
);
|
|
937
|
-
|
|
938
|
-
let dim_start = Instant::now();
|
|
939
|
-
let ds_dim = file.dataset("data/dim")?;
|
|
940
|
-
let data_dim: Array1<_> = ds_dim.read::<usize, Dim<[usize; 1]>>()?;
|
|
941
|
-
let num_samples = data_dim[0];
|
|
942
|
-
let _num_genes = data_dim[1];
|
|
943
|
-
timings.insert(
|
|
944
|
-
"read_dims_ms".to_string(),
|
|
945
|
-
Value::from(dim_start.elapsed().as_millis() as u64),
|
|
946
|
-
);
|
|
947
|
-
|
|
948
|
-
let ds_genes = file.dataset("gene_names")?;
|
|
949
|
-
let genes = ds_genes.read_1d::<FixedAscii<104>>()?;
|
|
950
|
-
|
|
951
|
-
let ds_samples = file.dataset("sample_names")?;
|
|
952
|
-
let samples = ds_samples.read_1d::<FixedAscii<104>>()?;
|
|
953
|
-
|
|
954
|
-
// Read p dataset (contains pointers for all genes)
|
|
955
|
-
let p_start_time = Instant::now();
|
|
956
|
-
let ds_p = file.dataset("data/p")?;
|
|
957
|
-
let data_p: Array1<usize> = ds_p.read_1d::<usize>()?;
|
|
958
|
-
timings.insert(
|
|
959
|
-
"read_p_dataset_ms".to_string(),
|
|
960
|
-
Value::from(p_start_time.elapsed().as_millis() as u64),
|
|
961
|
-
);
|
|
962
|
-
|
|
963
|
-
// Open i and x datasets
|
|
964
|
-
let ds_start_time = Instant::now();
|
|
965
|
-
let ds_i = file.dataset("data/i")?;
|
|
966
|
-
let ds_x = file.dataset("data/x")?;
|
|
967
|
-
timings.insert(
|
|
968
|
-
"open_i_x_datasets_ms".to_string(),
|
|
969
|
-
Value::from(ds_start_time.elapsed().as_millis() as u64),
|
|
970
|
-
);
|
|
971
|
-
|
|
972
|
-
// Determine number of threads to use
|
|
973
|
-
let num_threads = num_cpus::get();
|
|
974
|
-
timings.insert("num_threads".to_string(), Value::from(num_threads as u64));
|
|
975
|
-
|
|
976
|
-
// Thread-safe maps for results
|
|
977
|
-
let genes_map = Arc::new(std::sync::Mutex::new(Map::new()));
|
|
978
|
-
let gene_timings = Arc::new(std::sync::Mutex::new(Map::new()));
|
|
979
|
-
|
|
980
|
-
// Use rayon for parallel processing
|
|
981
|
-
gene_names.par_iter().for_each(|gene_name| {
|
|
982
|
-
let gene_start_time = Instant::now();
|
|
983
|
-
|
|
984
|
-
// Find the index of the requested gene
|
|
985
|
-
match genes.iter().position(|&x| x == *gene_name) {
|
|
986
|
-
Some(gene_index) => {
|
|
987
|
-
// Find start and end points for this gene's data
|
|
988
|
-
let array_start_point = data_p[gene_index];
|
|
989
|
-
let array_stop_point = data_p[gene_index + 1];
|
|
990
|
-
let num_populated_cells = array_stop_point - array_start_point;
|
|
991
|
-
|
|
992
|
-
if num_populated_cells == 0 {
|
|
993
|
-
// Gene has no data, create array of zeros
|
|
994
|
-
let mut samples_map = Map::new();
|
|
995
|
-
for (_i, sample) in samples.iter().enumerate() {
|
|
996
|
-
samples_map.insert(sample.to_string().replace("\\", ""), Value::from(0.0));
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
let gene_data = json!({
|
|
1000
|
-
"dataId": gene_name,
|
|
1001
|
-
"samples": samples_map
|
|
1002
|
-
});
|
|
1003
|
-
|
|
1004
|
-
let mut genes_map = genes_map.lock().unwrap();
|
|
1005
|
-
genes_map.insert(gene_name.clone(), gene_data);
|
|
1006
|
-
} else {
|
|
1007
|
-
// Read data for this gene
|
|
1008
|
-
match ds_i.read_slice_1d::<usize, _>(array_start_point..array_stop_point) {
|
|
1009
|
-
Ok(populated_column_ids) => {
|
|
1010
|
-
match ds_x.read_slice_1d::<f64, _>(array_start_point..array_stop_point) {
|
|
1011
|
-
Ok(populated_column_values) => {
|
|
1012
|
-
// Generate the complete array from sparse representation
|
|
1013
|
-
let mut gene_array: Array1<f64> = Array1::zeros(num_samples);
|
|
1014
|
-
|
|
1015
|
-
// Fill in values at populated column indices
|
|
1016
|
-
for (idx, &col_id) in populated_column_ids.iter().enumerate() {
|
|
1017
|
-
gene_array[col_id] = populated_column_values[idx];
|
|
1018
|
-
}
|
|
1019
|
-
|
|
1020
|
-
// Create samples map
|
|
1021
|
-
let mut samples_map = Map::new();
|
|
1022
|
-
for (_i, sample) in samples.iter().enumerate() {
|
|
1023
|
-
let value = if gene_array[_i].is_finite() {
|
|
1024
|
-
Value::from(gene_array[_i])
|
|
1025
|
-
} else {
|
|
1026
|
-
Value::Null
|
|
1027
|
-
};
|
|
1028
|
-
|
|
1029
|
-
samples_map.insert(sample.to_string().replace("\\", ""), value);
|
|
1030
|
-
}
|
|
1031
|
-
|
|
1032
|
-
let gene_data = json!({
|
|
1033
|
-
"dataId": gene_name,
|
|
1034
|
-
"samples": samples_map
|
|
1035
|
-
});
|
|
1036
|
-
|
|
1037
|
-
let mut genes_map = genes_map.lock().unwrap();
|
|
1038
|
-
genes_map.insert(gene_name.clone(), gene_data);
|
|
1039
|
-
}
|
|
1040
|
-
Err(err) => {
|
|
1041
|
-
let mut error_map = Map::new();
|
|
1042
|
-
error_map.insert(
|
|
1043
|
-
"error".to_string(),
|
|
1044
|
-
Value::String(format!("Failed to read x dataset: {:?}", err)),
|
|
1045
|
-
);
|
|
1046
|
-
|
|
1047
|
-
let mut genes_map = genes_map.lock().unwrap();
|
|
1048
|
-
genes_map.insert(gene_name.clone(), Value::Object(error_map));
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
}
|
|
1052
|
-
Err(err) => {
|
|
1053
|
-
let mut error_map = Map::new();
|
|
1054
|
-
error_map.insert(
|
|
1055
|
-
"error".to_string(),
|
|
1056
|
-
Value::String(format!("Failed to read i dataset: {:?}", err)),
|
|
1057
|
-
);
|
|
1058
|
-
|
|
1059
|
-
let mut genes_map = genes_map.lock().unwrap();
|
|
1060
|
-
genes_map.insert(gene_name.clone(), Value::Object(error_map));
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1064
|
-
}
|
|
1065
|
-
None => {
|
|
1066
|
-
let mut error_map = Map::new();
|
|
1067
|
-
error_map.insert(
|
|
1068
|
-
"error".to_string(),
|
|
1069
|
-
Value::String("Gene not found in dataset".to_string()),
|
|
1070
|
-
);
|
|
1071
|
-
|
|
1072
|
-
let mut genes_map = genes_map.lock().unwrap();
|
|
1073
|
-
genes_map.insert(gene_name.clone(), Value::Object(error_map));
|
|
1074
|
-
}
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
// Record timing
|
|
1078
|
-
let elapsed_time = gene_start_time.elapsed().as_millis() as u64;
|
|
1079
|
-
let mut gene_timings = gene_timings.lock().unwrap();
|
|
1080
|
-
gene_timings.insert(gene_name.clone(), Value::from(elapsed_time));
|
|
1081
|
-
});
|
|
1082
|
-
|
|
1083
|
-
// Get the final maps from the Arc<Mutex<>>
|
|
1084
|
-
let genes_map = Arc::try_unwrap(genes_map).unwrap().into_inner().unwrap();
|
|
1085
|
-
|
|
1086
|
-
let output_json = json!({
|
|
1087
|
-
"genes": genes_map,
|
|
1088
|
-
"timings": timings,
|
|
1089
|
-
"parallel": true,
|
|
1090
|
-
"total_time_ms": overall_start_time.elapsed().as_millis() as u64
|
|
1091
|
-
});
|
|
1092
|
-
|
|
1093
|
-
println!("{}", output_json);
|
|
1094
|
-
|
|
1095
|
-
Ok(())
|
|
1096
|
-
}
|
|
1097
|
-
fn main() -> Result<()> {
|
|
1098
|
-
let mut input = String::new();
|
|
1099
|
-
match io::stdin().read_line(&mut input) {
|
|
1100
|
-
Ok(_bytes_read) => {
|
|
1101
|
-
let input_json = json::parse(&input);
|
|
1102
|
-
match input_json {
|
|
1103
|
-
Ok(json_string) => {
|
|
1104
|
-
// Extract HDF5 filename
|
|
1105
|
-
let hdf5_filename = match json_string["hdf5_file"].as_str() {
|
|
1106
|
-
Some(x) => x.to_string(),
|
|
1107
|
-
None => {
|
|
1108
|
-
panic!("HDF5 filename not provided");
|
|
1109
|
-
}
|
|
1110
|
-
};
|
|
1111
|
-
|
|
1112
|
-
// Case 1: Check if "genes" field exists and is an array
|
|
1113
|
-
if json_string["genes"].is_array() {
|
|
1114
|
-
// Convert the JsonValue array to a Vec<String>
|
|
1115
|
-
let mut gene_names: Vec<String> = Vec::new();
|
|
1116
|
-
for gene_value in json_string["genes"].members() {
|
|
1117
|
-
if let Some(gene_str) = gene_value.as_str() {
|
|
1118
|
-
gene_names.push(gene_str.to_string());
|
|
1119
|
-
}
|
|
1120
|
-
}
|
|
1121
|
-
|
|
1122
|
-
if !gene_names.is_empty() {
|
|
1123
|
-
match detect_hdf5_format(&hdf5_filename)? {
|
|
1124
|
-
"dense" => query_multiple_genes_dense(hdf5_filename, gene_names)?,
|
|
1125
|
-
"sparse" => query_multiple_genes_sparse(hdf5_filename, gene_names)?,
|
|
1126
|
-
_ => {
|
|
1127
|
-
println!(
|
|
1128
|
-
"{}",
|
|
1129
|
-
serde_json::json!({
|
|
1130
|
-
"status": "failure",
|
|
1131
|
-
"message": "Cannot query genes in unknown file format.",
|
|
1132
|
-
"file_path": hdf5_filename
|
|
1133
|
-
})
|
|
1134
|
-
);
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
return Ok(());
|
|
1138
|
-
}
|
|
1139
|
-
}
|
|
1140
|
-
// Case 2: Check if "gene" field exists and is an array (this handles the case we're seeing)
|
|
1141
|
-
else if json_string["gene"].is_array() {
|
|
1142
|
-
// Convert the JsonValue array to a Vec<String>
|
|
1143
|
-
let mut gene_names: Vec<String> = Vec::new();
|
|
1144
|
-
for gene_value in json_string["gene"].members() {
|
|
1145
|
-
if let Some(gene_str) = gene_value.as_str() {
|
|
1146
|
-
gene_names.push(gene_str.to_string());
|
|
1147
|
-
}
|
|
1148
|
-
}
|
|
1149
|
-
|
|
1150
|
-
if !gene_names.is_empty() {
|
|
1151
|
-
// Process multiple genes
|
|
1152
|
-
match detect_hdf5_format(&hdf5_filename)? {
|
|
1153
|
-
"dense" => query_multiple_genes_dense(hdf5_filename, gene_names)?,
|
|
1154
|
-
"sparse" => query_multiple_genes_sparse(hdf5_filename, gene_names)?,
|
|
1155
|
-
_ => {
|
|
1156
|
-
println!(
|
|
1157
|
-
"{}",
|
|
1158
|
-
serde_json::json!({
|
|
1159
|
-
"status": "failure",
|
|
1160
|
-
"message": "Cannot query genes in unknown file format.",
|
|
1161
|
-
"file_path": hdf5_filename
|
|
1162
|
-
})
|
|
1163
|
-
);
|
|
1164
|
-
}
|
|
1165
|
-
}
|
|
1166
|
-
return Ok(());
|
|
1167
|
-
}
|
|
1168
|
-
}
|
|
1169
|
-
// Case 3: Check if "gene" field exists and is a string (original single gene case)
|
|
1170
|
-
else if let Some(gene_name) = json_string["gene"].as_str() {
|
|
1171
|
-
query_gene(hdf5_filename, gene_name.to_string())?;
|
|
1172
|
-
return Ok(());
|
|
1173
|
-
}
|
|
1174
|
-
println!(
|
|
1175
|
-
"{}",
|
|
1176
|
-
serde_json::json!({
|
|
1177
|
-
"status": "error",
|
|
1178
|
-
"message": "Neither gene nor genes array provided in input"
|
|
1179
|
-
})
|
|
1180
|
-
);
|
|
1181
|
-
}
|
|
1182
|
-
Err(error) => println!("Incorrect json: {}", error),
|
|
1183
|
-
}
|
|
1184
|
-
}
|
|
1185
|
-
Err(error) => println!("Piping error: {}", error),
|
|
1186
|
-
}
|
|
1187
|
-
Ok(())
|
|
1188
|
-
}
|