1use crate::{DType, Result, Tensor, WithDType};
6use half::{bf16, f16};
7
8impl Tensor {
9 fn fmt_dt<T: WithDType + std::fmt::Display>(
10 &self,
11 f: &mut std::fmt::Formatter,
12 ) -> std::fmt::Result {
13 let device_str = match self.device().location() {
14 crate::DeviceLocation::Cpu => "".to_owned(),
15 crate::DeviceLocation::Cuda { gpu_id } => {
16 format!(", cuda:{gpu_id}")
17 }
18 crate::DeviceLocation::Metal { gpu_id } => {
19 format!(", metal:{gpu_id}")
20 }
21 #[cfg(feature = "rocm")]
22 crate::DeviceLocation::Rocm { gpu_id } => {
23 format!(", rocm:{gpu_id}")
24 }
25 #[cfg(feature = "vulkan")]
26 crate::DeviceLocation::Vulkan { gpu_id } => {
27 format!(", vulkan:{gpu_id}")
28 }
29 #[cfg(feature = "wgpu")]
30 crate::DeviceLocation::Wgpu { gpu_id } => {
31 format!(", wgpu:{gpu_id}")
32 }
33 };
34
35 write!(f, "Tensor[")?;
36 match self.dims() {
37 [] => {
38 if let Ok(v) = self.to_scalar::<T>() {
39 write!(f, "{v}")?
40 }
41 }
42 [s] if *s < 10 => {
43 if let Ok(vs) = self.to_vec1::<T>() {
44 for (i, v) in vs.iter().enumerate() {
45 if i > 0 {
46 write!(f, ", ")?;
47 }
48 write!(f, "{v}")?;
49 }
50 }
51 }
52 dims => {
53 write!(f, "dims ")?;
54 for (i, d) in dims.iter().enumerate() {
55 if i > 0 {
56 write!(f, ", ")?;
57 }
58 write!(f, "{d}")?;
59 }
60 }
61 }
62 write!(f, "; {}{}]", self.dtype().as_str(), device_str)
63 }
64}
65
66impl std::fmt::Debug for Tensor {
67 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
68 match self.dtype() {
69 DType::U8 => self.fmt_dt::<u8>(f),
70 DType::U32 => self.fmt_dt::<u32>(f),
71 DType::I16 => self.fmt_dt::<i16>(f),
72 DType::I32 => self.fmt_dt::<i32>(f),
73 DType::I64 => self.fmt_dt::<i64>(f),
74 DType::BF16 => self.fmt_dt::<bf16>(f),
75 DType::F16 => self.fmt_dt::<f16>(f),
76 DType::F32 => self.fmt_dt::<f32>(f),
77 DType::F64 => self.fmt_dt::<f64>(f),
78 DType::F8E4M3 => self.fmt_dt::<float8::F8E4M3>(f),
79 DType::F6E2M3 | DType::F6E3M2 | DType::F4 | DType::F8E8M0 => {
80 write!(
81 f,
82 "Tensor[{:?}; dtype={}, unsupported dummy type]",
83 self.shape(),
84 self.dtype().as_str()
85 )
86 }
87 }
88 }
89}
90
91#[derive(Debug, Clone)]
93pub struct PrinterOptions {
94 pub precision: usize,
95 pub threshold: usize,
96 pub edge_items: usize,
97 pub line_width: usize,
98 pub sci_mode: Option<bool>,
99}
100
101static PRINT_OPTS: std::sync::Mutex<PrinterOptions> =
102 std::sync::Mutex::new(PrinterOptions::const_default());
103
104impl PrinterOptions {
105 const fn const_default() -> Self {
107 Self {
108 precision: 4,
109 threshold: 1000,
110 edge_items: 3,
111 line_width: 80,
112 sci_mode: None,
113 }
114 }
115}
116
117pub fn print_options() -> &'static std::sync::Mutex<PrinterOptions> {
118 &PRINT_OPTS
119}
120
121pub fn set_print_options(options: PrinterOptions) {
122 *PRINT_OPTS.lock().unwrap() = options
123}
124
125pub fn set_print_options_default() {
126 *PRINT_OPTS.lock().unwrap() = PrinterOptions::const_default()
127}
128
129pub fn set_print_options_short() {
130 *PRINT_OPTS.lock().unwrap() = PrinterOptions {
131 precision: 2,
132 threshold: 1000,
133 edge_items: 2,
134 line_width: 80,
135 sci_mode: None,
136 }
137}
138
139pub fn set_print_options_full() {
140 *PRINT_OPTS.lock().unwrap() = PrinterOptions {
141 precision: 4,
142 threshold: usize::MAX,
143 edge_items: 3,
144 line_width: 80,
145 sci_mode: None,
146 }
147}
148
149pub fn set_line_width(line_width: usize) {
150 PRINT_OPTS.lock().unwrap().line_width = line_width
151}
152
153pub fn set_precision(precision: usize) {
154 PRINT_OPTS.lock().unwrap().precision = precision
155}
156
157pub fn set_edge_items(edge_items: usize) {
158 PRINT_OPTS.lock().unwrap().edge_items = edge_items
159}
160
161pub fn set_threshold(threshold: usize) {
162 PRINT_OPTS.lock().unwrap().threshold = threshold
163}
164
165pub fn set_sci_mode(sci_mode: Option<bool>) {
166 PRINT_OPTS.lock().unwrap().sci_mode = sci_mode
167}
168
169struct FmtSize {
170 current_size: usize,
171}
172
173impl FmtSize {
174 fn new() -> Self {
175 Self { current_size: 0 }
176 }
177
178 fn final_size(self) -> usize {
179 self.current_size
180 }
181}
182
183impl std::fmt::Write for FmtSize {
184 fn write_str(&mut self, s: &str) -> std::fmt::Result {
185 self.current_size += s.len();
186 Ok(())
187 }
188}
189
190trait TensorFormatter {
191 type Elem: WithDType;
192
193 fn fmt<T: std::fmt::Write>(&self, v: Self::Elem, max_w: usize, f: &mut T) -> std::fmt::Result;
194
195 fn max_width(&self, to_display: &Tensor) -> usize {
196 let mut max_width = 1;
197 if let Ok(vs) = to_display.flatten_all().and_then(|t| t.to_vec1()) {
198 for &v in vs.iter() {
199 let mut fmt_size = FmtSize::new();
200 let _res = self.fmt(v, 1, &mut fmt_size);
201 max_width = usize::max(max_width, fmt_size.final_size())
202 }
203 }
204 max_width
205 }
206
207 fn write_newline_indent(i: usize, f: &mut std::fmt::Formatter) -> std::fmt::Result {
208 writeln!(f)?;
209 for _ in 0..i {
210 write!(f, " ")?
211 }
212 Ok(())
213 }
214
215 fn fmt_tensor(
216 &self,
217 t: &Tensor,
218 indent: usize,
219 max_w: usize,
220 summarize: bool,
221 po: &PrinterOptions,
222 f: &mut std::fmt::Formatter,
223 ) -> std::fmt::Result {
224 let dims = t.dims();
225 let edge_items = po.edge_items;
226 write!(f, "[")?;
227 match dims {
228 [] => {
229 if let Ok(v) = t.to_scalar::<Self::Elem>() {
230 self.fmt(v, max_w, f)?
231 }
232 }
233 [v] if summarize && *v > 2 * edge_items => {
234 if let Ok(vs) = t
235 .narrow(0, 0, edge_items)
236 .and_then(|t| t.to_vec1::<Self::Elem>())
237 {
238 for v in vs.into_iter() {
239 self.fmt(v, max_w, f)?;
240 write!(f, ", ")?;
241 }
242 }
243 write!(f, "...")?;
244 if let Ok(vs) = t
245 .narrow(0, v - edge_items, edge_items)
246 .and_then(|t| t.to_vec1::<Self::Elem>())
247 {
248 for v in vs.into_iter() {
249 write!(f, ", ")?;
250 self.fmt(v, max_w, f)?;
251 }
252 }
253 }
254 [_] => {
255 let elements_per_line = usize::max(1, po.line_width / (max_w + 2));
256 if let Ok(vs) = t.to_vec1::<Self::Elem>() {
257 for (i, v) in vs.into_iter().enumerate() {
258 if i > 0 {
259 if i % elements_per_line == 0 {
260 write!(f, ",")?;
261 Self::write_newline_indent(indent, f)?
262 } else {
263 write!(f, ", ")?;
264 }
265 }
266 self.fmt(v, max_w, f)?
267 }
268 }
269 }
270 _ => {
271 if summarize && dims[0] > 2 * edge_items {
272 for i in 0..edge_items {
273 match t.get(i) {
274 Ok(t) => self.fmt_tensor(&t, indent + 1, max_w, summarize, po, f)?,
275 Err(e) => write!(f, "{e:?}")?,
276 }
277 write!(f, ",")?;
278 Self::write_newline_indent(indent, f)?
279 }
280 write!(f, "...")?;
281 Self::write_newline_indent(indent, f)?;
282 for i in dims[0] - edge_items..dims[0] {
283 match t.get(i) {
284 Ok(t) => self.fmt_tensor(&t, indent + 1, max_w, summarize, po, f)?,
285 Err(e) => write!(f, "{e:?}")?,
286 }
287 if i + 1 != dims[0] {
288 write!(f, ",")?;
289 Self::write_newline_indent(indent, f)?
290 }
291 }
292 } else {
293 for i in 0..dims[0] {
294 match t.get(i) {
295 Ok(t) => self.fmt_tensor(&t, indent + 1, max_w, summarize, po, f)?,
296 Err(e) => write!(f, "{e:?}")?,
297 }
298 if i + 1 != dims[0] {
299 write!(f, ",")?;
300 Self::write_newline_indent(indent, f)?
301 }
302 }
303 }
304 }
305 }
306 write!(f, "]")?;
307 Ok(())
308 }
309}
310
311struct FloatFormatter<S: WithDType> {
312 int_mode: bool,
313 sci_mode: bool,
314 precision: usize,
315 _phantom: std::marker::PhantomData<S>,
316}
317
318impl<S> FloatFormatter<S>
319where
320 S: WithDType + num_traits::Float + std::fmt::Display,
321{
322 fn new(t: &Tensor, po: &PrinterOptions) -> Result<Self> {
323 let mut int_mode = true;
324 let mut sci_mode = false;
325
326 let values = t
329 .flatten_all()?
330 .to_vec1()?
331 .into_iter()
332 .filter(|v: &S| v.is_finite() && !v.is_zero())
333 .collect::<Vec<_>>();
334 if !values.is_empty() {
335 let mut nonzero_finite_min = S::max_value();
336 let mut nonzero_finite_max = S::min_value();
337 for &v in values.iter() {
338 let v = v.abs();
339 if v < nonzero_finite_min {
340 nonzero_finite_min = v
341 }
342 if v > nonzero_finite_max {
343 nonzero_finite_max = v
344 }
345 }
346
347 for &value in values.iter() {
348 if value.ceil() != value {
349 int_mode = false;
350 break;
351 }
352 }
353 if let Some(v1) = S::from(1000.) {
354 if let Some(v2) = S::from(1e8) {
355 if let Some(v3) = S::from(1e-4) {
356 sci_mode = nonzero_finite_max / nonzero_finite_min > v1
357 || nonzero_finite_max > v2
358 || nonzero_finite_min < v3
359 }
360 }
361 }
362 }
363
364 match po.sci_mode {
365 None => {}
366 Some(v) => sci_mode = v,
367 }
368 Ok(Self {
369 int_mode,
370 sci_mode,
371 precision: po.precision,
372 _phantom: std::marker::PhantomData,
373 })
374 }
375}
376
377impl<S> TensorFormatter for FloatFormatter<S>
378where
379 S: WithDType + num_traits::Float + std::fmt::Display + std::fmt::LowerExp,
380{
381 type Elem = S;
382
383 fn fmt<T: std::fmt::Write>(&self, v: Self::Elem, max_w: usize, f: &mut T) -> std::fmt::Result {
384 if self.sci_mode {
385 write!(
386 f,
387 "{v:width$.prec$e}",
388 v = v,
389 width = max_w,
390 prec = self.precision
391 )
392 } else if self.int_mode {
393 if v.is_finite() {
394 write!(f, "{v:width$.0}.", v = v, width = max_w - 1)
395 } else {
396 write!(f, "{v:max_w$.0}")
397 }
398 } else {
399 write!(
400 f,
401 "{v:width$.prec$}",
402 v = v,
403 width = max_w,
404 prec = self.precision
405 )
406 }
407 }
408}
409
410struct IntFormatter<S: WithDType> {
411 _phantom: std::marker::PhantomData<S>,
412}
413
414impl<S: WithDType> IntFormatter<S> {
415 fn new() -> Self {
416 Self {
417 _phantom: std::marker::PhantomData,
418 }
419 }
420}
421
422impl<S> TensorFormatter for IntFormatter<S>
423where
424 S: WithDType + std::fmt::Display,
425{
426 type Elem = S;
427
428 fn fmt<T: std::fmt::Write>(&self, v: Self::Elem, max_w: usize, f: &mut T) -> std::fmt::Result {
429 write!(f, "{v:max_w$}")
430 }
431}
432
433fn get_summarized_data(t: &Tensor, edge_items: usize) -> Result<Tensor> {
434 let dims = t.dims();
435 if dims.is_empty() {
436 Ok(t.clone())
437 } else if dims.len() == 1 {
438 if dims[0] > 2 * edge_items {
439 Tensor::cat(
440 &[
441 t.narrow(0, 0, edge_items)?,
442 t.narrow(0, dims[0] - edge_items, edge_items)?,
443 ],
444 0,
445 )
446 } else {
447 Ok(t.clone())
448 }
449 } else if dims[0] > 2 * edge_items {
450 let mut vs: Vec<_> = (0..edge_items)
451 .map(|i| get_summarized_data(&t.get(i)?, edge_items))
452 .collect::<Result<Vec<_>>>()?;
453 for i in (dims[0] - edge_items)..dims[0] {
454 vs.push(get_summarized_data(&t.get(i)?, edge_items)?)
455 }
456 Tensor::cat(&vs, 0)
457 } else {
458 let vs: Vec<_> = (0..dims[0])
459 .map(|i| get_summarized_data(&t.get(i)?, edge_items))
460 .collect::<Result<Vec<_>>>()?;
461 Tensor::cat(&vs, 0)
462 }
463}
464
465impl std::fmt::Display for Tensor {
466 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
467 let po = PRINT_OPTS.lock().unwrap();
468 let summarize = self.elem_count() > po.threshold;
469 let to_display = if summarize {
470 match get_summarized_data(self, po.edge_items) {
471 Ok(v) => v,
472 Err(err) => return write!(f, "{err:?}"),
473 }
474 } else {
475 self.clone()
476 };
477 match self.dtype() {
478 DType::U8 => {
479 let tf: IntFormatter<u8> = IntFormatter::new();
480 let max_w = tf.max_width(&to_display);
481 tf.fmt_tensor(self, 1, max_w, summarize, &po, f)?;
482 writeln!(f)?;
483 }
484 DType::U32 => {
485 let tf: IntFormatter<u32> = IntFormatter::new();
486 let max_w = tf.max_width(&to_display);
487 tf.fmt_tensor(self, 1, max_w, summarize, &po, f)?;
488 writeln!(f)?;
489 }
490 DType::I16 => {
491 let tf: IntFormatter<i16> = IntFormatter::new();
492 let max_w = tf.max_width(&to_display);
493 tf.fmt_tensor(self, 1, max_w, summarize, &po, f)?;
494 writeln!(f)?;
495 }
496 DType::I32 => {
497 let tf: IntFormatter<i32> = IntFormatter::new();
498 let max_w = tf.max_width(&to_display);
499 tf.fmt_tensor(self, 1, max_w, summarize, &po, f)?;
500 writeln!(f)?;
501 }
502 DType::I64 => {
503 let tf: IntFormatter<i64> = IntFormatter::new();
504 let max_w = tf.max_width(&to_display);
505 tf.fmt_tensor(self, 1, max_w, summarize, &po, f)?;
506 writeln!(f)?;
507 }
508 DType::BF16 => {
509 if let Ok(tf) = FloatFormatter::<bf16>::new(&to_display, &po) {
510 let max_w = tf.max_width(&to_display);
511 tf.fmt_tensor(self, 1, max_w, summarize, &po, f)?;
512 writeln!(f)?;
513 }
514 }
515 DType::F16 => {
516 if let Ok(tf) = FloatFormatter::<f16>::new(&to_display, &po) {
517 let max_w = tf.max_width(&to_display);
518 tf.fmt_tensor(self, 1, max_w, summarize, &po, f)?;
519 writeln!(f)?;
520 }
521 }
522 DType::F64 => {
523 if let Ok(tf) = FloatFormatter::<f64>::new(&to_display, &po) {
524 let max_w = tf.max_width(&to_display);
525 tf.fmt_tensor(self, 1, max_w, summarize, &po, f)?;
526 writeln!(f)?;
527 }
528 }
529 DType::F32 => {
530 if let Ok(tf) = FloatFormatter::<f32>::new(&to_display, &po) {
531 let max_w = tf.max_width(&to_display);
532 tf.fmt_tensor(self, 1, max_w, summarize, &po, f)?;
533 writeln!(f)?;
534 }
535 }
536 DType::F8E4M3 => {
537 if let Ok(tf) = FloatFormatter::<float8::F8E4M3>::new(&to_display, &po) {
538 let max_w = tf.max_width(&to_display);
539 tf.fmt_tensor(self, 1, max_w, summarize, &po, f)?;
540 writeln!(f)?;
541 }
542 }
543 DType::F6E2M3 | DType::F6E3M2 | DType::F4 | DType::F8E8M0 => {
544 writeln!(
545 f,
546 "Dummy type {} (not supported for display)",
547 self.dtype().as_str()
548 )?;
549 }
550 };
551
552 let device_str = match self.device().location() {
553 crate::DeviceLocation::Cpu => "".to_owned(),
554 crate::DeviceLocation::Cuda { gpu_id } => {
555 format!(", cuda:{gpu_id}")
556 }
557 crate::DeviceLocation::Metal { gpu_id } => {
558 format!(", metal:{gpu_id}")
559 }
560 #[cfg(feature = "rocm")]
561 crate::DeviceLocation::Rocm { gpu_id } => {
562 format!(", rocm:{gpu_id}")
563 }
564 #[cfg(feature = "vulkan")]
565 crate::DeviceLocation::Vulkan { gpu_id } => {
566 format!(", vulkan:{gpu_id}")
567 }
568 #[cfg(feature = "wgpu")]
569 crate::DeviceLocation::Wgpu { gpu_id } => {
570 format!(", wgpu:{gpu_id}")
571 }
572 };
573
574 write!(
575 f,
576 "Tensor[{:?}, {}{}]",
577 self.dims(),
578 self.dtype().as_str(),
579 device_str
580 )
581 }
582}