xref: /aosp_15_r20/external/tensorflow/tensorflow/python/debug/cli/tensor_format_test.py (revision b6fb3261f9314811a0f4371741dbb8839866f948)
1# Copyright 2016 The TensorFlow Authors. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14# ==============================================================================
15"""Unit tests for tensor formatter."""
16import re
17
18import numpy as np
19
20from tensorflow.core.framework import tensor_pb2
21from tensorflow.core.framework import tensor_shape_pb2
22from tensorflow.core.framework import types_pb2
23from tensorflow.python.debug.cli import cli_test_utils
24from tensorflow.python.debug.cli import tensor_format
25from tensorflow.python.debug.lib import debug_data
26from tensorflow.python.framework import test_util
27from tensorflow.python.platform import googletest
28
29
30class RichTextLinesTest(test_util.TensorFlowTestCase):
31
32  def setUp(self):
33    np.set_printoptions(
34        precision=8, threshold=1000, edgeitems=3, linewidth=75)
35
36  def _checkTensorMetadata(self, tensor, annotations):
37    self.assertEqual(
38        {"dtype": tensor.dtype, "shape": tensor.shape},
39        annotations["tensor_metadata"])
40
41  # Regular expression for text representation of float numbers, possibly in
42  # engineering notation.
43  _ELEMENT_REGEX = re.compile(
44      r"([+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?|nan|inf|-inf)")
45
46  def _checkBeginIndicesAnnotations(self, out, a):
47    """Check the beginning-index annotations of an ndarray representation.
48
49    Args:
50      out: An instance of RichTextLines representing a numpy.ndarray.
51      a: The numpy.ndarray being represented.
52
53    Raises:
54      ValueError: if any ellipses ("...") are found in the lines representing
55        the array.
56    """
57    begin_line_num = 0
58    while not out.lines[begin_line_num].startswith("array"):
59      begin_line_num += 1
60    element_index = 0
61    for line_num in range(begin_line_num, len(out.lines)):
62      line = out.lines[line_num]
63      if "..." in line:
64        raise ValueError("Unexpected found ellipses in line representing array")
65      matches = re.finditer(self._ELEMENT_REGEX, line)
66      for line_item_index, _ in enumerate(matches):
67        subscripts = list(np.unravel_index(element_index, a.shape))
68        if line_item_index == 0:
69          self.assertEqual({tensor_format.BEGIN_INDICES_KEY: subscripts},
70                           out.annotations[line_num])
71        element_index += 1
72    self.assertEqual(element_index, np.size(a))
73
74  def _checkTensorElementLocations(self, out, a):
75    """Check the results of locate_tensor_element on an ndarray representation.
76
77    that represents a numpy.ndarray.
78
79    Args:
80      out: An instance of RichTextLines representing a numpy.ndarray.
81      a: The numpy.ndarray being represented.
82
83    Raises:
84      ValueError: if any ellipses ("...") are found in the lines representing
85        the array.
86    """
87    # First, locate the beginning of the tensor value section.
88    begin_line_num = 0
89    while not out.lines[begin_line_num].startswith("array"):
90      begin_line_num += 1
91    # Second, find all matches to tensor-value regex.
92    element_index = 0
93    for line_num in range(begin_line_num, len(out.lines)):
94      line = out.lines[line_num]
95      if "..." in line:
96        raise ValueError("Unexpected found ellipses in line representing array")
97      matches = re.finditer(self._ELEMENT_REGEX, line)
98      for match in matches:
99        subscripts = list(np.unravel_index(element_index, a.shape))
100        is_omitted, row, start_col, end_col = (
101            tensor_format.locate_tensor_element(out, subscripts))
102        self.assertFalse(is_omitted)
103        self.assertEqual(line_num, row)
104        self.assertEqual(match.start(), start_col)
105        self.assertEqual(match.end(), end_col)
106        element_index += 1
107    self.assertEqual(element_index, np.size(a))
108
109  def _findFirst(self, lines, string):
110    """Find first occurrence of a string in a list of strings."""
111    for i, line in enumerate(lines):
112      find_index = line.find(string)
113      if find_index >= 0:
114        return i, find_index
115
116  def _extractBoldNumbers(self, out, start_line):
117    """Extract all numbers that have the bold font attribute.
118
119    Args:
120      out: An instance of RichTextLines.
121      start_line: 0-based index to start from.
122
123    Returns:
124      A list of floats.
125    """
126    floats = []
127    for i in range(start_line, len(out.lines)):
128      if i not in out.font_attr_segs:
129        continue
130      line_attrs = out.font_attr_segs[i]
131      for begin, end, attr_value in line_attrs:
132        if attr_value == "bold":
133          floats.append(float(out.lines[i][begin:end]))
134    return floats
135
136  def testFormatZeroDimensionTensor(self):
137    a = np.array(42, dtype=np.int32)
138
139    out = tensor_format.format_tensor(a, "a")
140
141    cli_test_utils.assert_lines_equal_ignoring_whitespace(
142        self, ["Tensor \"a\":", ""], out.lines[:2])
143    self.assertTrue(out.lines[2].startswith("array(42"))
144    self._checkTensorMetadata(a, out.annotations)
145
146  def testFormatTensorHighlightsTensorNameWithoutDebugOp(self):
147    tensor_name = "a_tensor:0"
148    a = np.zeros(2)
149    out = tensor_format.format_tensor(
150        a, tensor_name, np_printoptions={"linewidth": 40})
151    self.assertEqual([(8, 8 + len(tensor_name), "bold")], out.font_attr_segs[0])
152
153  def testFormatTensorHighlightsTensorNameWithDebugOp(self):
154    tensor_name = "a_tensor:0"
155    debug_op = "DebugIdentity"
156    a = np.zeros(2)
157    out = tensor_format.format_tensor(
158        a, "%s:%s" % (tensor_name, debug_op), np_printoptions={"linewidth": 40})
159    self.assertEqual([(8, 8 + len(tensor_name), "bold"),
160                      (8 + len(tensor_name) + 1,
161                       8 + len(tensor_name) + 1 + len(debug_op), "yellow")],
162                     out.font_attr_segs[0])
163
164  def testFormatTensor1DNoEllipsis(self):
165    a = np.zeros(20)
166
167    out = tensor_format.format_tensor(
168        a, "a", np_printoptions={"linewidth": 40})
169
170    cli_test_utils.assert_lines_equal_ignoring_whitespace(
171        self, ["Tensor \"a\":", ""], out.lines[:2])
172    self.assertEqual(repr(a).split("\n"), out.lines[2:])
173
174    self._checkTensorMetadata(a, out.annotations)
175
176    # Check annotations for beginning indices of the lines.
177    self._checkBeginIndicesAnnotations(out, a)
178
179  def testFormatTensor2DNoEllipsisNoRowBreak(self):
180    a = np.linspace(0.0, 1.0 - 1.0 / 16.0, 16).reshape([4, 4])
181
182    out = tensor_format.format_tensor(a, "a")
183
184    cli_test_utils.assert_lines_equal_ignoring_whitespace(
185        self, ["Tensor \"a\":", ""], out.lines[:2])
186    self.assertEqual(repr(a).split("\n"), out.lines[2:])
187
188    self._checkTensorMetadata(a, out.annotations)
189    self._checkBeginIndicesAnnotations(out, a)
190
191  def testFormatTensorSuppressingTensorName(self):
192    a = np.linspace(0.0, 1.0 - 1.0 / 16.0, 16).reshape([4, 4])
193
194    out = tensor_format.format_tensor(a, None)
195    self.assertEqual(repr(a).split("\n"), out.lines)
196
197    self._checkTensorMetadata(a, out.annotations)
198    self._checkBeginIndicesAnnotations(out, a)
199
200  def testFormatTensorWithMetadata(self):
201    a = np.linspace(0.0, 1.0 - 1.0 / 16.0, 16).reshape([4, 4])
202
203    out = tensor_format.format_tensor(a, "a", include_metadata=True)
204
205    cli_test_utils.assert_lines_equal_ignoring_whitespace(
206        self,
207        ["Tensor \"a\":",
208         "  dtype: float64",
209         "  shape: (4, 4)",
210         ""], out.lines[:4])
211    self.assertEqual(repr(a).split("\n"), out.lines[4:])
212
213    self._checkTensorMetadata(a, out.annotations)
214    self._checkBeginIndicesAnnotations(out, a)
215
216  def testFormatTensor2DNoEllipsisWithRowBreak(self):
217    a = np.linspace(0.0, 1.0 - 1.0 / 40.0, 40).reshape([2, 20])
218
219    out = tensor_format.format_tensor(
220        a, "a", np_printoptions={"linewidth": 50})
221
222    self.assertEqual(
223        {"dtype": a.dtype, "shape": a.shape},
224        out.annotations["tensor_metadata"])
225
226    cli_test_utils.assert_lines_equal_ignoring_whitespace(
227        self, ["Tensor \"a\":", ""], out.lines[:2])
228    self.assertEqual(repr(a).split("\n"), out.lines[2:])
229
230    self._checkTensorMetadata(a, out.annotations)
231
232    # Check annotations for the beginning indices of the lines.
233    self._checkBeginIndicesAnnotations(out, a)
234
235  def testFormatTensor3DNoEllipsis(self):
236    a = np.linspace(0.0, 1.0 - 1.0 / 24.0, 24).reshape([2, 3, 4])
237
238    out = tensor_format.format_tensor(a, "a")
239
240    cli_test_utils.assert_lines_equal_ignoring_whitespace(
241        self, ["Tensor \"a\":", ""], out.lines[:2])
242    self.assertEqual(repr(a).split("\n"), out.lines[2:])
243
244    self._checkTensorMetadata(a, out.annotations)
245    self._checkBeginIndicesAnnotations(out, a)
246
247  def testFormatTensor3DNoEllipsisWithArgwhereHighlightWithMatches(self):
248    a = np.linspace(0.0, 1.0 - 1.0 / 24.0, 24).reshape([2, 3, 4])
249
250    lower_bound = 0.26
251    upper_bound = 0.5
252
253    def highlight_filter(x):
254      return np.logical_and(x > lower_bound, x < upper_bound)
255
256    highlight_options = tensor_format.HighlightOptions(
257        highlight_filter, description="between 0.26 and 0.5")
258    out = tensor_format.format_tensor(
259        a, "a", highlight_options=highlight_options)
260
261    cli_test_utils.assert_lines_equal_ignoring_whitespace(
262        self,
263        ["Tensor \"a\": "
264         "Highlighted(between 0.26 and 0.5): 5 of 24 element(s) (20.83%)",
265         ""],
266        out.lines[:2])
267    self.assertEqual(repr(a).split("\n"), out.lines[2:])
268
269    self._checkTensorMetadata(a, out.annotations)
270
271    # Check annotations for beginning indices of the lines.
272    self._checkBeginIndicesAnnotations(out, a)
273
274    self.assertAllClose(
275        [0.29166667, 0.33333333, 0.375, 0.41666667, 0.45833333],
276        self._extractBoldNumbers(out, 2))
277
278  def testFormatTensor3DNoEllipsisWithArgwhereHighlightWithNoMatches(self):
279    a = np.linspace(0.0, 1.0 - 1.0 / 24.0, 24).reshape([2, 3, 4])
280
281    def highlight_filter(x):
282      return x > 10.0
283
284    highlight_options = tensor_format.HighlightOptions(highlight_filter)
285    out = tensor_format.format_tensor(
286        a, "a", highlight_options=highlight_options)
287
288    cli_test_utils.assert_lines_equal_ignoring_whitespace(
289        self,
290        ["Tensor \"a\": Highlighted: 0 of 24 element(s) (0.00%)", ""],
291        out.lines[:2])
292    self.assertEqual(repr(a).split("\n"), out.lines[2:])
293
294    self._checkTensorMetadata(a, out.annotations)
295    self._checkBeginIndicesAnnotations(out, a)
296
297    # Check font attribute segments for highlighted elements.
298    for i in range(2, len(out.lines)):
299      self.assertNotIn(i, out.font_attr_segs)
300
301  def testFormatTensorWithEllipses(self):
302    a = (np.arange(11 * 11 * 11) + 1000).reshape([11, 11, 11]).astype(np.int32)
303
304    out = tensor_format.format_tensor(
305        a, "a", False, np_printoptions={"threshold": 100, "edgeitems": 2})
306
307    cli_test_utils.assert_lines_equal_ignoring_whitespace(
308        self, ["Tensor \"a\":", ""], out.lines[:2])
309    self.assertEqual(repr(a).split("\n"), out.lines[2:])
310
311    self._checkTensorMetadata(a, out.annotations)
312
313    # Check annotations for beginning indices of the lines.
314    actual_row_0_0_0, _ = self._findFirst(out.lines, "1000")
315    self.assertEqual({tensor_format.BEGIN_INDICES_KEY: [0, 0, 0]},
316                     out.annotations[actual_row_0_0_0])
317    actual_row_0_1_0, _ = self._findFirst(out.lines, "1011")
318    self.assertEqual({tensor_format.BEGIN_INDICES_KEY: [0, 1, 0]},
319                     out.annotations[actual_row_0_1_0])
320    # Find the first line that is completely omitted.
321    omitted_line = 2
322    while not out.lines[omitted_line].strip().startswith("..."):
323      omitted_line += 1
324    self.assertEqual({tensor_format.OMITTED_INDICES_KEY: [0, 2, 0]},
325                     out.annotations[omitted_line])
326
327    actual_row_10_10_0, _ = self._findFirst(out.lines, "2320")
328    self.assertEqual({tensor_format.BEGIN_INDICES_KEY: [10, 10, 0]},
329                     out.annotations[actual_row_10_10_0])
330    # Find the last line that is completely omitted.
331    omitted_line = len(out.lines) - 1
332    while not out.lines[omitted_line].strip().startswith("..."):
333      omitted_line -= 1
334    self.assertEqual({tensor_format.OMITTED_INDICES_KEY: [10, 2, 0]},
335                     out.annotations[omitted_line])
336
337  def testFormatUninitializedTensor(self):
338    tensor_proto = tensor_pb2.TensorProto(
339        dtype=types_pb2.DataType.Value("DT_FLOAT"),
340        tensor_shape=tensor_shape_pb2.TensorShapeProto(
341            dim=[tensor_shape_pb2.TensorShapeProto.Dim(size=1)]))
342    out = tensor_format.format_tensor(
343        debug_data.InconvertibleTensorProto(tensor_proto, False), "a")
344
345    self.assertEqual(["Tensor \"a\":", "", "Uninitialized tensor:"],
346                     out.lines[:3])
347    self.assertEqual(str(tensor_proto).split("\n"), out.lines[3:])
348
349  def testFormatResourceTypeTensor(self):
350    tensor_proto = tensor_pb2.TensorProto(
351        dtype=types_pb2.DataType.Value("DT_RESOURCE"),
352        tensor_shape=tensor_shape_pb2.TensorShapeProto(
353            dim=[tensor_shape_pb2.TensorShapeProto.Dim(size=1)]))
354    out = tensor_format.format_tensor(
355        debug_data.InconvertibleTensorProto(tensor_proto), "a")
356
357    self.assertEqual(["Tensor \"a\":", ""], out.lines[:2])
358    self.assertEqual(str(tensor_proto).split("\n"), out.lines[2:])
359
360  def testLocateTensorElement1DNoEllipsis(self):
361    a = np.zeros(20)
362
363    out = tensor_format.format_tensor(
364        a, "a", np_printoptions={"linewidth": 40})
365
366    cli_test_utils.assert_lines_equal_ignoring_whitespace(
367        self, ["Tensor \"a\":", ""], out.lines[:2])
368    self.assertEqual(repr(a).split("\n"), out.lines[2:])
369
370    self._checkTensorElementLocations(out, a)
371
372    with self.assertRaisesRegex(ValueError, "Indices exceed tensor dimensions"):
373      tensor_format.locate_tensor_element(out, [20])
374
375    with self.assertRaisesRegex(ValueError, "Indices contain negative"):
376      tensor_format.locate_tensor_element(out, [-1])
377
378    with self.assertRaisesRegex(ValueError, "Dimensions mismatch"):
379      tensor_format.locate_tensor_element(out, [0, 0])
380
381  def testLocateTensorElement1DNoEllipsisBatchMode(self):
382    a = np.zeros(20)
383
384    out = tensor_format.format_tensor(
385        a, "a", np_printoptions={"linewidth": 40})
386
387    cli_test_utils.assert_lines_equal_ignoring_whitespace(
388        self, ["Tensor \"a\":", ""], out.lines[:2])
389    self.assertEqual(repr(a).split("\n"), out.lines[2:])
390
391    self._checkTensorElementLocations(out, a)
392
393  def testBatchModeWithErrors(self):
394    a = np.zeros(20)
395
396    out = tensor_format.format_tensor(
397        a, "a", np_printoptions={"linewidth": 40})
398
399    cli_test_utils.assert_lines_equal_ignoring_whitespace(
400        self, ["Tensor \"a\":", ""], out.lines[:2])
401    self.assertEqual(repr(a).split("\n"), out.lines[2:])
402
403    with self.assertRaisesRegex(ValueError, "Dimensions mismatch"):
404      tensor_format.locate_tensor_element(out, [[0, 0], [0]])
405
406    with self.assertRaisesRegex(ValueError, "Indices exceed tensor dimensions"):
407      tensor_format.locate_tensor_element(out, [[0], [20]])
408
409    with self.assertRaisesRegex(ValueError,
410                                r"Indices contain negative value\(s\)"):
411      tensor_format.locate_tensor_element(out, [[0], [-1]])
412
413    with self.assertRaisesRegex(
414        ValueError, "Input indices sets are not in ascending order"):
415      tensor_format.locate_tensor_element(out, [[5], [0]])
416
417  def testLocateTensorElement1DTinyAndNanValues(self):
418    a = np.ones([3, 3]) * 1e-8
419    a[1, 0] = np.nan
420    a[1, 2] = np.inf
421
422    out = tensor_format.format_tensor(
423        a, "a", np_printoptions={"linewidth": 100})
424
425    cli_test_utils.assert_lines_equal_ignoring_whitespace(
426        self, ["Tensor \"a\":", ""], out.lines[:2])
427    self.assertEqual(repr(a).split("\n"), out.lines[2:])
428
429    self._checkTensorElementLocations(out, a)
430
431  def testLocateTensorElement2DNoEllipsis(self):
432    a = np.linspace(0.0, 1.0 - 1.0 / 16.0, 16).reshape([4, 4])
433
434    out = tensor_format.format_tensor(a, "a")
435
436    cli_test_utils.assert_lines_equal_ignoring_whitespace(
437        self, ["Tensor \"a\":", ""], out.lines[:2])
438    self.assertEqual(repr(a).split("\n"), out.lines[2:])
439
440    self._checkTensorElementLocations(out, a)
441
442    with self.assertRaisesRegex(ValueError, "Indices exceed tensor dimensions"):
443      tensor_format.locate_tensor_element(out, [1, 4])
444
445    with self.assertRaisesRegex(ValueError, "Indices contain negative"):
446      tensor_format.locate_tensor_element(out, [-1, 2])
447
448    with self.assertRaisesRegex(ValueError, "Dimensions mismatch"):
449      tensor_format.locate_tensor_element(out, [0])
450
451  def testLocateTensorElement2DNoEllipsisWithNumericSummary(self):
452    a = np.linspace(0.0, 1.0 - 1.0 / 16.0, 16).reshape([4, 4])
453
454    out = tensor_format.format_tensor(a, "a", include_numeric_summary=True)
455
456    cli_test_utils.assert_lines_equal_ignoring_whitespace(
457        self,
458        ["Tensor \"a\":",
459         "",
460         "Numeric summary:",
461         "|  0  + | total |",
462         "|  1 15 |    16 |",
463         "|           min           max          mean           std |"],
464        out.lines[:6])
465    cli_test_utils.assert_array_lines_close(
466        self, [0.0, 0.9375, 0.46875, 0.28811076429], out.lines[6:7])
467    cli_test_utils.assert_array_lines_close(self, a, out.lines[8:])
468
469    self._checkTensorElementLocations(out, a)
470
471    with self.assertRaisesRegex(ValueError, "Indices exceed tensor dimensions"):
472      tensor_format.locate_tensor_element(out, [1, 4])
473
474    with self.assertRaisesRegex(ValueError, "Indices contain negative"):
475      tensor_format.locate_tensor_element(out, [-1, 2])
476
477    with self.assertRaisesRegex(ValueError, "Dimensions mismatch"):
478      tensor_format.locate_tensor_element(out, [0])
479
480  def testLocateTensorElement3DWithEllipses(self):
481    a = (np.arange(11 * 11 * 11) + 1000).reshape([11, 11, 11]).astype(np.int32)
482
483    out = tensor_format.format_tensor(
484        a, "a", False, np_printoptions={"threshold": 100, "edgeitems": 2})
485
486    cli_test_utils.assert_lines_equal_ignoring_whitespace(
487        self, ["Tensor \"a\":", ""], out.lines[:2])
488
489    actual_row_0_0_0, actual_col_0_0_0 = self._findFirst(out.lines, "1000")
490    is_omitted, row, start_col, end_col = tensor_format.locate_tensor_element(
491        out, [0, 0, 0])
492    self.assertFalse(is_omitted)
493    self.assertEqual(actual_row_0_0_0, row)
494    self.assertEqual(actual_col_0_0_0, start_col)
495    self.assertEqual(actual_col_0_0_0 + 4, end_col)
496
497    actual_row_0_0_10, _ = self._findFirst(out.lines, "1010")
498    is_omitted, row, start_col, end_col = tensor_format.locate_tensor_element(
499        out, [0, 0, 10])
500    self.assertFalse(is_omitted)
501    self.assertEqual(actual_row_0_0_10, row)
502    self.assertIsNone(start_col)  # Passes ellipsis.
503    self.assertIsNone(end_col)
504
505    actual_row_0_1_0, actual_col_0_1_0 = self._findFirst(out.lines, "1011")
506    is_omitted, row, start_col, end_col = tensor_format.locate_tensor_element(
507        out, [0, 1, 0])
508    self.assertFalse(is_omitted)
509    self.assertEqual(actual_row_0_1_0, row)
510    self.assertEqual(actual_col_0_1_0, start_col)
511    self.assertEqual(actual_col_0_1_0 + 4, end_col)
512
513    is_omitted, row, start_col, end_col = tensor_format.locate_tensor_element(
514        out, [0, 2, 0])
515    self.assertTrue(is_omitted)  # In omitted line.
516    self.assertIsNone(start_col)
517    self.assertIsNone(end_col)
518
519    is_omitted, row, start_col, end_col = tensor_format.locate_tensor_element(
520        out, [0, 2, 10])
521    self.assertTrue(is_omitted)  # In omitted line.
522    self.assertIsNone(start_col)
523    self.assertIsNone(end_col)
524
525    is_omitted, row, start_col, end_col = tensor_format.locate_tensor_element(
526        out, [0, 8, 10])
527    self.assertTrue(is_omitted)  # In omitted line.
528    self.assertIsNone(start_col)
529    self.assertIsNone(end_col)
530
531    actual_row_0_10_1, actual_col_0_10_1 = self._findFirst(out.lines, "1111")
532    is_omitted, row, start_col, end_col = tensor_format.locate_tensor_element(
533        out, [0, 10, 1])
534    self.assertFalse(is_omitted)
535    self.assertEqual(actual_row_0_10_1, row)
536    self.assertEqual(actual_col_0_10_1, start_col)
537    self.assertEqual(actual_col_0_10_1 + 4, end_col)
538
539    is_omitted, row, start_col, end_col = tensor_format.locate_tensor_element(
540        out, [5, 1, 1])
541    self.assertTrue(is_omitted)  # In omitted line.
542    self.assertIsNone(start_col)
543    self.assertIsNone(end_col)
544
545    actual_row_10_10_10, _ = self._findFirst(out.lines, "2330")
546    is_omitted, row, start_col, end_col = tensor_format.locate_tensor_element(
547        out, [10, 10, 10])
548    self.assertFalse(is_omitted)
549    self.assertEqual(actual_row_10_10_10, row)
550    self.assertIsNone(start_col)  # Past ellipsis.
551    self.assertIsNone(end_col)
552
553    with self.assertRaisesRegex(ValueError, "Indices exceed tensor dimensions"):
554      tensor_format.locate_tensor_element(out, [11, 5, 5])
555
556    with self.assertRaisesRegex(ValueError, "Indices contain negative"):
557      tensor_format.locate_tensor_element(out, [-1, 5, 5])
558
559    with self.assertRaisesRegex(ValueError, "Dimensions mismatch"):
560      tensor_format.locate_tensor_element(out, [5, 5])
561
562  def testLocateTensorElement3DWithEllipsesBatchMode(self):
563    a = (np.arange(11 * 11 * 11) + 1000).reshape([11, 11, 11]).astype(np.int32)
564
565    out = tensor_format.format_tensor(
566        a, "a", False, np_printoptions={"threshold": 100,
567                                        "edgeitems": 2})
568
569    cli_test_utils.assert_lines_equal_ignoring_whitespace(
570        self, ["Tensor \"a\":", ""], out.lines[:2])
571    self.assertEqual(repr(a).split("\n"), out.lines[2:])
572
573    actual_row_0_0_0, actual_col_0_0_0 = self._findFirst(out.lines, "1000")
574    actual_row_0_0_10, _ = self._findFirst(out.lines, "1010")
575    actual_row_10_10_10, _ = self._findFirst(out.lines, "2330")
576
577    (are_omitted, rows, start_cols,
578     end_cols) = tensor_format.locate_tensor_element(out, [[0, 0, 0]])
579    self.assertEqual([False], are_omitted)
580    self.assertEqual([actual_row_0_0_0], rows)
581    self.assertEqual([actual_col_0_0_0], start_cols)
582    self.assertEqual([actual_col_0_0_0 + 4], end_cols)
583
584    (are_omitted, rows, start_cols,
585     end_cols) = tensor_format.locate_tensor_element(out,
586                                                     [[0, 0, 0], [0, 0, 10]])
587    self.assertEqual([False, False], are_omitted)
588    self.assertEqual([actual_row_0_0_0, actual_row_0_0_10], rows)
589    self.assertEqual([actual_col_0_0_0, None], start_cols)
590    self.assertEqual([actual_col_0_0_0 + 4, None], end_cols)
591
592    (are_omitted, rows, start_cols,
593     end_cols) = tensor_format.locate_tensor_element(out,
594                                                     [[0, 0, 0], [0, 2, 0]])
595    self.assertEqual([False, True], are_omitted)
596    self.assertEqual([2, 4], rows)
597    self.assertEqual(2, len(start_cols))
598    self.assertEqual(2, len(end_cols))
599
600    (are_omitted, rows, start_cols,
601     end_cols) = tensor_format.locate_tensor_element(out,
602                                                     [[0, 0, 0], [10, 10, 10]])
603    self.assertEqual([False, False], are_omitted)
604    self.assertEqual([actual_row_0_0_0, actual_row_10_10_10], rows)
605    self.assertEqual([actual_col_0_0_0, None], start_cols)
606    self.assertEqual([actual_col_0_0_0 + 4, None], end_cols)
607
608  def testLocateTensorElementAnnotationsUnavailable(self):
609    tensor_proto = tensor_pb2.TensorProto(
610        dtype=types_pb2.DataType.Value("DT_FLOAT"),
611        tensor_shape=tensor_shape_pb2.TensorShapeProto(
612            dim=[tensor_shape_pb2.TensorShapeProto.Dim(size=1)]))
613    out = tensor_format.format_tensor(
614        debug_data.InconvertibleTensorProto(tensor_proto, False), "a")
615
616    self.assertEqual(["Tensor \"a\":", "", "Uninitialized tensor:"],
617                     out.lines[:3])
618
619    with self.assertRaisesRegex(
620        AttributeError, "tensor_metadata is not available in annotations"):
621      tensor_format.locate_tensor_element(out, [0])
622
623
624class NumericSummaryTest(test_util.TensorFlowTestCase):
625
626  def testNumericSummaryOnFloatFullHouse(self):
627    x = np.array([np.nan, np.nan, -np.inf, np.inf, np.inf, np.inf, -2, -3, -4,
628                  0, 1, 2, 2, 2, 2, 0, 0, 0, np.inf, np.inf, np.inf])
629    out = tensor_format.numeric_summary(x)
630    cli_test_utils.assert_lines_equal_ignoring_whitespace(
631        self,
632        ["|  nan -inf    -    0    + +inf | total |",
633         "|    2    1    3    4    5    6 |    21 |",
634         "|     min     max    mean    std |"], out.lines[:3])
635    cli_test_utils.assert_array_lines_close(
636        self, [-4.0, 2.0, 0.0, 1.95789002075], out.lines[3:4])
637
638  def testNumericSummaryOnFloatMissingCategories(self):
639    x = np.array([np.nan, np.nan])
640    out = tensor_format.numeric_summary(x)
641    self.assertEqual(2, len(out.lines))
642    cli_test_utils.assert_lines_equal_ignoring_whitespace(
643        self, ["| nan | total |", "|   2 |     2 |"], out.lines[:2])
644
645    x = np.array([-np.inf, np.inf, 0, 0, np.inf, np.inf])
646    out = tensor_format.numeric_summary(x)
647    cli_test_utils.assert_lines_equal_ignoring_whitespace(
648        self,
649        ["| -inf    0 +inf | total |",
650         "|    1    2    3 |     6 |",
651         "|  min  max mean  std |"], out.lines[:3])
652    cli_test_utils.assert_array_lines_close(
653        self, [0.0, 0.0, 0.0, 0.0], out.lines[3:4])
654
655    x = np.array([-120, 120, 130])
656    out = tensor_format.numeric_summary(x)
657    cli_test_utils.assert_lines_equal_ignoring_whitespace(
658        self,
659        ["| - + | total |",
660         "| 1 2 |     3 |",
661         "|       min       max     mean      std |"],
662        out.lines[:3])
663    cli_test_utils.assert_array_lines_close(
664        self, [-120, 130, 43.3333333333, 115.566238822], out.lines[3:4])
665
666  def testNumericSummaryOnEmptyFloat(self):
667    x = np.array([], dtype=np.float32)
668    out = tensor_format.numeric_summary(x)
669    self.assertEqual(["No numeric summary available due to empty tensor."],
670                     out.lines)
671
672  def testNumericSummaryOnInt(self):
673    x = np.array([-3] * 50 + [3] * 200 + [0], dtype=np.int32)
674    out = tensor_format.numeric_summary(x)
675    cli_test_utils.assert_lines_equal_ignoring_whitespace(
676        self,
677        ["|   -   0   + | total |",
678         "|  50   1 200 |   251 |",
679         "|      min     max    mean     std |"],
680        out.lines[:3])
681    cli_test_utils.assert_array_lines_close(
682        self, [-3, 3, 1.79282868526, 2.39789673081], out.lines[3:4])
683
684  def testNumericSummaryOnBool(self):
685    x = np.array([False, True, True, False], dtype=np.bool_)
686    out = tensor_format.numeric_summary(x)
687    cli_test_utils.assert_lines_equal_ignoring_whitespace(
688        self,
689        ["| False  True | total |", "|     2     2 |     4 |"], out.lines)
690
691    x = np.array([True] * 10, dtype=np.bool_)
692    out = tensor_format.numeric_summary(x)
693    cli_test_utils.assert_lines_equal_ignoring_whitespace(
694        self, ["| True | total |", "|   10 |    10 |"], out.lines)
695
696    x = np.array([False] * 10, dtype=np.bool_)
697    out = tensor_format.numeric_summary(x)
698    cli_test_utils.assert_lines_equal_ignoring_whitespace(
699        self, ["| False | total |", "|    10 |    10 |"], out.lines)
700
701    x = np.array([], dtype=np.bool_)
702    out = tensor_format.numeric_summary(x)
703    self.assertEqual(["No numeric summary available due to empty tensor."],
704                     out.lines)
705
706  def testNumericSummaryOnStrTensor(self):
707    x = np.array(["spam", "egg"], dtype=np.object_)
708    out = tensor_format.numeric_summary(x)
709    self.assertEqual(
710        ["No numeric summary available due to tensor dtype: object."],
711        out.lines)
712
713
714if __name__ == "__main__":
715  googletest.main()
716