From 1ca751dc2c128a13ee8239eecc33f30dce133744 Mon Sep 17 00:00:00 2001 From: Chang Sun Date: Tue, 31 Oct 2023 14:33:31 -0700 Subject: [PATCH 1/6] Fix quartus writer with io_stream and multi-output --- hls4ml/writer/quartus_writer.py | 14 +++++--- test/pytest/test_multiout_network.py | 53 ++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 test/pytest/test_multiout_network.py diff --git a/hls4ml/writer/quartus_writer.py b/hls4ml/writer/quartus_writer.py index a5ba6dc1c3..f8f3d76188 100644 --- a/hls4ml/writer/quartus_writer.py +++ b/hls4ml/writer/quartus_writer.py @@ -172,7 +172,8 @@ def write_project_cpp(self, model): for inp in model_inputs: newline += indent + f'stream_in<{inp.type.name}> &{inp.name}_stream,\n' for out in model_outputs: - newline += indent + f'stream_out<{out.type.name}> &{out.name}_stream' + newline += indent + f'stream_out<{out.type.name}> &{out.name}_stream,\n' + newline = newline[:-2] # Remove the tailing ',\n' if model_brams: newline += ',\n' + brams_str newline += '\n) {\n' @@ -191,7 +192,8 @@ def write_project_cpp(self, model): for inp in model_inputs: newline += indent + f'stream_in<{inp.type.name}> &{inp.name}_stream,\n' for out in model_outputs: - newline += indent + f'stream_out<{out.type.name}> &{out.name}_stream' + newline += indent + f'stream_out<{out.type.name}> &{out.name}_stream,\n' + newline = newline[:-2] # Remove the tailing ',\n'\ if model_brams: newline += ',\n' + brams_str newline += '\n) {\n' @@ -277,7 +279,7 @@ def write_project_cpp(self, model): newline += indent + f' {out.type.name} tmp = {out.name}.read();\n' newline += indent + f' {out.name}_stream.write(tmp);\n' newline += indent + '}\n' - newline += '}\n' + newline += '}\n' else: newline = line newline += indent + 'return outputs;\n' @@ -330,7 +332,8 @@ def write_project_header(self, model): for inp in model_inputs: newline += indent + f'stream_in<{inp.type.name}> &{inp.name}_stream,\n' for out in model_outputs: - newline += indent + f'stream_out<{out.type.name}> &{out.name}_stream' + newline += indent + f'stream_out<{out.type.name}> &{out.name}_stream,\n' + newline = newline[:-2] # Remove the tailing ',\n' if model_brams: newline += ',\n' + brams_str newline += '\n);\n' @@ -350,7 +353,8 @@ def write_project_header(self, model): for inp in model_inputs: newline += indent + f'stream_in<{inp.type.name}> &{inp.name}_stream,\n' for out in model_outputs: - newline += indent + f'stream_out<{out.type.name}> &{out.name}_stream' + newline += indent + f'stream_out<{out.type.name}> &{out.name}_stream,\n' + newline = newline[:-2] # Remove the tailing ',\n' if model_brams: newline += ',\n' + brams_str newline += '\n);\n' diff --git a/test/pytest/test_multiout_network.py b/test/pytest/test_multiout_network.py new file mode 100644 index 0000000000..ebec9a8873 --- /dev/null +++ b/test/pytest/test_multiout_network.py @@ -0,0 +1,53 @@ +import os +import random +from pathlib import Path + +import numpy as np +import pytest +import tensorflow as tf +from keras.layers import Dense +from tensorflow import keras + +from hls4ml.converters import convert_from_keras_model + +test_root_path = Path(__file__).parent + + +@pytest.fixture(scope='module') +def model(): + seed = 42 + os.environ['RANDOM_SEED'] = f'{seed}' + np.random.seed(seed) + tf.random.set_seed(seed) + tf.get_logger().setLevel('ERROR') + random.seed(seed) + + inp = keras.Input(shape=(10,)) + x = Dense(10)(inp) + y = Dense(10)(inp) + model = keras.Model(inp, [x, y]) + return model + + +@pytest.fixture(scope='module') +def data(): + rng = np.random.RandomState(42) + X = rng.normal(0, 1, (1000, 10)) + X = np.clip(X, -16, 15) + return X + + +@pytest.mark.parametrize('backend', ['Vivado', 'Quartus', 'Vitis']) +@pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) +def test_multi_clone(model, data, backend: str, io_type: str): + output_dir = str(test_root_path / f'hls4mlprj_multiout_network_{backend}_{io_type}') + hls_config = {'Model': {'Precision': 'fixed<32,5>', 'ReuseFactor': 1}} + model_hls = convert_from_keras_model( + model, backend=backend, output_dir=output_dir, hls_config=hls_config, io_type=io_type + ) + model_hls.compile() + r_hls = model_hls.predict(data) + r_keras = [x.numpy() for x in model(data)] + + assert np.allclose(r_hls[0], r_keras[0], atol=1e-5, rtol=0) + assert np.allclose(r_hls[1], r_keras[1], atol=1e-5, rtol=0) From 684c092971d4fc688e312a7ac2daa9cd1b5b2f31 Mon Sep 17 00:00:00 2001 From: Chang Sun Date: Fri, 1 Dec 2023 19:05:12 -0800 Subject: [PATCH 2/6] no explicit seeding --- test/pytest/test_multiout_network.py | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/test/pytest/test_multiout_network.py b/test/pytest/test_multiout_network.py index ebec9a8873..3fe1fc6219 100644 --- a/test/pytest/test_multiout_network.py +++ b/test/pytest/test_multiout_network.py @@ -1,10 +1,7 @@ -import os -import random from pathlib import Path import numpy as np import pytest -import tensorflow as tf from keras.layers import Dense from tensorflow import keras @@ -15,13 +12,6 @@ @pytest.fixture(scope='module') def model(): - seed = 42 - os.environ['RANDOM_SEED'] = f'{seed}' - np.random.seed(seed) - tf.random.set_seed(seed) - tf.get_logger().setLevel('ERROR') - random.seed(seed) - inp = keras.Input(shape=(10,)) x = Dense(10)(inp) y = Dense(10)(inp) @@ -31,8 +21,7 @@ def model(): @pytest.fixture(scope='module') def data(): - rng = np.random.RandomState(42) - X = rng.normal(0, 1, (1000, 10)) + X = np.random.normal(0, 1, (1000, 10)) X = np.clip(X, -16, 15) return X From c48afd2641a3b5ab92b9c7580beaeec084739ad0 Mon Sep 17 00:00:00 2001 From: Chang Sun Date: Fri, 1 Dec 2023 19:16:56 -0800 Subject: [PATCH 3/6] prevent output dtype overwrite --- hls4ml/model/graph.py | 4 ++-- test/pytest/test_multiout_network.py | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/hls4ml/model/graph.py b/hls4ml/model/graph.py index c44fd8f02e..ce5c6b9287 100644 --- a/hls4ml/model/graph.py +++ b/hls4ml/model/graph.py @@ -472,7 +472,7 @@ def make_node(self, kind, name, attributes, inputs, outputs=None): for o in node.outputs: out_var = node.get_output_variable(output_name=o) if o in self.outputs: - out_var.type.name = 'result_t' + out_var.type.name = 'result_' + out_var.type.name self.output_vars[o] = out_var return node @@ -609,7 +609,7 @@ def get_input_variables(self): def register_output_variable(self, out_name, variable): if out_name in self.outputs: - variable.type.name = 'result_t' + variable.type.name = 'result_' + variable.type.name self.output_vars[out_name] = variable def get_output_variables(self): diff --git a/test/pytest/test_multiout_network.py b/test/pytest/test_multiout_network.py index 3fe1fc6219..b3acbd2be6 100644 --- a/test/pytest/test_multiout_network.py +++ b/test/pytest/test_multiout_network.py @@ -8,13 +8,14 @@ from hls4ml.converters import convert_from_keras_model test_root_path = Path(__file__).parent +test_root_path = Path('/tmp/hls4mlprj_multiout_network') @pytest.fixture(scope='module') def model(): inp = keras.Input(shape=(10,)) - x = Dense(10)(inp) - y = Dense(10)(inp) + x = Dense(10, name='dense1')(inp) + y = Dense(10, name='dense2')(inp) model = keras.Model(inp, [x, y]) return model @@ -31,9 +32,19 @@ def data(): def test_multi_clone(model, data, backend: str, io_type: str): output_dir = str(test_root_path / f'hls4mlprj_multiout_network_{backend}_{io_type}') hls_config = {'Model': {'Precision': 'fixed<32,5>', 'ReuseFactor': 1}} + layer_config = { + 'dense1': {'Precision': {'result': 'fixed<35,5>'}}, + 'dense2': {'Precision': {'result': 'fixed<40,5>'}}, + 'dense1_linear': {'Precision': {'result': 'fixed<35,5>'}}, + 'dense2_linear': {'Precision': {'result': 'fixed<40,5>'}}, + } + hls_config['LayerName'] = layer_config model_hls = convert_from_keras_model( model, backend=backend, output_dir=output_dir, hls_config=hls_config, io_type=io_type ) + + assert model_hls.graph['dense1'].attributes['result_t'] != model_hls.graph['dense2'].attributes['result_t'] + model_hls.compile() r_hls = model_hls.predict(data) r_keras = [x.numpy() for x in model(data)] From 9804cce2170a74248e4fe74226156aed5bff243d Mon Sep 17 00:00:00 2001 From: Chang Sun Date: Mon, 4 Dec 2023 10:42:33 -0800 Subject: [PATCH 4/6] fix seed --- test/pytest/test_stream_multi_clone.py | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/test/pytest/test_stream_multi_clone.py b/test/pytest/test_stream_multi_clone.py index 64b4779bfe..bd7d7a0bb1 100644 --- a/test/pytest/test_stream_multi_clone.py +++ b/test/pytest/test_stream_multi_clone.py @@ -1,10 +1,7 @@ -import os -import random from pathlib import Path import numpy as np import pytest -import tensorflow as tf from keras.layers import Add, Dense from tensorflow import keras @@ -15,13 +12,6 @@ @pytest.fixture(scope='module') def model(): - seed = 42 - os.environ['RANDOM_SEED'] = f'{seed}' - np.random.seed(seed) - tf.random.set_seed(seed) - tf.get_logger().setLevel('ERROR') - random.seed(seed) - inp = keras.Input(shape=(10,)) x = Dense(10)(inp) y = Dense(10)(inp) @@ -35,8 +25,7 @@ def model(): @pytest.fixture(scope='module') def data(): - rng = np.random.RandomState(42) - X = rng.normal(0, 1, (1000, 10)) + X = np.random.normal(0, 1, (1000, 10)) X = np.clip(X, -16, 15) return X From 69011fa4b699973fb41bfb6daa8968f9c2af7d60 Mon Sep 17 00:00:00 2001 From: Chang Sun Date: Wed, 13 Dec 2023 22:27:33 +0100 Subject: [PATCH 5/6] remove output only if multiple output --- hls4ml/model/graph.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hls4ml/model/graph.py b/hls4ml/model/graph.py index ce5c6b9287..9aad07ea8f 100644 --- a/hls4ml/model/graph.py +++ b/hls4ml/model/graph.py @@ -471,8 +471,8 @@ def make_node(self, kind, name, attributes, inputs, outputs=None): node = layer_cls(self, name, attributes, inputs, outputs) for o in node.outputs: out_var = node.get_output_variable(output_name=o) - if o in self.outputs: - out_var.type.name = 'result_' + out_var.type.name + if len(self.outputs) == 1 and o in self.outputs: + out_var.type.name = 'result_t' self.output_vars[o] = out_var return node @@ -608,8 +608,8 @@ def get_input_variables(self): return variables def register_output_variable(self, out_name, variable): - if out_name in self.outputs: - variable.type.name = 'result_' + variable.type.name + if len(self.outputs) == 1 and out_name in self.outputs: + variable.type.name = 'result_t' self.output_vars[out_name] = variable def get_output_variables(self): From 4660232678f1871bf0d9404ba448d38c3801466a Mon Sep 17 00:00:00 2001 From: Chang Sun Date: Fri, 15 Dec 2023 01:19:01 +0100 Subject: [PATCH 6/6] fix test path --- test/pytest/test_multiout_network.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/pytest/test_multiout_network.py b/test/pytest/test_multiout_network.py index b3acbd2be6..15e23ff79a 100644 --- a/test/pytest/test_multiout_network.py +++ b/test/pytest/test_multiout_network.py @@ -8,7 +8,6 @@ from hls4ml.converters import convert_from_keras_model test_root_path = Path(__file__).parent -test_root_path = Path('/tmp/hls4mlprj_multiout_network') @pytest.fixture(scope='module')