1use indicatif::{ProgressBar, ProgressStyle, MultiProgress};
2use std::time::{Duration, Instant};
3use nu_ansi_term::Color::{Green, Blue, Yellow, Red, Cyan};
4
5#[derive(Debug)]
8pub struct QuantizationProgressTracker {
9 multi_progress: MultiProgress,
10 main_progress: ProgressBar,
11 phase_progress: ProgressBar,
12 current_phase: QuantizationPhase,
13 start_time: Instant,
14 total_phases: u64,
15 completed_phases: u64,
16 quality_metrics: QualityMetrics,
17 verbosity_level: VerbosityLevel,
18}
19
20#[derive(Debug, Clone, Copy)]
21pub enum QuantizationPhase {
22 ModelLoading,
23 CodebookInitialization,
24 Level1Refinement,
25 Level2Refinement,
26 QualityValidation,
27 ModelSaving,
28 Complete,
29}
30
31#[derive(Debug, Clone, Copy)]
32pub enum VerbosityLevel {
33 Silent, Minimal, Standard, Detailed, }
38
39#[derive(Debug, Clone)]
40pub struct QualityMetrics {
41 pub mse: f32,
42 pub accuracy: f32,
43 pub compression_ratio: f32,
44 pub recovery_count: u32,
45 pub nan_issues: u32,
46 pub inf_issues: u32,
47}
48
49impl Default for QualityMetrics {
50 fn default() -> Self {
51 Self {
52 mse: 0.0,
53 accuracy: 0.0,
54 compression_ratio: 0.0,
55 recovery_count: 0,
56 nan_issues: 0,
57 inf_issues: 0,
58 }
59 }
60}
61
62impl QuantizationProgressTracker {
63 pub fn new(verbosity: VerbosityLevel) -> Self {
64 let multi_progress = MultiProgress::new();
65
66 let main_progress = multi_progress.add(ProgressBar::new(6));
68 main_progress.set_style(
69 ProgressStyle::default_bar()
70 .template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} {msg}")
71 .unwrap()
72 .progress_chars("#>-")
73 );
74 main_progress.set_message("Initializing NOVAQ Quantization");
75
76 let phase_progress = multi_progress.add(ProgressBar::new(100));
78 phase_progress.set_style(
79 ProgressStyle::default_bar()
80 .template(" └─ {spinner:.yellow} [{bar:30.yellow/dim}] {percent}% {msg}")
81 .unwrap()
82 .progress_chars("█▉▊▋▌▍▎▏ ")
83 );
84
85 Self {
86 multi_progress,
87 main_progress,
88 phase_progress,
89 current_phase: QuantizationPhase::ModelLoading,
90 start_time: Instant::now(),
91 total_phases: 6,
92 completed_phases: 0,
93 quality_metrics: QualityMetrics::default(),
94 verbosity_level: verbosity,
95 }
96 }
97
98 pub fn start_phase(&mut self, phase: QuantizationPhase, max_iterations: Option<u64>) {
100 self.current_phase = phase;
101
102 match self.verbosity_level {
103 VerbosityLevel::Silent => return,
104 _ => {}
105 }
106
107 let (phase_name, description) = self.get_phase_info(phase);
108
109 self.main_progress.set_position(self.completed_phases);
111 self.main_progress.set_message(format!("{} {}", phase_name, description));
112
113 let iterations = max_iterations.unwrap_or(100);
115 self.phase_progress.reset();
116 self.phase_progress.set_length(iterations);
117 self.phase_progress.set_message(description.to_string());
118
119 if matches!(self.verbosity_level, VerbosityLevel::Standard | VerbosityLevel::Detailed) {
120 println!("{}", Blue.bold().paint(format!("🔄 Starting: {}", phase_name)));
121 }
122 }
123
124 pub fn update_iteration(&mut self, iteration: u64, metrics: Option<&QualityMetrics>) {
126 if matches!(self.verbosity_level, VerbosityLevel::Silent) {
127 return;
128 }
129
130 self.phase_progress.set_position(iteration);
131
132 if let Some(m) = metrics {
133 self.quality_metrics = m.clone();
134
135 let message = match self.current_phase {
137 QuantizationPhase::Level1Refinement | QuantizationPhase::Level2Refinement => {
138 if m.recovery_count > 0 {
139 format!("MSE: {:.4}, Acc: {:.2}% [Recovered: {}]", m.mse, m.accuracy * 100.0, m.recovery_count)
140 } else {
141 format!("MSE: {:.4}, Accuracy: {:.2}%", m.mse, m.accuracy * 100.0)
142 }
143 },
144 _ => format!("Quality: {:.2}%", m.accuracy * 100.0),
145 };
146 self.phase_progress.set_message(message);
147 }
148 }
149
150 pub fn complete_phase(&mut self) {
152 self.completed_phases += 1;
153
154 match self.verbosity_level {
155 VerbosityLevel::Silent => return,
156 _ => {}
157 }
158
159 self.phase_progress.finish_and_clear();
160
161 let (phase_name, _) = self.get_phase_info(self.current_phase);
162 let elapsed = self.start_time.elapsed();
163
164 if matches!(self.verbosity_level, VerbosityLevel::Standard | VerbosityLevel::Detailed) {
165 let status_icon = if self.quality_metrics.recovery_count > 0 { "🛡️" } else { "✅" };
166 println!("{} Completed: {} ({})",
167 status_icon,
168 phase_name,
169 self.format_duration(elapsed));
170
171 if matches!(self.verbosity_level, VerbosityLevel::Detailed) {
172 self.print_phase_metrics();
173 }
174 }
175
176 self.main_progress.set_position(self.completed_phases);
177 }
178
179 pub fn complete(&mut self) {
181 match self.verbosity_level {
182 VerbosityLevel::Silent => return,
183 _ => {}
184 }
185
186 self.phase_progress.finish_and_clear();
187 self.main_progress.finish_and_clear();
188
189 let total_time = self.start_time.elapsed();
190
191 println!();
192 println!("{}", Green.bold().paint("🎉 NOVAQ Quantization Complete!"));
193 println!();
194
195 if matches!(self.verbosity_level, VerbosityLevel::Standard | VerbosityLevel::Detailed) {
196 println!("{}", Cyan.bold().paint("Summary:"));
197 println!(" Duration: {}", self.format_duration(total_time));
198 println!(" Final Accuracy: {:.2}%", self.quality_metrics.accuracy * 100.0);
199
200 if self.quality_metrics.compression_ratio > 0.0 {
201 println!(" Compression Ratio: {:.1}x", self.quality_metrics.compression_ratio);
202 }
203
204 if self.quality_metrics.recovery_count > 0 {
205 println!(" {} Recoveries: {} (NaN: {}, Inf: {})",
206 Yellow.paint("🛡️"),
207 self.quality_metrics.recovery_count,
208 self.quality_metrics.nan_issues,
209 self.quality_metrics.inf_issues);
210 } else {
211 println!(" {} No numerical issues detected", Green.paint("✨"));
212 }
213 println!();
214 }
215 }
216
217 pub fn error(&mut self, error_msg: &str) {
219 self.phase_progress.abandon_with_message(format!("❌ {}", error_msg));
220 self.main_progress.abandon_with_message("❌ Quantization failed");
221
222 if !matches!(self.verbosity_level, VerbosityLevel::Silent) {
223 println!("{}", Red.bold().paint(format!("❌ Error: {}", error_msg)));
224 }
225 }
226
227 pub fn set_verbosity(&mut self, level: VerbosityLevel) {
229 self.verbosity_level = level;
230 }
231
232 pub fn should_log_iteration(&self, iteration: u64) -> bool {
234 match self.verbosity_level {
235 VerbosityLevel::Silent => false,
236 VerbosityLevel::Minimal => false,
237 VerbosityLevel::Standard => iteration % 25 == 0,
238 VerbosityLevel::Detailed => iteration % 10 == 0,
239 }
240 }
241
242 fn get_phase_info(&self, phase: QuantizationPhase) -> (&str, &str) {
243 match phase {
244 QuantizationPhase::ModelLoading => ("Model Loading", "Loading and preprocessing model weights"),
245 QuantizationPhase::CodebookInitialization => ("Codebook Init", "Initializing L1 and L2 codebooks"),
246 QuantizationPhase::Level1Refinement => ("L1 Refinement", "Optimizing level-1 codebook centroids"),
247 QuantizationPhase::Level2Refinement => ("L2 Refinement", "Optimizing level-2 residual codebooks"),
248 QuantizationPhase::QualityValidation => ("Quality Check", "Validating compression quality"),
249 QuantizationPhase::ModelSaving => ("Model Saving", "Serializing compressed model"),
250 QuantizationPhase::Complete => ("Complete", "Quantization finished successfully"),
251 }
252 }
253
254 fn format_duration(&self, duration: Duration) -> String {
255 let total_secs = duration.as_secs();
256 if total_secs < 60 {
257 format!("{:.1}s", duration.as_secs_f32())
258 } else if total_secs < 3600 {
259 format!("{}m {}s", total_secs / 60, total_secs % 60)
260 } else {
261 format!("{}h {}m {}s", total_secs / 3600, (total_secs % 3600) / 60, total_secs % 60)
262 }
263 }
264
265 fn print_phase_metrics(&self) {
266 if self.quality_metrics.accuracy > 0.0 {
267 println!(" Accuracy: {:.3}%", self.quality_metrics.accuracy * 100.0);
268 }
269 if self.quality_metrics.mse > 0.0 {
270 println!(" MSE Loss: {:.6}", self.quality_metrics.mse);
271 }
272 if self.quality_metrics.recovery_count > 0 {
273 println!(" Numerical Recoveries: {}", self.quality_metrics.recovery_count);
274 }
275 }
276}
277
278pub fn create_progress_tracker() -> QuantizationProgressTracker {
280 let verbosity = match std::env::var("NOVAQ_VERBOSITY").as_deref() {
281 Ok("silent") => VerbosityLevel::Silent,
282 Ok("minimal") => VerbosityLevel::Minimal,
283 Ok("detailed") => VerbosityLevel::Detailed,
284 _ => VerbosityLevel::Standard,
285 };
286
287 QuantizationProgressTracker::new(verbosity)
288}
289
290#[cfg(test)]
291mod tests {
292 use super::*;
293
294 #[test]
295 fn test_progress_tracker_creation() {
296 let tracker = QuantizationProgressTracker::new(VerbosityLevel::Standard);
297 assert_eq!(tracker.completed_phases, 0);
298 assert_eq!(tracker.total_phases, 6);
299 }
300
301 #[test]
302 fn test_phase_transitions() {
303 let mut tracker = QuantizationProgressTracker::new(VerbosityLevel::Minimal);
304
305 tracker.start_phase(QuantizationPhase::CodebookInitialization, Some(50));
306 assert!(matches!(tracker.current_phase, QuantizationPhase::CodebookInitialization));
307
308 tracker.complete_phase();
309 assert_eq!(tracker.completed_phases, 1);
310 }
311
312 #[test]
313 fn test_metrics_update() {
314 let mut tracker = QuantizationProgressTracker::new(VerbosityLevel::Standard);
315 let metrics = QualityMetrics {
316 mse: 0.001,
317 accuracy: 0.95,
318 compression_ratio: 4.2,
319 recovery_count: 2,
320 nan_issues: 1,
321 inf_issues: 1,
322 };
323
324 tracker.update_iteration(10, Some(&metrics));
325 assert!((tracker.quality_metrics.accuracy - 0.95).abs() < f32::EPSILON);
326 assert_eq!(tracker.quality_metrics.recovery_count, 2);
327 }
328
329 #[test]
330 fn test_verbosity_levels() {
331 let silent_tracker = QuantizationProgressTracker::new(VerbosityLevel::Silent);
332 assert!(!silent_tracker.should_log_iteration(10));
333
334 let detailed_tracker = QuantizationProgressTracker::new(VerbosityLevel::Detailed);
335 assert!(detailed_tracker.should_log_iteration(10));
336 assert!(!detailed_tracker.should_log_iteration(15));
337 }
338}