jjinx 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,179 @@
1
+ """Methods for printing nouns (arrays, atoms)."""
2
+
3
+ import itertools
4
+ import os
5
+ from typing import Sequence
6
+
7
+ import numpy as np
8
+ from jinx.execution.numpy.helpers import is_box
9
+ from jinx.vocabulary import Noun
10
+
11
+ MAX_COLS = 100
12
+
13
+
14
+ def noun_to_string(noun: Noun[np.ndarray], max_cols: int = MAX_COLS) -> str:
15
+ """Convert a noun to a string representation."""
16
+ arr = noun.implementation
17
+ rows = array_to_rows(arr, max_cols=max_cols)
18
+ return os.linesep.join(rows)
19
+
20
+
21
+ def array_to_rows(arr: np.ndarray, max_cols: int = MAX_COLS) -> list[str]:
22
+ """Convert an array to a list of strings for printing."""
23
+ arr = np.atleast_1d(arr)
24
+
25
+ if arr.size == 0:
26
+ return [""]
27
+
28
+ if is_box(arr):
29
+ return box_to_rows(arr)
30
+
31
+ if arr.shape[-1] > max_cols:
32
+ arr = arr[..., :max_cols]
33
+ append_ellipsis = True
34
+ else:
35
+ append_ellipsis = False
36
+
37
+ if np.issubdtype(arr.dtype, np.floating):
38
+ rounded = [format_float(n) for n in arr.ravel().tolist()]
39
+ arr = np.asarray(rounded).reshape(arr.shape)
40
+
41
+ elif np.issubdtype(arr.dtype, np.bool_):
42
+ arr = arr.view(np.int8)
43
+
44
+ elif (
45
+ np.issubdtype(arr.dtype, np.str_) and arr.dtype.itemsize == arr.dtype.alignment
46
+ ):
47
+ width = arr.shape[-1]
48
+ arr = arr.view(f"{arr.dtype.byteorder}{arr.dtype.kind}{width}")
49
+
50
+ arr_str = arr.astype(str)
51
+
52
+ if np.issubdtype(arr.dtype, np.number):
53
+ arr_str = np.strings.replace(arr_str, "-", "_")
54
+
55
+ lengths = np.strings.str_len(arr_str)
56
+ justify = np.max(lengths, axis=tuple(range(arr.ndim - 1)))
57
+ arr_str = np.strings.rjust(arr_str, justify)
58
+ return ndim_n_to_rows(arr_str, append_ellipsis=append_ellipsis)
59
+
60
+
61
+ def get_decimal_places(n: float) -> int:
62
+ n = abs(n)
63
+ if n < 1:
64
+ return 6
65
+ if n < 10:
66
+ return 5
67
+ if n < 100:
68
+ return 4
69
+ if n < 1000:
70
+ return 3
71
+ if n < 10000:
72
+ return 2
73
+ if n < 100000:
74
+ return 1
75
+ return 0
76
+
77
+
78
+ def format_float(n: float) -> str:
79
+ if np.isinf(n):
80
+ return "__" if n < 0 else "_"
81
+ if n.is_integer():
82
+ return f"{int(n)}"
83
+ decimal_places = get_decimal_places(n)
84
+ rounded_n = round(n, decimal_places)
85
+ return f"{rounded_n}"
86
+
87
+
88
+ def ndim_1_to_str(arr: np.ndarray, append_ellipsis: bool) -> str:
89
+ result = " ".join(arr.tolist())
90
+ if append_ellipsis:
91
+ result += " ..."
92
+ return result
93
+
94
+
95
+ def ndim_n_to_rows(arr: np.ndarray, append_ellipsis: bool) -> list[str]:
96
+ if arr.ndim == 1:
97
+ return [ndim_1_to_str(arr, append_ellipsis)]
98
+
99
+ rows = []
100
+ for n, item in enumerate(arr):
101
+ if item.ndim == 1:
102
+ rows.append(ndim_1_to_str(item, append_ellipsis))
103
+ else:
104
+ rows.extend(ndim_n_to_rows(item, append_ellipsis))
105
+ if n < len(arr) - 1:
106
+ rows.extend([""] * (arr.ndim - 2))
107
+ return rows
108
+
109
+
110
+ BatchedRowsT = Sequence[str] | Sequence["BatchedRowsT"]
111
+
112
+
113
+ def box_1D_or_2D_to_rows(box: BatchedRowsT, widths: list[int]) -> list[str]:
114
+ """Convert a 2D box (list of list of strings) to a list of strings for printing."""
115
+ rows = [box_top_line(widths=widths)]
116
+ for n, box_row in enumerate(box):
117
+ for row_item_row in itertools.zip_longest(*box_row, fillvalue=""):
118
+ vals = [str(val).ljust(width) for val, width in zip(row_item_row, widths)]
119
+ row = "│" + "│".join(vals) + "│"
120
+ rows.append(row)
121
+
122
+ if n < len(box) - 1:
123
+ rows.append(box_row_divider_line(widths=widths))
124
+
125
+ rows.append(box_bottom_line(widths=widths))
126
+ return rows
127
+
128
+
129
+ def box_to_rows(box: np.ndarray) -> list[str]:
130
+ """Convert a box to a list of strings for printing."""
131
+ box_items = [item[0] for item in box.ravel()]
132
+ items_as_rows = [array_to_rows(item) for item in box_items]
133
+
134
+ row_groups: BatchedRowsT = list(
135
+ itertools.batched(items_as_rows, box.shape[-1], strict=True)
136
+ )
137
+ if box.ndim >= 2:
138
+ row_groups = list(itertools.batched(row_groups, box.shape[-2], strict=True))
139
+
140
+ item_widths = np.array([len(rows[0]) for rows in items_as_rows]).reshape(box.shape)
141
+ widths = np.max(item_widths, axis=tuple(range(box.ndim - 1))).tolist()
142
+
143
+ if box.ndim == 1:
144
+ return box_1D_or_2D_to_rows(row_groups, widths)
145
+
146
+ if box.ndim == 2:
147
+ return box_1D_or_2D_to_rows(row_groups[0], widths)
148
+
149
+ boxes: BatchedRowsT = [box_1D_or_2D_to_rows(rows, widths) for rows in row_groups]
150
+ leading_dims = list(box.shape[:-2])
151
+
152
+ while leading_dims:
153
+ boxes = list(itertools.batched(boxes, leading_dims.pop(), strict=True))
154
+
155
+ rows = []
156
+
157
+ def flatten_to_rows(box_list, gap_size):
158
+ for n, bxs in enumerate(box_list):
159
+ if isinstance(bxs, str):
160
+ rows.append(bxs)
161
+ else:
162
+ flatten_to_rows(bxs, gap_size - 1)
163
+ if n < len(box_list) - 1:
164
+ rows.extend([""] * gap_size)
165
+
166
+ flatten_to_rows(boxes[0], box.ndim - 2)
167
+ return rows
168
+
169
+
170
+ def box_top_line(widths: list[int]) -> str:
171
+ return "┌" + "┬".join(["─" * width for width in widths]) + "┐"
172
+
173
+
174
+ def box_row_divider_line(widths: list[int]) -> str:
175
+ return "├" + "┼".join(["─" * width for width in widths]) + "┤"
176
+
177
+
178
+ def box_bottom_line(widths: list[int]) -> str:
179
+ return "└" + "┴".join(["─" * width for width in widths]) + "┘"