//! BioPhys Neural 가중치 파일(.bpsn) 파서 //! 파일 구조: [매직(8B)][이름길이(4B)][이름(NB)][가중치(0x55AA 패턴)] use std::path::Path; use std::fs; /// BPSN 파일 매직 넘버 const BPSN_MAGIC: &[u8] = b"BPSN4.0_"; /// 파싱된 BioPhys 뇌 모델 #[derive(Debug, Clone)] pub struct BpsnModel { pub name: String, pub weights: Vec, // 2-bit 패킹 가중치 (uint32 = 16쿼크) pub weight_count: usize, pub size_mb: f32, } impl BpsnModel { /// .bpsn 파일 로딩 및 파싱 pub fn load(path: &Path) -> Result { let bytes = fs::read(path) .map_err(|e| format!("파일 읽기 실패: {}", e))?; // 매직 넘버 검증 if bytes.len() < 12 || &bytes[0..8] != BPSN_MAGIC { return Err(format!("유효하지 않은 BPSN 파일: {:?}", path)); } // 모델명 길이 (bytes[8..12] = u32 LE) let name_len = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]) as usize; // 모델명 파싱 let name_start = 12; let name_end = (name_start + name_len).min(bytes.len()); let name = String::from_utf8_lossy(&bytes[name_start..name_end]) .trim_end_matches('\0') .to_string(); // 가중치 데이터 (이름 이후 나머지 전부) let weight_start = name_start + name_len; let weight_bytes = &bytes[weight_start..]; // u8 → u32 패킹 (4바이트 = 1 쿼크 패킷 = 16개 2-bit 가중치) let weights: Vec = weight_bytes .chunks(4) .filter(|c| c.len() == 4) .map(|c| u32::from_le_bytes([c[0], c[1], c[2], c[3]])) .collect(); let weight_count = weights.len(); let size_mb = bytes.len() as f32 / 1024.0 / 1024.0; Ok(BpsnModel { name, weights, weight_count, size_mb }) } /// 가중치를 바이트 슬라이스로 반환 (GPU 업로드용) pub fn weights_as_bytes(&self) -> &[u8] { unsafe { std::slice::from_raw_parts( self.weights.as_ptr() as *const u8, self.weights.len() * 4, ) } } /// 초끈 공명 연산 (CPU 폴백) pub fn superstring_spin(&self, input_idx: usize, wave: u32) -> f32 { if input_idx >= self.weights.len() { return 0.0; } let quark = self.weights[input_idx] ^ wave; quark.count_ones() as f32 * 1.414 } /// 모델 요약 출력 pub fn summary(&self) { println!(" 📦 [BPSN] 모델: {}", self.name); println!(" 📦 가중치 패킷: {}개 ({}×16 = {}개 쿼크)", self.weight_count, self.weight_count, self.weight_count * 16); println!(" 📦 파일 크기: {:.3}MB", self.size_mb); println!(" 📦 샘플[0]: 0x{:08X} ({:08b}...)", self.weights.get(0).copied().unwrap_or(0), self.weights.get(0).copied().unwrap_or(0)); } } /// 모든 .bpsn 모델 일괄 로딩 pub fn load_all_brains(dir: &Path) -> Vec { let mut models = Vec::new(); let pattern = dir.join("*_8state.bpsn"); if let Ok(entries) = fs::read_dir(dir) { for entry in entries.flatten() { let p = entry.path(); if p.extension().map(|e| e == "bpsn").unwrap_or(false) { match BpsnModel::load(&p) { Ok(m) => { println!(" ✅ 로딩 완료: {}", m.name); models.push(m); }, Err(e) => println!(" ❌ 로딩 실패: {} → {}", p.display(), e), } } } } models }