Zip and Skip Tries
Loading...
Searching...
No Matches
genetics.cu
Go to the documentation of this file.
1
13#include "genetics.cuh"
14#include "nucleotide.hpp"
15#include "data.hpp"
16
17#include <algorithm>
18#include <filesystem>
19#include <fstream>
20#include <random>
21#include <string>
22#include <unordered_set>
23#include <vector>
24
25using namespace genetics;
26
37{
38 Gene gene;
39
40 std::string line;
41 while (std::getline(stream, line))
42 {
43 for (char c : line)
44 {
46 {
48 }
49 if (c == '>')
50 {
51 printf("NOT GOOD\n");
52 exit(0);
53 }
54 }
55 }
56
57 return gene;
58}
59
71Gene genetics::from_gbk(const std::string& filename)
72{
73 std::ifstream file(filename);
74 if (!file.is_open())
75 {
76 throw std::invalid_argument("File not found");
77 }
78
79 // step 1: skip header
80 // keep reading until the line just read is "ORIGIN"
81
82 std::string line;
83 while (std::getline(file, line))
84 {
85 if (line == "ORIGIN")
86 {
87 break;
88 }
89 }
90
91 // step 2: read nucleotides
92 // read line by line, character by character, discarding any non-nucleotide characters
93
94 return from_stream(file);
95}
96
108{
109 std::ifstream file(filename);
110 if (!file.is_open())
111 {
112 throw std::invalid_argument("File not found");
113 }
114
115 // step 1: skip header (just one line)
116
117 std::string line;
118 std::getline(file, line);
119
120 return from_stream(file);
121}
122
132void genetics::to_dna(const Gene& gene, const std::string& filename)
133{
134 std::ofstream file(filename);
135 if (!file.is_open())
136 {
137 throw std::invalid_argument("File not found");
138 }
139
140 file << gene;
141}
142
153{
154 std::ifstream file(filename);
155 if (!file.is_open())
156 {
157 throw std::invalid_argument("File not found");
158 }
159
160 Gene gene;
161
162 std::string line;
163 while (std::getline(file, line))
164 {
165 for (char c : line)
166 {
168 }
169 }
170
171 return gene;
172}
173
183std::vector<Gene> genetics::from_directory(const std::string& dirname)
184{
185 std::vector<Gene> genes;
186
187 for (const auto& entry : std::filesystem::directory_iterator(dirname))
188 {
189 genes.emplace_back(from_file(entry));
190 }
191
192 return genes;
193}
194
195std::vector<Gene> genetics::from_directory(const std::string& dirname, FileType type)
196{
197 std::vector<Gene> genes;
198
199 // Iterate through directory and filter by the requested file type
200 for (const auto& entry : std::filesystem::directory_iterator(dirname))
201 {
202 // Only process files matching the specified type
203 if (get_file_type(entry) == type)
204 {
205 genes.emplace_back(from_file(entry));
206 }
207 }
208
209 return genes;
210}
211
212std::vector<std::string> genetics::get_dna_strings_from_directory(const std::string& dirname)
213{
214 std::vector<std::string> dna_strings;
215
216 // Iterate through all files in the directory
217 for (const auto& entry : std::filesystem::directory_iterator(dirname))
218 {
219 // Process only DNA format files
220 if (get_file_type(entry) == FileType::DNA)
221 {
222 std::ifstream file(entry.path());
223 if (!file.is_open())
224 {
225 throw std::invalid_argument("File not found");
226 }
227
228 // Read just the first line from each file
229 // Note: This assumes DNA files contain a single line with the entire sequence
230 std::string dna_string;
231 std::getline(file, dna_string);
232 dna_strings.emplace_back(dna_string);
233 }
234 }
235
236 return dna_strings;
237}
238
239namespace
240{
250 bool string_ends_with(const std::string& str, const std::string& suffix)
251 {
252 return str.size() >= suffix.size() && str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
253 }
254}
255
257{
259 {
260 return FileType::GBK;
261 }
263 {
264 return FileType::FNA;
265 }
267 {
268 return FileType::DNA;
269 }
271 {
272 return FileType::DNA_BIN;
273 }
275 {
276 return FileType::DNA_BIN_COMBINED;
277 }
278 else
279 {
280 return FileType::UNKNOWN;
281 }
282}
283
285{
287
288 switch (type)
289 {
290 case FileType::GBK:
291 return from_gbk(filename);
292 case FileType::FNA:
293 return from_fna(filename);
294 case FileType::DNA:
295 return from_dna(filename);
296 case FileType::DNA_BIN:
298 default:
299 throw std::invalid_argument("Unknown file type");
300 }
301}
302
303FileType genetics::get_file_type(const std::filesystem::path& path)
304{
305 return get_file_type(path.string());
306}
307
308FileType genetics::get_file_type(const std::filesystem::directory_entry& entry)
309{
310 return get_file_type(entry.path().string());
311}
312
313Gene genetics::from_file(const std::filesystem::path& path)
314{
315 return from_file(path.string());
316}
317
318Gene genetics::from_file(const std::filesystem::directory_entry& entry)
319{
320 return from_file(entry.path().string());
321}
322
323std::vector<std::filesystem::path> genetics::get_virus_nucleotide_paths()
324{
325 std::vector<std::filesystem::path> paths;
326
327 for (const auto& entry : std::filesystem::directory_iterator(VIRUS_DIRECTORY))
328 {
329 paths.emplace_back(entry.path());
330 }
331
332 return paths;
333}
334
335void genetics::combine_dna_bin_files(const std::string& dirname, const std::string& combined_filename)
336{
337 // Phase 1: Collect paths of all DNA_BIN files in the directory
339 timer.start("Adding filepaths to vector");
340 std::vector<std::filesystem::path> filepaths;
341 for (const auto& entry : std::filesystem::directory_iterator(dirname))
342 {
343 if (get_file_type(entry) == FileType::DNA_BIN)
344 {
345 filepaths.emplace_back(entry.path());
346 }
347 }
348 timer.print();
349
350 printf("Added %lu filepaths to vector\n", filepaths.size());
351
352 // Phase 2: Create the combined file header with file count, names, and placeholders for positions
353 timer.start("Writing names to binary file");
354 std::ofstream file(dirname + combined_filename + DNA_BIN_COMBINED_EXTENSION, std::ios::binary);
355 if (!file.is_open())
356 {
357 throw std::invalid_argument("File not found");
358 }
359
360 // Write the number of files first for easy parsing later
361 size_t num_names = filepaths.size();
362 file.write(reinterpret_cast<const char*>(&num_names), sizeof(num_names));
363
364 // For each file, write its name and reserve space for its position in the file
365 std::vector<std::streampos> streampos_positions;
366 for (const auto& filepath : filepaths)
367 {
368 // Extract the base filename without the .dna.bin extension
369 auto name = filepath.filename().string();
370 name = name.substr(0, name.size() - DNA_BIN_EXTENSION.size());
371
372 // Write the name with null terminator
373 file.write(name.c_str(), name.size());
374 file.write("\0", 1);
375
376 // Store the current position to update later with the actual gene data position
377 streampos_positions.push_back(file.tellp());
378
379 // Write a placeholder for the position (will update later)
380 file.write(reinterpret_cast<const char*>(&streampos_positions.back()), sizeof(streampos_positions.back()));
381 }
382
383 timer.print();
384
385 // Phase 3: Write each gene to the file and update its position in the header
386 timer.start();
387 for (unsigned i = 0; i < filepaths.size(); ++i)
388 {
389 // Remember current position where gene data will start
390 auto pos = file.tellp();
391
392 // Go back to the position placeholder in the header and update it
393 file.seekp(streampos_positions[i]);
394 file.write(reinterpret_cast<const char*>(&pos), sizeof(pos));
395
396 // Return to the end of the file to write the gene data
397 file.seekp(pos);
398
399 // Load and write the gene
400 auto gene = Gene::from_file(filepaths[i].string());
401 gene.to_file(file);
402
403 // Progress reporting for long operations
404 if (i % 1000 == 0)
405 {
406 printf("Wrote %u files, took %s\n", i, pretty_print(timer.elapsed_nanoseconds()).c_str());
407 }
408 }
409}
410
411GeneManager::GeneManager(const std::string& path) : m_path(path)
412{
413 // Open the combined binary file
414 m_file.open(path + DNA_BIN_COMBINED_EXTENSION, std::ios::binary);
415 if (!m_file.is_open())
416 {
417 throw std::invalid_argument("File not found");
418 }
419
421 timer.start("Loading names and positions");
422
423 // Read the number of genes stored in the file
424 size_t num_names;
425 m_file.read(reinterpret_cast<char*>(&num_names), sizeof(num_names));
426
427 // Reserve space for all gene metadata
428 m_gene_infos.reserve(num_names);
429 // Note: Previous implementation stored names and positions in separate vectors
430 // m_names.reserve(num_names);
431 // m_positions.reserve(num_names);
432
433 // Parse the gene metadata header
434 for (size_t i = 0; i < num_names; ++i)
435 {
436 // Read null-terminated gene name
437 std::string name;
438 char c;
439 while (m_file.read(&c, 1) && c != '\0')
440 {
441 name.push_back(c);
442 }
443
444 // Read the position where this gene's data starts in the file
445 std::streampos position;
446 m_file.read(reinterpret_cast<char*>(&position), sizeof(position));
447
448 // Store the metadata (size will be loaded or determined later)
449 m_gene_infos.push_back({ name, position, 0 });
450 }
451
452 timer.print();
453
454 // Load or create the size information for each gene
455 if (std::filesystem::exists(path + DNA_BIN_SIZES_EXTENSION))
456 {
457 // If size cache already exists, load it
458 load_sizes();
459 }
460 else
461 {
462 // Otherwise, calculate and save sizes
463 save_sizes();
464 }
465
466 // Sort genes by their size for optimized access patterns
467 timer.start("Sorting by size");
468 std::sort(m_gene_infos.begin(), m_gene_infos.end(), [](const GeneInfo& a, const GeneInfo& b) { return a.size < b.size; });
469 timer.print();
470}
471
473{
475 timer.start("Loading & saving sizes");
476
477 // Create a companion file to store size information for quick loading in future
478 std::ofstream sizes_file(m_path + DNA_BIN_SIZES_EXTENSION, std::ios::binary);
479 if (!sizes_file.is_open())
480 {
481 throw std::invalid_argument("File not found");
482 }
483
484 // For each gene, read its size from the combined file and save to the sizes file
485 for (auto& info : m_gene_infos)
486 {
487 // Navigate to where this gene's data begins
488 m_file.seekg(info.position);
489
490 // Read the size value from the gene data header
491 m_file.read(reinterpret_cast<char*>(&info.size), sizeof(info.size));
492
493 // Write the size to the companion file for future quick loading
494 sizes_file.write(reinterpret_cast<const char*>(&info.size), sizeof(info.size));
495 }
496
497 timer.print();
498}
499
501{
503 timer.start("Loading sizes");
504
505 // Open the pre-computed sizes file which contains size information for each gene
506 std::ifstream sizes_file(m_path + DNA_BIN_SIZES_EXTENSION, std::ios::binary);
507 if (!sizes_file.is_open())
508 {
509 throw std::invalid_argument("File not found");
510 }
511
512 // Read size for each gene in the same order as the gene infos
513 for (auto& info : m_gene_infos)
514 {
515 // Load the pre-computed size directly into the gene info structure
516 sizes_file.read(reinterpret_cast<char*>(&info.size), sizeof(info.size));
517 }
518
519 timer.print();
520}
521
523{
524 m_file.close();
525}
526
527std::vector<Gene> GeneManager::all_genes() const
528{
529 std::vector<Gene> genes;
530 // Iterate through all gene infos and load each gene from the combined file
531 for (const auto& info : m_gene_infos)
532 {
533 // Position file pointer at the start of this gene's data
534 m_file.seekg(info.position);
535
536 // Load the gene from the current file position and add to result vector
537 genes.emplace_back(Gene::from_file(m_file));
538 }
539
540 return genes;
541}
542
543std::vector<Gene> GeneManager::get_random_genes(size_t count) const
544{
545 // Initialize static random number generator for efficient repeated calls
546 static std::random_device rd;
547 static std::mt19937 gen(rd());
548 static std::uniform_int_distribution<size_t> dist(0, m_gene_infos.size() - 1);
549
550 // Use a set to ensure we don't select the same gene twice
551 // Store positions rather than indices to directly access the file
552 std::unordered_set<size_t> positions;
553
554 // Select random genes until we have the requested count
555 while (positions.size() < count)
556 {
557 positions.insert(m_gene_infos[dist(gen)].position);
558 }
559
560 std::vector<Gene> genes;
561
562 // Load each selected gene from its file position
563 for (const auto& position : positions)
564 {
565 // Position file pointer at the gene's data
566 m_file.seekg(position);
567
568 // Load the gene and add to result vector
569 genes.emplace_back(Gene::from_file(m_file));
570 }
571
572 return genes;
573}
void push_back(CHAR_T c) noexcept
Appends a single character to the end of the BitString.
static BitString from_file(const std::string &filename)
Reads a BitString from a file in binary format.
T * alloc_to_device(size_t size)
Allocates memory on the CUDA device.
std::string pretty_print(size_t nanoseconds)
Converts a duration in nanoseconds to a human-readable string format.
Definition data.cpp:65
Defines utilities for managing performance data logging and timing.
Provides utilities for working with genetic data in various formats.
Contains utilities for working with genetic data and file formats.
Gene from_file(const std::string &filename)
Loads a gene from a file with automatic format detection.
Definition genetics.cu:284
static const std::string DNA_BIN_COMBINED_EXTENSION
File extension for combined binary DNA files.
Definition genetics.cuh:55
Gene from_gbk(const std::string &filename)
Loads a gene from a GenBank format file.
Definition genetics.cu:71
static const std::string DNA_BIN_SIZES_EXTENSION
File extension for binary DNA file size information.
Definition genetics.cuh:58
std::vector< std::filesystem::path > get_virus_nucleotide_paths()
Gets paths to all virus nucleotide files in the virus directory.
Definition genetics.cu:323
Gene from_dna(const std::string &filename)
Loads a gene from a plain text DNA format file.
Definition genetics.cu:152
void to_dna(const Gene &gene, const std::string &filename)
Saves a gene to a plain text DNA format file.
Definition genetics.cu:132
static const std::string GBK_EXTENSION
File extension for GenBank format files.
Definition genetics.cuh:61
static const std::string DNA_EXTENSION
File extension for plain text DNA files.
Definition genetics.cuh:49
static const std::string FNA_EXTENSION
File extension for FASTA nucleotide format files.
Definition genetics.cuh:64
FileType get_file_type(const std::string &filename)
Determines the file type based on its filename extension.
Definition genetics.cu:256
std::vector< std::string > get_dna_strings_from_directory(const std::string &dirname)
Extracts DNA strings from all supported files in a directory.
Definition genetics.cu:212
Gene from_stream(std::istream &stream)
Loads a gene from an input stream.
Definition genetics.cu:36
static const std::string VIRUS_DIRECTORY
Path to the virus genome data directory.
Definition genetics.cuh:43
Gene from_fna(const std::string &filename)
Loads a gene from a FASTA nucleotide format file.
Definition genetics.cu:107
FileType
Enumerates the supported genetic file formats.
Definition genetics.cuh:74
std::vector< Gene > from_directory(const std::string &dirname)
Loads all genes from files in a directory with automatic format detection.
Definition genetics.cu:183
static const std::string DNA_BIN_EXTENSION
File extension for binary DNA files.
Definition genetics.cuh:52
void combine_dna_bin_files(const std::string &dirname, const std::string &combined_filename)
Combines multiple binary DNA files into a single combined file.
Definition genetics.cu:335
bool is_valid_nucleotide(char nucleotide)
Checks if a character represents a valid nucleotide.
Defines nucleotide representation and related utility functions.
static constexpr std::array< Nucleotide, 256 > char_to_nucleotide
Lookup table for converting characters to nucleotides.
A timer class measuring wall-clock time using std::chrono::high_resolution_clock.
Definition data.hpp:116
void start(const std::string &prompt="", bool reset=true) noexcept
Starts or resets the timer and optionally prints a starting message.
Definition data.hpp:138
Structure to track information about a gene in the combined file.
Definition genetics.cuh:260
std::string m_path
Path to the combined binary file.
Definition genetics.cuh:266
void load_sizes()
Loads gene size information from the companion file.
Definition genetics.cu:500
GeneManager(const std::string &path)
Constructs a GeneManager for accessing genes in the specified path.
Definition genetics.cu:411
std::vector< GeneInfo > m_gene_infos
Index of all genes in the combined file.
Definition genetics.cuh:268
std::vector< Gene > get_random_genes(size_t count) const
Retrieves a random subset of genes from the combined file.
Definition genetics.cu:543
void save_sizes()
Saves the size information of genes to a companion file.
Definition genetics.cu:472
~GeneManager()
Destructor for GeneManager.
Definition genetics.cu:522
std::ifstream m_file
File stream for reading gene data (mutable to allow const methods to read)
Definition genetics.cuh:267
std::vector< Gene > all_genes() const
Retrieves all genes from the combined file.
Definition genetics.cu:527