Skip to content

Commit 33288f2

Browse files
committed
Add "format string" support for tensors
This comes in 2 forms: 1. Add a version of the pretty function that takes a format string as its input instead of a precision value. Also add a new "showHeader" argument to the original pretty function. - This new version of pretty let's you specify the format string that must be used to display each element. It also adds some new tensor-specific format string "tokens" that are used to control how tensors are displayed (beyond the format of each element). 2. Add a formatValue procedure that takes a tensor as its first input. This makes it possible to control how tensors are displayed in format strings, in the exact same way as if you were using the new pretty(specifier) procedure. The special, tensor-specific tokens added by this change are: - "[:]": Display the tensor as if it were a nim "array". This makes it easy to use the representation of a tensor in your own code. No header is shown. - "[]": Same as "[:]" but displays the tensor in a single line. No header is shown. - "<>": Combined with the 2 above (i.e. "<>[:]" or "<>[]") adds a header with basic tensor info (type and shape). "<:>" can be used as a shortcut for "<>[:]" while "<>" on its own is equivalent to "<>[]". Can also be combined with "<>||" (see below). - "||": "Pretty-print" the tensor without a header. This can also be combined with "<>" (i.e. "<>||") to explicitly enable the default mode, which is pretty printing with a header. - 'j': Formats complex values as (A+Bj) like in mathematics. Ignored for non Complex tensors Note that these are only used to control how the tensors themselves are displayed as a whole, and are removed before displaying the individual elements.
1 parent 58abfd1 commit 33288f2

File tree

3 files changed

+644
-39
lines changed

3 files changed

+644
-39
lines changed

src/arraymancer/tensor/display.nim

Lines changed: 152 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,160 @@
1313
# limitations under the License.
1414

1515
import ./private/p_display,
16-
./data_structure,
17-
typetraits
16+
./data_structure
17+
import std / typetraits
1818

19-
proc pretty*[T](t: Tensor[T], precision = -1): string =
20-
## Pretty-print a Tensor with the option to set a custom `precision`
21-
## for float values.
22-
var desc = t.type.name & " of shape \"" & $t.shape & "\" on backend \"" & "Cpu" & "\""
23-
if t.storage.isNil: # return useful message for uninit'd tensors instead of crashing
24-
return "Uninitialized " & $desc
25-
if t.size() == 0:
26-
return desc & "\n [] (empty)"
27-
elif t.rank == 1: # for rank 1 we want an indentation, because we have no `|`
28-
return desc & "\n " & t.prettyImpl(precision = precision)
29-
else:
30-
return desc & "\n" & t.prettyImpl(precision = precision)
19+
proc pretty*[T](t: Tensor[T], precision: int = -1, showHeader: static bool = true): string =
20+
## Pretty-print a Tensor as a "table" with a given precision and optional header
21+
##
22+
## Pretty-print a Tensor with options to set a custom `precision` for float
23+
## values and to show or hide a header describing the tensor type and shape.
24+
##
25+
## Inputs:
26+
## - Input Tensor.
27+
## - precision: The number of decimals printed (for float tensors),
28+
## _including_ the decimal point.
29+
## - showHeader: If true (the default) show a dscription header
30+
## indicating the tensor type and shape.
31+
## Result:
32+
## - A string containing a "pretty-print" representation of the Tensor.
33+
##
34+
## Examples:
35+
## ```nim
36+
## let t = arange(-2.0, 4.0).reshape(2, 3)
37+
##
38+
## echo t.pretty()
39+
## # Tensor[system.float] of shape "[2, 3]" on backend "Cpu"
40+
## # |-2 -1 0|
41+
## # |1 2 3|
42+
##
43+
## # Note that the precision counts
44+
## echo t.pretty(2)
45+
## # Tensor[system.float] of shape "[2, 3]" on backend "Cpu"
46+
## # |-2.0 -1.0 0.0|
47+
## # |1.0 2.0 3.0|
48+
## ```
49+
const specifier = if showHeader: "" else: "||"
50+
t.prettyImpl(precision = precision, specifier = specifier)
51+
52+
proc pretty*[T](t: Tensor[T], specifier: static string = ""): string =
53+
## Pretty-print a Tensor with the option to set a custom format `specifier`
54+
##
55+
## The "format specifier" is similar to those used in format strings, with
56+
## the addition of a few, tensor specific modifiers (shown below).
57+
##
58+
## Inputs:
59+
## - Input Tensor
60+
## - specifier: A format specifier similar to those used in format strings,
61+
## with the addition of a few, tensor specific modifiers which
62+
## can be combined to achieve different results:
63+
## - "[:]": Display the tensor as if it were a nim "array".
64+
## This makes it easy to use the representation of a
65+
## tensor in your own code. No header is shown.
66+
## - "[]": Same as "[:]" but displays the tensor in a single
67+
## line. No header is shown.
68+
## - "<>": Combined with the 2 above (i.e. "<>[:]" or "<>[]")
69+
## adds a header with basic tensor info (type and
70+
## shape). "<:>" can be used as a shortcut for "<>[:]"
71+
## while "<>" on its own is equivalent to "<>[]".
72+
## Can also be combined with "<>||" (see below).
73+
## - "||": "Pretty-print" the tensor _without_ a header. This
74+
## can also be combined with "<>" (i.e. "<>||") to
75+
## explicitly enable the default mode, which is pretty
76+
## printing with a header.
77+
## Notes:
78+
## - Note that in addition to these we support all of the standard format
79+
## specifiers, such as "f", "g", "+", etc (and including, in nim 2.2 and
80+
## above, the 'j' specifier for complex tensors). For a list of supported
81+
## format specifiers please check the documentation of nim's `strformat`
82+
## module.
83+
## - This version of this function does not have a `showHeader` argument
84+
## because to disable the header you must add a "n" to the format
85+
## specifier.
86+
##
87+
## Examples:
88+
## ```nim
89+
## let t_int = arange(-2, 22, 4).reshape(2, 3)
90+
##
91+
## # You can specify a format for the elements in the tensor
92+
## # Note that the default is "pretty-printing" the tensor
93+
## # _and_ showing a header describing its type and shape
94+
## echo t_int.pretty("+05X")
95+
## # Tensor[system.int] of shape "[2, 3]" on backend "Cpu"
96+
## # |-0002 +0002 +0006|
97+
## # |+000a +000e +0012|
98+
##
99+
## # The header can be disabled by using "||"
100+
## echo t_int.pretty("+05X||")
101+
## # |-0002 +0002 +0006|
102+
## # |+000a +000e +0012|
103+
##
104+
## # Use the "[:]" format specifier to print the tensor as a
105+
## # "multi-line array" _without_ a header
106+
## echo t_int.pretty("[:]")
107+
## # [[-2, 2, 6],
108+
## # [10, 14, 18]]
109+
##
110+
## # Enable the header adding "<>" (i.e. "<>[:]") or the shorter "<:>"
111+
## echo t_int.pretty("<:>")
112+
## # Tensor[int]<2,3>:
113+
## # [[-2, 2, 6],
114+
## # [10, 14, 18]]
115+
##
116+
## # The "[]" specifier is similar to "[:]" but prints on a single line
117+
## echo t_int.pretty("[]")
118+
## # Tensor[float]<2,3>:[[-2.00, -1.00, +0.00], [+1.00, +2.00, +3.00]]
119+
##
120+
## # You can also enable the header using "<>" or "<>[]"
121+
## echo t_int.pretty("<>")
122+
## # [[-2.00, -1.00, +0.00], [+1.00, +2.00, +3.00]]
123+
##
124+
## # You can combine "[]", "[:]", "<>" and "<:>" with a regular format spec:
125+
## let t_float = arange(-2.0, 22.0, 4.0).reshape(2, 3)
126+
##
127+
## echo t_float.pretty("6.2f<:>")
128+
## # Tensor[int]<2,3>:
129+
## # [[ -2.00, 2.00, 6.00],
130+
## # [ 10.00, 14.00, 18.00]]
131+
## ```
132+
t.prettyImpl(precision = -1, specifier = specifier)
31133

32134
proc `$`*[T](t: Tensor[T]): string =
33135
## Pretty-print a tensor (when using ``echo`` for example)
34136
t.pretty()
137+
138+
proc `$$`*[T](t: Tensor[T]): string =
139+
## Print the "elements" of a tensor as a multi-line array
140+
t.pretty(specifier = "<:>")
141+
142+
proc `$<>`*[T](t: Tensor[T]): string =
143+
## Print the "elements" of a tensor as a single-line array
144+
t.pretty(specifier = "<>")
145+
146+
proc formatValue*[T](result: var string, t: Tensor[T], specifier: static string) =
147+
## Standard format implementation for `Tensor`. It makes little
148+
## sense to call this directly, but it is required to exist
149+
## by the `&` macro.
150+
##
151+
## For Tensors, we add some additional specifiers which can be combined to
152+
## achieve different results:
153+
## - "[:]": Display the tensor as if it were a nim "array".
154+
## This makes it easy to use the representation of a
155+
## tensor in your own code. No header is shown.
156+
## - "[]": Same as "[:]" but displays the tensor in a single
157+
## line. No header is shown.
158+
## - "<>": Combined with the 2 above (i.e. "<>[:]" or "<>[]")
159+
## adds a header with basic tensor info (type and
160+
## shape). "<:>" can be used as a shortcut for "<>[:]"
161+
## while "<>" on its own is equivalent to "<>[]".
162+
## Can also be combined with "<>||" (see below).
163+
## - "||": "Pretty-print" the tensor _without_ a header. This
164+
## can also be combined with "<>" (i.e. "<>||") to
165+
## explicitly enable the default mode, which is pretty
166+
## printing with a header.
167+
## - 'j': Formats complex values as (A+Bj) like in mathematics.
168+
## Ignored for non Complex tensors
169+
if specifier.len == 0:
170+
result.add $t
171+
else:
172+
result.add t.pretty(specifier = specifier)

0 commit comments

Comments
 (0)