blob: 848b1b404b7adec494a6812a2d01dcbdd2ebd43b [file] [log] [blame]
Jeremy Johnson015c3552022-02-23 12:15:03 +00001#!/usr/bin/env python3
Luke Hutton261b7b62023-01-10 14:50:31 +00002# Copyright (c) 2020-2023, ARM Limited.
Jeremy Johnson015c3552022-02-23 12:15:03 +00003# SPDX-License-Identifier: Apache-2.0
4import argparse
Jeremy Johnson015c3552022-02-23 12:15:03 +00005import json
6import math
7import os
8import queue
9import re
10import sys
11import threading
12import traceback
13from datetime import datetime
14from enum import IntEnum
15from enum import unique
Eric Kunze97b00272023-07-20 10:52:56 -070016from pathlib import Path
Jeremy Johnson015c3552022-02-23 12:15:03 +000017
18import numpy as np
19from checker.tosa_result_checker import LogColors
20from checker.tosa_result_checker import print_color
21from checker.tosa_result_checker import set_print_in_color
22from runner.run_command import run_sh_command
23from xunit.xunit import xunit_results
24from xunit.xunit import xunit_test
25
26
27def parse_args():
28 parser = argparse.ArgumentParser()
29 parser.add_argument(
Jared Smolensb7af4612022-03-21 19:41:52 -070030 "-t",
31 "--test",
32 dest="test",
33 default=[],
Eric Kunze3403ded2023-07-28 17:23:48 +000034 type=Path,
Jared Smolensb7af4612022-03-21 19:41:52 -070035 nargs="+",
36 help="Test(s) to run",
Jeremy Johnson015c3552022-02-23 12:15:03 +000037 )
38 parser.add_argument(
39 "-r",
40 "--recursive",
41 dest="recursive_tests",
42 action="store_true",
43 help="Recursively search for tests",
44 )
45 parser.add_argument(
46 "--tf-base-dir",
47 dest="tf_base_dir",
48 type=str,
49 required=True,
50 help="Tensorflow/MLIR base directory",
51 )
52 parser.add_argument(
53 "--tools-base-dir",
54 dest="tools_base_dir",
Eric Kunze3403ded2023-07-28 17:23:48 +000055 type=Path,
Jeremy Johnson015c3552022-02-23 12:15:03 +000056 required=True,
57 help="Reference model base directory",
58 )
59 parser.add_argument(
Tai Lya4d748b2023-03-28 22:06:56 +000060 "-p",
61 "--precise-mode",
62 dest="precise_mode",
63 action="store_true",
64 help="run in precise mode (FP64)",
65 )
66 parser.add_argument(
Jeremy Johnson015c3552022-02-23 12:15:03 +000067 "-v", "--verbose", dest="verbose", action="count", help="Verbose run"
68 )
69 parser.add_argument(
70 "-dref",
71 "--debug-ref-model",
72 dest="debug_ref_model",
73 action="store_true",
74 help="Enable TOSA Reference model debugging",
75 )
76 parser.add_argument(
77 "--tolerance",
78 dest="tolerance",
79 default=1e-3,
80 type=float,
81 help="Comparison tolerance b value",
82 )
83 parser.add_argument(
Jerry Gea793f462023-04-11 00:05:02 +000084 "--tosa_level",
85 dest="tosa_level",
86 default="EIGHTK",
87 type=str,
88 help="A TOSA level defines operator parameter ranges that an implementation shall support."
89 "Config tosa_level for running the reference model only. Default is EIGHTK",
90 )
91 parser.add_argument(
Jeremy Johnson015c3552022-02-23 12:15:03 +000092 "--no-compiler",
93 dest="no_compiler",
94 action="store_true",
95 help="Do not run TF MLIR/tfopt/TOSA compiler. Just run TOSA Reference model",
96 )
97 parser.add_argument(
98 "--no-ref-model",
99 dest="no_ref",
100 action="store_true",
101 help="Do not run TOSA reference model, just run TF MLIR/tfopt/TOSA compiler.",
102 )
103 parser.add_argument(
104 "--valgrind",
105 dest="valgrind",
106 action="store_true",
107 help="Enable valgrind on TOSA Reference Model",
108 )
109 parser.add_argument(
110 "-j", "--jobs", dest="jobs", type=int, default=1, help="Number of parallel jobs"
111 )
112 parser.add_argument(
113 "--no-color",
114 "--no-colour",
115 dest="no_color",
116 action="store_true",
117 help="Disable color output",
118 )
119 parser.add_argument(
120 "-f",
121 "--framework",
122 dest="framework",
123 default=[],
124 action="append",
125 help="Frameworks to test (tf, tflite)",
126 )
127 parser.add_argument(
128 "--override-exclusions",
129 dest="override_exclusions",
130 default=False,
131 action="store_true",
132 help="Ignore the framework exclusions listed in the test JSON",
133 )
134 parser.add_argument(
135 "--xunit-file",
136 dest="xunit_file",
137 type=str,
138 default="result.xml",
139 help="XUnit result output file",
140 )
141 parser.add_argument(
142 "--xunit-classname-prefix",
143 dest="xunit_classname_prefix",
144 default="TFUnitTests",
145 help="Prefix for xunit classname",
146 )
147 parser.add_argument(
148 "--hex-bool-hack",
149 dest="hex_bool_hack",
150 default=1,
151 type=int,
152 help=(
153 "Hack around bug in MLIR hex parsing for boolean types"
154 " by disabling hex encoding"
155 ),
156 )
157 parser.add_argument(
158 "--regression-mode",
159 dest="regression_mode",
160 default=False,
161 action="store_true",
162 help="Options to make the script more friendly for jenkins regressions",
163 )
164 parser.add_argument(
165 "--quantize-tolerance",
166 dest="quantize_tolerance",
167 default=0,
168 type=int,
169 help=(
170 "Tolerance when comparing TOSA reference model result"
171 " to TensorFlow Lite reference"
172 ),
173 )
174 parser.add_argument(
175 "--test-dir",
176 dest="test_dir",
Eric Kunze3403ded2023-07-28 17:23:48 +0000177 type=Path,
Jeremy Johnson015c3552022-02-23 12:15:03 +0000178 help="Path to prepend to paths in test.json",
179 )
180
181 parser.add_argument(
182 "-o", "--output", dest="output_file", help="Redirect script output to a file"
183 )
184
185 args = parser.parse_args()
186
187 # No easy way to both do array append and override a default value
188 if not args.framework:
189 args.framework = ["tf", "tflite"]
190
191 # Autodetect CPU count
192 if args.jobs <= 0:
193 args.jobs = os.cpu_count()
194
195 return args
196
197
198@unique
199class TestResult(IntEnum):
200 PASS = 0
201 COMPILER_ERROR = 1
202 REF_MODEL_ERROR = 2
203 REF_MODEL_UNPREDICTABLE = 3
204 REF_MODEL_RUNTIME_ERROR = 4
205 MISMATCH = 5
206 NOT_LOWERED = 6
207 INVALID_MLIR = 7
208 INTERNAL_ERROR = 8
209 SKIPPED = 9
210
211
212TestResultErrorStr = [
213 "",
214 "Compiler error",
215 "Reference model error",
216 "Reference model unpredictable",
217 "Reference model runtime error",
218 "Mismatch",
219 "Not lowered",
220 "Invalid MLIR",
221 "Internal error",
222 "",
223]
224
225
226def parse_compiler_output(compiler_stdout, compiler_stderr):
227 # Look for "has not been lowered yet, skipped" strings in stdout
228 expr = re.compile(".* has not been lowered yet, skipped.*")
229
230 for line in compiler_stdout.splitlines():
231 if expr.match(line):
232 return TestResult.NOT_LOWERED
233
234 return TestResult.PASS
235
236
237def parse_reference_model_output(ref_model_stdout, ref_model_stderr):
238 # Look for "has not been lowered yet, skipped" strings in stdout
239 unpredictable_expr = re.compile(r".*UNPREDICTABLE.*")
240 error_expr = re.compile(".* Graph result: ERROR.*")
241 unknown_expr = re.compile(".* Unknown graph status code.*")
242
243 for line in ref_model_stderr.splitlines():
244 if unpredictable_expr.match(line):
245 return TestResult.REF_MODEL_UNPREDICTABLE
246 elif error_expr.match(line):
247 return TestResult.REF_MODEL_ERROR
248 elif unknown_expr.match(line):
249 return TestResult.REF_MODEL_RUNTIME_ERROR
250
251 return TestResult.PASS
252
253
254# write a self-contained test descriptor in json format
255def write_reference_runner_json(
256 filename,
257 tosa_filename,
258 ifm_name,
259 ifm_file,
260 ofm_name,
261 ofm_file,
262 expected_failure=False,
263):
264 """Write a json test file so that it is fairly easy to pick up the test
265 and generate commands for third party tool"""
266 test_desc = dict()
267
268 test_desc["tosa_file"] = tosa_filename
269 test_desc["ifm_name"] = ifm_name
270 test_desc["ifm_file"] = ifm_file
271 test_desc["ofm_name"] = ofm_name
272 test_desc["ofm_file"] = ofm_file
273 test_desc["expected_failure"] = expected_failure
274
275 with open(filename, "w") as f:
276 json.dump(test_desc, f, indent=" ")
277
278
Eric Kunze3403ded2023-07-28 17:23:48 +0000279def run_test(args, test_path, framework):
Eric Kunze97b00272023-07-20 10:52:56 -0700280 msg = ""
281
282 try:
283 with open(test_path / "test.json", "r") as f:
284 test_desc = json.load(f)
285 except Exception:
286 raise Exception(f"Could not load or parse test from {test_path / 'test.json'}")
287
Jeremy Johnson015c3552022-02-23 12:15:03 +0000288 test_name = None
Eric Kunze97b00272023-07-20 10:52:56 -0700289 if "name" in test_desc:
290 test_name = test_desc["name"]
291 else:
292 test_name = test_path.name
Jeremy Johnson015c3552022-02-23 12:15:03 +0000293 if not test_name:
Eric Kunze3403ded2023-07-28 17:23:48 +0000294 raise Exception(f"Could not parse test_name from {test_path}")
Jeremy Johnson015c3552022-02-23 12:15:03 +0000295
Eric Kunze3403ded2023-07-28 17:23:48 +0000296 print_color(LogColors.GREEN, f"## Running {framework} test {test_name}")
Jeremy Johnson015c3552022-02-23 12:15:03 +0000297
Jeremy Johnson015c3552022-02-23 12:15:03 +0000298 try:
299 if not args.override_exclusions:
300 for excl in test_desc["framework_exclusions"]:
301 if excl == framework:
302 print_color(LogColors.GREEN, "Results SKIPPED")
Eric Kunze3403ded2023-07-28 17:23:48 +0000303 return (TestResult.SKIPPED, 0.0, "", test_name)
Jeremy Johnson015c3552022-02-23 12:15:03 +0000304 except KeyError:
305 pass
306
Eric Kunze3403ded2023-07-28 17:23:48 +0000307 tf_tools_dir = Path(
308 f"{args.tf_base_dir}/bazel-bin/tensorflow/compiler/mlir"
309 ).resolve()
Jeremy Johnson015c3552022-02-23 12:15:03 +0000310
Eric Kunze3403ded2023-07-28 17:23:48 +0000311 pre_opt_filename = str(test_path / f"test_{framework}.preopt.mlir")
312 post_opt_filename = str(test_path / f"test_{framework}.postopt.mlir")
Jeremy Johnson015c3552022-02-23 12:15:03 +0000313 if args.test_dir:
314 test_path_prepend = args.test_dir
315 else:
Eric Kunze3403ded2023-07-28 17:23:48 +0000316 test_path_prepend = test_path
Jeremy Johnson015c3552022-02-23 12:15:03 +0000317
318 # 1. Framework to MLIR translator command
319 if framework == "tf":
320 if test_desc["tf_model_filename"].endswith(".mlir"):
321 pre_opt_filename = test_desc["tf_model_filename"]
322 translate_mlir_cmd = []
323 else:
324 translate_mlir_cmd = [
Eric Kunze3403ded2023-07-28 17:23:48 +0000325 str(tf_tools_dir / "tf-mlir-translate"),
Jeremy Johnson015c3552022-02-23 12:15:03 +0000326 "--graphdef-to-mlir",
327 "--tf-enable-shape-inference-on-import",
Eric Kunze3403ded2023-07-28 17:23:48 +0000328 f"--tf-output-arrays={test_desc['tf_result_name']}",
329 str(test_path_prepend / test_desc["tf_model_filename"]),
Jeremy Johnson015c3552022-02-23 12:15:03 +0000330 "-o",
331 pre_opt_filename,
332 ]
333 elif framework == "tflite":
334 if test_desc["tflite_model_filename"].endswith(".mlir"):
335 pre_opt_filename = test_desc["tflite_model_filename"]
336 translate_mlir_cmd = []
337 else:
338 translate_mlir_cmd = [
Eric Kunze3403ded2023-07-28 17:23:48 +0000339 str(tf_tools_dir / "lite" / "flatbuffer_translate"),
Jeremy Johnson015c3552022-02-23 12:15:03 +0000340 "--tflite-flatbuffer-to-mlir",
Eric Kunze3403ded2023-07-28 17:23:48 +0000341 str(test_path_prepend / test_desc["tflite_model_filename"]),
342 f"--output-arrays={test_desc['tflite_result_name']}",
Jeremy Johnson015c3552022-02-23 12:15:03 +0000343 "-o",
344 pre_opt_filename,
345 ]
346 else:
Eric Kunze3403ded2023-07-28 17:23:48 +0000347 raise Exception(f"Unknown framwork: {framework}")
Jeremy Johnson015c3552022-02-23 12:15:03 +0000348
349 # Any additional inputs to the translator?
350 input_tensor_prefix = "TosaInput_"
Eric Kunze3403ded2023-07-28 17:23:48 +0000351 flatbuffer_dir = f"flatbuffer-{framework}"
Jeremy Johnson015c3552022-02-23 12:15:03 +0000352 mlir_opts = []
353
354 # Temporary hack: MLIR's new hex encoding of large tensors does not work for
355 # boolean types
356 # for TF hash 8e8041d594a888eb67eafa5cc62627d7e9ca8082
Eric Kunze3403ded2023-07-28 17:23:48 +0000357 if str(test_path).endswith("_bool") and args.hex_bool_hack:
Jeremy Johnson015c3552022-02-23 12:15:03 +0000358 mlir_opts.append("--mlir-print-elementsattrs-with-hex-if-larger=-1")
359
360 try:
361 # specify input tensors if test is generated from .pb
362 if framework == "tf":
363 # Convert the shape to a mlir-friendly string
364 shapes = []
365 for curr_shape in test_desc["ifm_shape"]:
366 shape_str = ""
367 for dim in curr_shape:
368 shape_str = shape_str + str(dim) + ","
369 shapes.append(shape_str)
370
371 translate_mlir_cmd.extend(
372 ["--tf-input-arrays", ",".join(test_desc["ifm_name"])]
373 )
374 translate_mlir_cmd.extend(["--tf-input-shapes", ":".join(shapes)])
375
376 # Write the hard-coded placeholder input (reshaped as necesary) to
377 # the file that compiler specified.
378 reference_runner_ifm_name = []
379 for i in range(len(test_desc["ifm_file"])):
Eric Kunze3403ded2023-07-28 17:23:48 +0000380 ifm_tensor_name = f"{input_tensor_prefix}{i}"
Jeremy Johnson015c3552022-02-23 12:15:03 +0000381
382 assert test_desc["ifm_file"][i].endswith(".npy")
Eric Kunze3403ded2023-07-28 17:23:48 +0000383 ifm_np = np.load(test_path / test_desc["ifm_file"][i])
Jared Smolensb7af4612022-03-21 19:41:52 -0700384
385 # We sometimes encounter input shape/expected input shape mismatches
386 # due to a missing batch dimension on the input (e.g. a single 3D image).
387 #
388 # Make sure input numpy and input shape from descriptor match,
389 # expand_dims on the outer dimensions until the rank matches,
390 # then do the shape comparison.
391 while len(list(ifm_np.shape)) < len(test_desc["ifm_shape"][i]):
392 ifm_np = np.expand_dims(ifm_np, axis=0)
393
Luke Hutton714aa602023-02-08 19:45:26 +0000394 # After legalization, complex tensors are expected to be represented
395 # as a single floating point tensor of shape [?, ..., ?, 2].
396 expected_shape = test_desc["ifm_shape"][i]
Eric Kunze3403ded2023-07-28 17:23:48 +0000397 if str(test_path).endswith("c64"):
Luke Hutton714aa602023-02-08 19:45:26 +0000398 expected_shape.append(2)
399
400 assert list(ifm_np.shape) == expected_shape
Jeremy Johnson015c3552022-02-23 12:15:03 +0000401
402 reference_runner_ifm_name.append(ifm_tensor_name)
403
404 except KeyError:
405 # No additional inputs. Ignore.
406 pass
407
408 tf_opt_cmd = [
Eric Kunze3403ded2023-07-28 17:23:48 +0000409 str(tf_tools_dir / "tf-opt"),
Jeremy Johnson015c3552022-02-23 12:15:03 +0000410 "--tf-executor-to-functional-conversion",
411 "--verify-each",
412 pre_opt_filename,
413 "-o",
414 post_opt_filename,
415 ]
416
417 translate_mlir_cmd.extend(mlir_opts)
418 tf_opt_cmd.extend(mlir_opts)
419
Eric Kunze3403ded2023-07-28 17:23:48 +0000420 compiler_cmd = [str(tf_tools_dir / "tf-opt")]
Jeremy Johnson015c3552022-02-23 12:15:03 +0000421
422 if framework == "tf":
423 compiler_cmd.append("--tf-to-tosa-pipeline")
424 elif framework == "tflite":
425 compiler_cmd.append("--tfl-to-tosa-pipeline")
426 compiler_cmd.append("--tosa-strip-quant-types")
427
Eric Kunze3403ded2023-07-28 17:23:48 +0000428 tosa_mlir_filename = str(test_path / f"output_{framework}.tosa.mlir")
Jeremy Johnson015c3552022-02-23 12:15:03 +0000429
Eric Kunze3403ded2023-07-28 17:23:48 +0000430 flatbuffer_dir_fullpath = test_path / flatbuffer_dir
Jeremy Johnson015c3552022-02-23 12:15:03 +0000431
Eric Kunze3403ded2023-07-28 17:23:48 +0000432 flatbuffer_dir_fullpath.mkdir(exist_ok=True)
Jeremy Johnson015c3552022-02-23 12:15:03 +0000433
434 compiler_cmd.extend(
435 [
436 "--verify-each",
437 post_opt_filename,
438 "-o",
439 tosa_mlir_filename,
440 "--tosa-serialize",
Eric Kunze3403ded2023-07-28 17:23:48 +0000441 f"--tosa-flatbuffer-filename={flatbuffer_dir_fullpath / f'{test_name}.tosa'}",
Jeremy Johnson015c3552022-02-23 12:15:03 +0000442 ]
443 )
444
445 if not args.no_compiler:
446 try:
447 if translate_mlir_cmd:
448 run_sh_command(translate_mlir_cmd, args.verbose, True)
449 if tf_opt_cmd:
450 run_sh_command(tf_opt_cmd, args.verbose, True)
451 except Exception as e:
Eric Kunze3403ded2023-07-28 17:23:48 +0000452 print_color(LogColors.RED, f"Results INVALID_MLIR {test_name}: {e}")
453 return (TestResult.INVALID_MLIR, 0.0, e, test_name)
Jeremy Johnson015c3552022-02-23 12:15:03 +0000454
455 try:
Jeremy Johnson015c3552022-02-23 12:15:03 +0000456 compiler_stdout, compiler_stderr = run_sh_command(
457 compiler_cmd, args.verbose, True
458 )
459 compiler_rc = parse_compiler_output(compiler_stdout, compiler_stderr)
460 if compiler_rc == TestResult.NOT_LOWERED:
461 print_color(
462 LogColors.RED,
Eric Kunze3403ded2023-07-28 17:23:48 +0000463 f"Results NOT_LOWERED {test_name}, framework {framework}",
Jeremy Johnson015c3552022-02-23 12:15:03 +0000464 )
Eric Kunze3403ded2023-07-28 17:23:48 +0000465 return (TestResult.NOT_LOWERED, 0.0, "", test_name)
Jeremy Johnson015c3552022-02-23 12:15:03 +0000466
467 pass
468
469 except Exception as e:
470 if "same scale constraint" in str(e):
Eric Kunze3403ded2023-07-28 17:23:48 +0000471 print_color(LogColors.RED, f"Results INVALID_MLIR {test_name}: {e}")
472 return (TestResult.INVALID_MLIR, 0.0, e, test_name)
Jeremy Johnson015c3552022-02-23 12:15:03 +0000473 else:
Eric Kunze3403ded2023-07-28 17:23:48 +0000474 print_color(LogColors.RED, f"Results COMPILER_ERROR {test_name}: {e}")
475 return (TestResult.COMPILER_ERROR, 0.0, e, test_name)
Jeremy Johnson015c3552022-02-23 12:15:03 +0000476
477 if framework == "tf":
478 try:
Eric Kunze3403ded2023-07-28 17:23:48 +0000479 tf_result = np.load(test_path / test_desc["tf_result_npy_filename"])
Jeremy Johnson015c3552022-02-23 12:15:03 +0000480 except KeyError:
481 assert 0, "fail to load tf result numpy"
482 elif framework == "tflite":
483 try:
Eric Kunze3403ded2023-07-28 17:23:48 +0000484 tf_result = np.load(test_path / test_desc["tflite_result_npy_filename"])
Jeremy Johnson015c3552022-02-23 12:15:03 +0000485 except KeyError:
486 assert 0, "fail to load tflite result numpy"
487
Luke Hutton261b7b62023-01-10 14:50:31 +0000488 # TOSA has no notion of complex datatypes, it represents complex values using two
489 # fp32 output tensors representing real and imaginary values. When legalizing
490 # complex operations from frameworks, these two output tensors are combined into
491 # a single tensor of shape [?, ..., ?, 2] whereby each inner pair of values
492 # represents the real and imaginary parts of a complex value. This is completed
493 # by inserting reshape and concatenate TOSA operations during the legalization to
494 # maintain a one-to-one correspondance with framework outputs, thus simplifying
495 # legalization. Here tf_result should also match this format before being
496 # compared to the ref model output.
497 if tf_result.dtype == np.complex64:
498 ifm_shape = tf_result.shape + (2,)
499 tf_result = tf_result.view(np.float32)
500 tf_result = tf_result.reshape(ifm_shape)
501
Jeremy Johnson015c3552022-02-23 12:15:03 +0000502 # Generate test descriptor per flatbuffer generation
503 # Input .npy will be shared across different frameworks
504 # Output .npy will be generated in its corresponding flatbuffer
505 reference_runner_ifm_file = [
Eric Kunze3403ded2023-07-28 17:23:48 +0000506 str(Path("..") / ifm_file) for ifm_file in test_desc["ifm_file"]
Jeremy Johnson015c3552022-02-23 12:15:03 +0000507 ]
508
509 # Check if there's any operator in output graph.
510 empty_graph = True
511 with open(tosa_mlir_filename, "r") as f:
512 for line in f:
513 if re.search('"tosa.*"', line):
514 empty_graph = False
515
516 break
517
518 # Fast-forward input tensor to output tensor if TOSA graph is empty.
519 if empty_graph:
520 reference_runner_ofm_name = reference_runner_ifm_name
521 else:
522 reference_runner_ofm_name = ["TosaOutput_0"]
523
524 write_reference_runner_json(
Eric Kunze3403ded2023-07-28 17:23:48 +0000525 filename=str(test_path / flatbuffer_dir / "desc.json"),
526 tosa_filename=f"{test_name}.tosa",
Jeremy Johnson015c3552022-02-23 12:15:03 +0000527 ifm_name=reference_runner_ifm_name,
528 ifm_file=reference_runner_ifm_file,
529 ofm_name=reference_runner_ofm_name,
530 ofm_file=["ref_model_output_0.npy"],
531 )
532
533 ref_model_cmd = [
Eric Kunze3403ded2023-07-28 17:23:48 +0000534 str(args.tools_base_dir / "build" / "reference_model" / "tosa_reference_model"),
535 f"--test_desc={test_path / flatbuffer_dir / 'desc.json'}",
Jeremy Johnson015c3552022-02-23 12:15:03 +0000536 ]
537
538 if args.debug_ref_model:
Eric Kunze286f8342022-06-22 11:30:23 -0700539 ref_model_cmd.extend(["-D ALL", "-l high"])
Jeremy Johnson015c3552022-02-23 12:15:03 +0000540
Tai Lya4d748b2023-03-28 22:06:56 +0000541 if args.precise_mode:
542 ref_model_cmd.extend(["--precise_mode=1"])
543
Jeremy Johnson015c3552022-02-23 12:15:03 +0000544 if args.valgrind:
545 ref_model_cmd = [
546 "valgrind",
547 "--show-leak-kinds=all",
548 "--log-fd=1",
549 "-q",
550 ] + ref_model_cmd
551
Eric Kunze3403ded2023-07-28 17:23:48 +0000552 ref_model_cmd = ref_model_cmd + [f"--tosa_level={args.tosa_level}"]
Jerry Gea793f462023-04-11 00:05:02 +0000553
Jeremy Johnson015c3552022-02-23 12:15:03 +0000554 # Clean out any ref_model result first
Eric Kunze3403ded2023-07-28 17:23:48 +0000555 for f in (test_path / flatbuffer_dir).glob("ref_model_*.npy"):
556 f.unlink()
Jeremy Johnson015c3552022-02-23 12:15:03 +0000557
Jared Smolensb7af4612022-03-21 19:41:52 -0700558 if args.no_ref:
559 return (TestResult.PASS, 0.0, msg)
560
561 try:
562 ref_model_stdout, ref_model_stderr = run_sh_command(
563 ref_model_cmd, args.verbose, True
564 )
565 ref_model_rc = parse_reference_model_output(ref_model_stdout, ref_model_stderr)
566 if ref_model_rc != TestResult.PASS:
567 return (ref_model_rc, 0.0, "")
568 except Exception as e:
569 ref_model_rc = parse_reference_model_output("", str(e))
570 if ref_model_rc != TestResult.PASS:
Jeremy Johnson015c3552022-02-23 12:15:03 +0000571 print_color(
572 LogColors.RED,
Eric Kunze3403ded2023-07-28 17:23:48 +0000573 f"Results {TestResultErrorStr[ref_model_rc]} {test_name}: {e}",
Jeremy Johnson015c3552022-02-23 12:15:03 +0000574 )
Jared Smolensb7af4612022-03-21 19:41:52 -0700575 return (ref_model_rc, 0.0, "")
Eric Kunze3403ded2023-07-28 17:23:48 +0000576 print_color(LogColors.RED, f"Results REF_MODEL_RUNTIME_ERROR {test_name}: {e}")
577 return (TestResult.REF_MODEL_RUNTIME_ERROR, 0.0, e, test_name)
Jeremy Johnson015c3552022-02-23 12:15:03 +0000578
Tai Lya4d748b2023-03-28 22:06:56 +0000579 if args.precise_mode == 1 and (
580 tf_result.dtype == np.float16 or tf_result.dtype == np.float32
581 ):
582 tf_result = tf_result.astype(np.float64)
583 elif tf_result.dtype == np.float16:
Jeremy Johnson015c3552022-02-23 12:15:03 +0000584 tf_result = tf_result.astype(np.float32)
585 elif (
586 tf_result.dtype == np.uint8
587 or tf_result.dtype == np.int8
588 or tf_result.dtype == np.int16
589 or tf_result.dtype == np.int64
590 ):
591 tf_result = tf_result.astype(np.int32)
592
Eric Kunze3403ded2023-07-28 17:23:48 +0000593 # For now, search for the first output from ref_model
594 ref_model_result_files = list((test_path / flatbuffer_dir).glob("ref_model_*.npy"))
Jeremy Johnson015c3552022-02-23 12:15:03 +0000595 ref_model_result = np.load(ref_model_result_files[0])
596
597 assert (
598 tf_result.dtype == ref_model_result.dtype
Eric Kunze3403ded2023-07-28 17:23:48 +0000599 ), f"Numpy type mismatch {tf_result.dtype} != {ref_model_result.dtype} when comparing result"
Jeremy Johnson015c3552022-02-23 12:15:03 +0000600
601 # Size comparison
602 # Size = 1 tensors can be equivalently represented as having rank 0 or rank
603 # >= 0, allow that special case
604 tf_result = np.squeeze(tf_result)
605 ref_model_result = np.squeeze(ref_model_result)
606
607 if np.shape(tf_result) != np.shape(ref_model_result):
Eric Kunze3403ded2023-07-28 17:23:48 +0000608 print_color(LogColors.RED, f"Results MISCOMPARE {test_name}")
609 msg = f"Shapes mismatch: Reference {np.shape(tf_result)} vs {np.shape(ref_model_result)}"
Jeremy Johnson015c3552022-02-23 12:15:03 +0000610 print(msg)
Eric Kunze3403ded2023-07-28 17:23:48 +0000611 return (TestResult.MISMATCH, 0.0, msg, test_name)
Jeremy Johnson015c3552022-02-23 12:15:03 +0000612
613 # for quantized test, allow +-(args.quantize_tolerance) error
614 if ref_model_result.dtype == np.int32:
615 assert tf_result.dtype == np.int32
616
617 if np.all(np.absolute(ref_model_result - tf_result) <= args.quantize_tolerance):
Eric Kunze3403ded2023-07-28 17:23:48 +0000618 print_color(LogColors.GREEN, f"Results PASS {test_name}")
Jeremy Johnson015c3552022-02-23 12:15:03 +0000619 else:
Eric Kunze3403ded2023-07-28 17:23:48 +0000620 print_color(LogColors.RED, f"Results MISCOMPARE {test_name}")
Jeremy Johnson015c3552022-02-23 12:15:03 +0000621
622 tolerance = args.quantize_tolerance + 1
623 while not np.all(
624 np.absolute(ref_model_result - tf_result) <= args.quantize_tolerance
625 ):
626 tolerance = tolerance + 1
627 if tolerance >= 10:
628 break
629
Eric Kunze3403ded2023-07-28 17:23:48 +0000630 msg = f"Result is within {tolerance} {test_path}"
Jeremy Johnson015c3552022-02-23 12:15:03 +0000631 print(msg)
632
633 np.set_printoptions(threshold=128)
Eric Kunze3403ded2023-07-28 17:23:48 +0000634 print(f"tf_result: {tf_result.shape}\n")
Jeremy Johnson015c3552022-02-23 12:15:03 +0000635 print(tf_result)
Eric Kunze3403ded2023-07-28 17:23:48 +0000636 print(f"ref_model_result: {ref_model_result.shape}\n")
Jeremy Johnson015c3552022-02-23 12:15:03 +0000637 print(ref_model_result)
638 # print(tf_result - ref_model_result)
Eric Kunze3403ded2023-07-28 17:23:48 +0000639 return (TestResult.MISMATCH, tolerance, msg, test_name)
Jeremy Johnson015c3552022-02-23 12:15:03 +0000640 else:
641 if np.allclose(
642 ref_model_result, tf_result, atol=args.tolerance, equal_nan=True
643 ):
Eric Kunze3403ded2023-07-28 17:23:48 +0000644 print_color(LogColors.GREEN, f"Results PASS {test_name}")
Jeremy Johnson015c3552022-02-23 12:15:03 +0000645 else:
Eric Kunze3403ded2023-07-28 17:23:48 +0000646 print_color(LogColors.RED, f"Results MISCOMPARE {test_name}")
Jeremy Johnson015c3552022-02-23 12:15:03 +0000647
648 # Many of these tests would match with a reasonable looser tolerence.
649 # Determine what would have worked.
650 tolerance = args.tolerance * 10.0
651 while not np.allclose(
652 ref_model_result, tf_result, atol=tolerance, equal_nan=True
653 ):
654 tolerance = tolerance * 10.0
655 if tolerance > 1.0e10:
656 tolerance = math.inf
657 break
658
Eric Kunze3403ded2023-07-28 17:23:48 +0000659 msg = f"Result is within {tolerance:.0e} {test_name}"
Jeremy Johnson015c3552022-02-23 12:15:03 +0000660 print(msg)
661
662 np.set_printoptions(precision=4, threshold=128)
Eric Kunze3403ded2023-07-28 17:23:48 +0000663 print(f"tf_result: {tf_result.shape}\n")
Jeremy Johnson015c3552022-02-23 12:15:03 +0000664 print(tf_result)
Eric Kunze3403ded2023-07-28 17:23:48 +0000665 print(f"ref_model_result: {ref_model_result.shape}\n")
Jeremy Johnson015c3552022-02-23 12:15:03 +0000666 print(ref_model_result)
667 # print(tf_result - ref_model_result)
Eric Kunze3403ded2023-07-28 17:23:48 +0000668 return (TestResult.MISMATCH, tolerance, msg, test_name)
Jeremy Johnson015c3552022-02-23 12:15:03 +0000669
Eric Kunze3403ded2023-07-28 17:23:48 +0000670 return (TestResult.PASS, args.tolerance, msg, test_name)
Jeremy Johnson015c3552022-02-23 12:15:03 +0000671
672
673def worker_thread(task_queue, args, result_queue):
674 while True:
675 try:
676 (test, framework) = task_queue.get(block=False)
677 except queue.Empty:
678 break
679
680 if test is None:
681 break
682
683 msg = ""
684 start_time = datetime.now()
685 try:
Eric Kunze3403ded2023-07-28 17:23:48 +0000686 (rc, tolerance, msg, test_name) = run_test(args, test, framework)
Jeremy Johnson015c3552022-02-23 12:15:03 +0000687 except Exception as e:
Eric Kunze3403ded2023-07-28 17:23:48 +0000688 print(f"Internal regression error: {e}")
Jeremy Johnson015c3552022-02-23 12:15:03 +0000689 print(
690 "".join(
691 traceback.format_exception(
692 etype=type(e), value=e, tb=e.__traceback__
693 )
694 )
695 )
696 rc = TestResult.INTERNAL_ERROR
697 tolerance = 0.0
698
699 end_time = datetime.now()
700
Eric Kunze3403ded2023-07-28 17:23:48 +0000701 result_queue.put(
702 (test, framework, rc, tolerance, msg, end_time - start_time, test_name)
703 )
Jeremy Johnson015c3552022-02-23 12:15:03 +0000704 task_queue.task_done()
705
706 return True
707
708
709def getTestsInDir(directory):
710 # Recursively find any tests in this directory
Eric Kunze3403ded2023-07-28 17:23:48 +0000711 if (directory / "test.json").is_file():
Jeremy Johnson015c3552022-02-23 12:15:03 +0000712 return [directory]
Eric Kunze3403ded2023-07-28 17:23:48 +0000713 elif directory.is_dir():
Jeremy Johnson015c3552022-02-23 12:15:03 +0000714 test_list = []
Eric Kunze3403ded2023-07-28 17:23:48 +0000715 for d in directory.glob("*"):
Jeremy Johnson015c3552022-02-23 12:15:03 +0000716 test_list.extend(getTestsInDir(d))
717 return test_list
718 else:
719 return []
720
721
722def main():
723 args = parse_args()
724
725 set_print_in_color(not args.no_color)
726
727 if args.output_file:
728 set_print_in_color(False)
729 sys.stdout = open(args.output_file, "w")
730
731 # Disable TF info messages
732 os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3"
733
734 task_queue = queue.Queue()
735 result_queue = queue.Queue()
736
737 threads = []
738
739 # Result counters for each of the TestResult return codes
740 results = [0] * len(TestResult)
741
742 for tdir in args.test:
Jeremy Johnson015c3552022-02-23 12:15:03 +0000743 if args.recursive_tests:
744 tdirList = getTestsInDir(tdir)
745 else:
746 tdirList = [tdir]
747
748 for t in tdirList:
749 for f in args.framework:
750 task_queue.put((t, f))
751
752 for i in range(args.jobs):
753 t = threading.Thread(
754 target=worker_thread, args=(task_queue, args, result_queue)
755 )
756 t.setDaemon(True)
757 t.start()
758 threads.append(t)
759
760 # Run until queue is empty
761 task_queue.join()
762
763 print_color(LogColors.BOLD_WHITE, "Result summary")
764
765 result_list = []
766 while True:
767 try:
Eric Kunze3403ded2023-07-28 17:23:48 +0000768 test, framework, rc, tol, msg, time_delta, test_name = result_queue.get(
769 block=False
770 )
Jeremy Johnson015c3552022-02-23 12:15:03 +0000771 except queue.Empty:
772 break
773
Eric Kunze3403ded2023-07-28 17:23:48 +0000774 result_list.append((test, framework, rc, tol, msg, time_delta, test_name))
Jeremy Johnson015c3552022-02-23 12:15:03 +0000775 results[rc] = results[rc] + 1
776
777 xunit_result = xunit_results()
778 xunit_suite = xunit_result.create_suite(args.xunit_classname_prefix)
779
780 # Sort by test name
Eric Kunze3403ded2023-07-28 17:23:48 +0000781 for test, framework, rc, tol, err_msg, time_delta, test_name in sorted(
Jeremy Johnson015c3552022-02-23 12:15:03 +0000782 result_list, key=lambda tup: tup[0]
783 ):
Jeremy Johnson015c3552022-02-23 12:15:03 +0000784 class_name = f"{args.xunit_classname_prefix}.{framework}"
785
786 xt = xunit_test(test_name, class_name)
787
788 msg = TestResultErrorStr[rc]
789
790 xt.time = str(
791 float(time_delta.seconds) + (float(time_delta.microseconds) * 1e-6)
792 )
793
794 if len(msg) > 0:
Eric Kunze3403ded2023-07-28 17:23:48 +0000795 print(f"{msg} on {framework} {test}")
Jeremy Johnson015c3552022-02-23 12:15:03 +0000796
797 # Add any more verbose messaging for the xml log
798 if err_msg:
Eric Kunze3403ded2023-07-28 17:23:48 +0000799 msg = f"{msg} {err_msg}"
Jeremy Johnson015c3552022-02-23 12:15:03 +0000800
801 if rc == TestResult.PASS:
802 pass
803 elif rc == TestResult.SKIPPED:
804 xt.skipped()
805 else:
806 xt.failed(msg)
807
808 xunit_suite.tests.append(xt)
809
810 result_queue.task_done()
811
812 xunit_result.write_results(args.xunit_file)
813
814 print("Totals: ", end="")
815 for result in TestResult:
Eric Kunze3403ded2023-07-28 17:23:48 +0000816 print(f"{results[result]} {result.name.lower()}, ", end="")
Jeremy Johnson015c3552022-02-23 12:15:03 +0000817 print()
818
819 if not args.regression_mode and (
820 results[TestResult.COMPILER_ERROR] > 0
821 or results[TestResult.REF_MODEL_ERROR] > 0
822 or results[TestResult.MISMATCH] > 0
823 ):
824 return 1
825
826 return 0
827
828
829if __name__ == "__main__":
830 exit(main())