From 6271bf3426dc622dad7107472a2ce3287bf337ab Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Wed, 5 Mar 2025 15:39:00 -0600 Subject: [PATCH 01/18] test cvmfs availability in pytests --- test/pytest/generate_ci_yaml.py | 78 +++------------------------------ test/pytest/test_cvmfs_mount.py | 9 ++++ 2 files changed, 16 insertions(+), 71 deletions(-) create mode 100644 test/pytest/test_cvmfs_mount.py diff --git a/test/pytest/generate_ci_yaml.py b/test/pytest/generate_ci_yaml.py index adc3d680ab..643a3dfa3e 100644 --- a/test/pytest/generate_ci_yaml.py +++ b/test/pytest/generate_ci_yaml.py @@ -1,84 +1,20 @@ -import itertools -import os -from pathlib import Path - import yaml -''' -Create a Gitlab CI yml file with a separate entry for each test_* file -in the pytests directory to parallelise the CI jobs. -''' - - template = """ -pytest.{}: +pytest.test_cvmfs_mount: extends: .pytest variables: - PYTESTFILE: {} - EXAMPLEMODEL: {} + PYTESTFILE: 'test_cvmfs_mount.py' + EXAMPLEMODEL: 0 """ -n_test_files_per_yml = int(os.environ.get('N_TESTS_PER_YAML', 4)) - -# Blacklisted tests will be skipped -BLACKLIST = {'test_reduction'} - -# Long-running tests will not be bundled with other tests -LONGLIST = {'test_hgq_layers', 'test_hgq_players', 'test_qkeras', 'test_pytorch_api'} - - -def path_to_name(test_path): - path = Path(test_path) - name = path.stem.replace('test_', '') - return name - - -def batched(iterable, chunk_size): - iterator = iter(iterable) - while chunk := tuple(itertools.islice(iterator, chunk_size)): - yield chunk - - -def uses_example_model(test_filename): - with open(test_filename) as f: - content = f.read() - return 'example-models' in content - - -def generate_test_yaml(test_root='.'): - test_root = Path(test_root) - test_paths = [path for path in test_root.glob('**/test_*.py') if path.stem not in (BLACKLIST | LONGLIST)] - need_example_models = [uses_example_model(path) for path in test_paths] - - idxs = list(range(len(need_example_models))) - idxs = sorted(idxs, key=lambda i: f'{need_example_models[i]}_{path_to_name(test_paths[i])}') - - yml = None - for batch_idxs in batched(idxs, n_test_files_per_yml): - batch_paths: list[Path] = [test_paths[i] for i in batch_idxs] - names = [path_to_name(path) for path in batch_paths] - name = '+'.join(names) - test_files = ' '.join([str(path.relative_to(test_root)) for path in batch_paths]) - batch_need_example_model = int(any([need_example_models[i] for i in batch_idxs])) - diff_yml = yaml.safe_load(template.format(name, test_files, batch_need_example_model)) - if yml is None: - yml = diff_yml - else: - yml.update(diff_yml) - - test_paths = [path for path in test_root.glob('**/test_*.py') if path.stem in LONGLIST] - for path in test_paths: - name = path.stem.replace('test_', '') - test_file = str(path.relative_to(test_root)) - needs_examples = uses_example_model(path) - diff_yml = yaml.safe_load(template.format(name, test_file, int(needs_examples))) - yml.update(diff_yml) - +def generate_fixed_test_yaml(): + yml = yaml.safe_load(template) return yml if __name__ == '__main__': - yml = generate_test_yaml(Path(__file__).parent) + yml = generate_fixed_test_yaml() with open('pytests.yml', 'w') as yamlfile: - yaml.safe_dump(yml, yamlfile) + yaml.safe_dump(yml, yamlfile, default_flow_style=False) diff --git a/test/pytest/test_cvmfs_mount.py b/test/pytest/test_cvmfs_mount.py new file mode 100644 index 0000000000..a361225eaa --- /dev/null +++ b/test/pytest/test_cvmfs_mount.py @@ -0,0 +1,9 @@ +import os + + +def test_vivado_hls_availability(): + + vivado_dir = '/cvmfs/projects.cern.ch/hls4ml/vivado/2020.1_v1/vivado-2020.1_v1' + + contents = os.listdir(vivado_dir) + print("Contents of Vivado HLS bin directory:", contents) From a70d5ae542c7fe908e9eba2eeb226b5d3046da1d Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Wed, 5 Mar 2025 15:55:39 -0600 Subject: [PATCH 02/18] remove quotes from yml template --- test/pytest/generate_ci_yaml.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/pytest/generate_ci_yaml.py b/test/pytest/generate_ci_yaml.py index 643a3dfa3e..efb317326d 100644 --- a/test/pytest/generate_ci_yaml.py +++ b/test/pytest/generate_ci_yaml.py @@ -4,7 +4,7 @@ pytest.test_cvmfs_mount: extends: .pytest variables: - PYTESTFILE: 'test_cvmfs_mount.py' + PYTESTFILE: test_cvmfs_mount.py EXAMPLEMODEL: 0 """ From a69720c7bc6813ca564dd2e73651d6a43b351a17 Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Wed, 5 Mar 2025 16:36:29 -0600 Subject: [PATCH 03/18] test vivado -version command --- test/pytest/test_cvmfs_mount.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/test/pytest/test_cvmfs_mount.py b/test/pytest/test_cvmfs_mount.py index a361225eaa..3e8a46411c 100644 --- a/test/pytest/test_cvmfs_mount.py +++ b/test/pytest/test_cvmfs_mount.py @@ -1,9 +1,26 @@ import os +import subprocess + +import pytest def test_vivado_hls_availability(): - vivado_dir = '/cvmfs/projects.cern.ch/hls4ml/vivado/2020.1_v1/vivado-2020.1_v1' + vivado_bin_dir = '/cvmfs/projects.cern.ch/hls4ml/vivado/2020.1_v1/vivado-2020.1_v1/bin' + + try: + contents = os.listdir(vivado_bin_dir) + print("Contents of Vivado HLS bin directory:", contents) + except Exception as e: + print("Failed to list directory contents:", e) + pytest.fail(f"Unable to access the directory {vivado_bin_dir}") + + os.environ['PATH'] += os.pathsep + vivado_bin_dir - contents = os.listdir(vivado_dir) - print("Contents of Vivado HLS bin directory:", contents) + try: + result = subprocess.run(['vivado', '-version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True) + print("Vivado HLS Version Information:") + print(result.stdout.decode()) + except subprocess.CalledProcessError as e: + print("Failed to execute vivado_hls for version check:", e) + pytest.fail("Vivado HLS version check failed.") From 79dd34566de1b53231ce51a7ce0f1bd17c06d2e6 Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Thu, 6 Mar 2025 10:08:15 -0600 Subject: [PATCH 04/18] fix vivado path --- test/pytest/test_cvmfs_mount.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/pytest/test_cvmfs_mount.py b/test/pytest/test_cvmfs_mount.py index 3e8a46411c..5ccc95184d 100644 --- a/test/pytest/test_cvmfs_mount.py +++ b/test/pytest/test_cvmfs_mount.py @@ -6,7 +6,7 @@ def test_vivado_hls_availability(): - vivado_bin_dir = '/cvmfs/projects.cern.ch/hls4ml/vivado/2020.1_v1/vivado-2020.1_v1/bin' + vivado_bin_dir = '/cvmfs/projects.cern.ch/hls4ml/vivado/2020.1_v1/vivado-2020.1_v1/opt/Xilinx/Vivado/2020.1/bin' try: contents = os.listdir(vivado_bin_dir) From b23e76efef557908d0f0b774b125f4cf495776ae Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Thu, 6 Mar 2025 10:29:52 -0600 Subject: [PATCH 05/18] fix subprocess capture output --- test/pytest/test_cvmfs_mount.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/pytest/test_cvmfs_mount.py b/test/pytest/test_cvmfs_mount.py index 5ccc95184d..f44b4565e1 100644 --- a/test/pytest/test_cvmfs_mount.py +++ b/test/pytest/test_cvmfs_mount.py @@ -18,7 +18,9 @@ def test_vivado_hls_availability(): os.environ['PATH'] += os.pathsep + vivado_bin_dir try: - result = subprocess.run(['vivado', '-version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True) + result = subprocess.run( + ['vivado', '-version'], capture_output=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True + ) print("Vivado HLS Version Information:") print(result.stdout.decode()) except subprocess.CalledProcessError as e: From 28f6eb9c6b1071828e3732c49b4a3829a9c3c276 Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Thu, 6 Mar 2025 10:43:08 -0600 Subject: [PATCH 06/18] fix subprocess stdout --- test/pytest/test_cvmfs_mount.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/test/pytest/test_cvmfs_mount.py b/test/pytest/test_cvmfs_mount.py index f44b4565e1..414344c57d 100644 --- a/test/pytest/test_cvmfs_mount.py +++ b/test/pytest/test_cvmfs_mount.py @@ -18,11 +18,12 @@ def test_vivado_hls_availability(): os.environ['PATH'] += os.pathsep + vivado_bin_dir try: - result = subprocess.run( - ['vivado', '-version'], capture_output=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True - ) + result = subprocess.run(['vivado', '-version'], capture_output=True, check=True, text=True) print("Vivado HLS Version Information:") - print(result.stdout.decode()) + print(result.stdout) + if result.stderr: + print("Error:", result.stderr) except subprocess.CalledProcessError as e: - print("Failed to execute vivado_hls for version check:", e) + print("Failed to execute vivado for version check:", e) + print(e.stderr) pytest.fail("Vivado HLS version check failed.") From 177a02a40fe1570ef2f7780f23191baa196efe4a Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Mon, 10 Mar 2025 10:49:53 -0500 Subject: [PATCH 07/18] source vivado settings64 --- test/pytest/ci-template.yml | 3 +++ test/pytest/test_cvmfs_mount.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/test/pytest/ci-template.yml b/test/pytest/ci-template.yml index 873fe0fec4..eb22ac8696 100644 --- a/test/pytest/ci-template.yml +++ b/test/pytest/ci-template.yml @@ -11,6 +11,9 @@ - git submodule update --init --recursive hls4ml/templates/catapult/ - if [ $EXAMPLEMODEL == 1 ]; then git submodule update --init example-models; fi - pip install .[testing,sr,optimization] + - export XILINX_PATH=/cvmfs/projects.cern.ch/hls4ml/vivado/2020.1_v1/vivado-2020.1_v1/opt/Xilinx + - source ${XILINX_PATH}/Vivado/2020.1/settings64.sh + - source ${XILINX_PATH}/Vitis/2020.1/settings64.sh script: - cd test/pytest - pytest $PYTESTFILE -rA --cov-report xml --cov-report term --cov=hls4ml --junitxml=report.xml --randomly-seed=42 --randomly-dont-reorganize --randomly-dont-reset-seed diff --git a/test/pytest/test_cvmfs_mount.py b/test/pytest/test_cvmfs_mount.py index 414344c57d..3d1c4a24cc 100644 --- a/test/pytest/test_cvmfs_mount.py +++ b/test/pytest/test_cvmfs_mount.py @@ -15,7 +15,7 @@ def test_vivado_hls_availability(): print("Failed to list directory contents:", e) pytest.fail(f"Unable to access the directory {vivado_bin_dir}") - os.environ['PATH'] += os.pathsep + vivado_bin_dir + # os.environ['PATH'] += os.pathsep + vivado_bin_dir try: result = subprocess.run(['vivado', '-version'], capture_output=True, check=True, text=True) From 935632561a2236f655b7726a196bbb7e9c85b589 Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Mon, 10 Mar 2025 11:06:02 -0500 Subject: [PATCH 08/18] add export XILINX_VIVADO --- test/pytest/ci-template.yml | 3 --- test/pytest/test_cvmfs_mount.py | 3 ++- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/test/pytest/ci-template.yml b/test/pytest/ci-template.yml index eb22ac8696..873fe0fec4 100644 --- a/test/pytest/ci-template.yml +++ b/test/pytest/ci-template.yml @@ -11,9 +11,6 @@ - git submodule update --init --recursive hls4ml/templates/catapult/ - if [ $EXAMPLEMODEL == 1 ]; then git submodule update --init example-models; fi - pip install .[testing,sr,optimization] - - export XILINX_PATH=/cvmfs/projects.cern.ch/hls4ml/vivado/2020.1_v1/vivado-2020.1_v1/opt/Xilinx - - source ${XILINX_PATH}/Vivado/2020.1/settings64.sh - - source ${XILINX_PATH}/Vitis/2020.1/settings64.sh script: - cd test/pytest - pytest $PYTESTFILE -rA --cov-report xml --cov-report term --cov=hls4ml --junitxml=report.xml --randomly-seed=42 --randomly-dont-reorganize --randomly-dont-reset-seed diff --git a/test/pytest/test_cvmfs_mount.py b/test/pytest/test_cvmfs_mount.py index 3d1c4a24cc..7a81bd9cdc 100644 --- a/test/pytest/test_cvmfs_mount.py +++ b/test/pytest/test_cvmfs_mount.py @@ -15,7 +15,8 @@ def test_vivado_hls_availability(): print("Failed to list directory contents:", e) pytest.fail(f"Unable to access the directory {vivado_bin_dir}") - # os.environ['PATH'] += os.pathsep + vivado_bin_dir + os.environ['PATH'] += os.pathsep + vivado_bin_dir + os.environ['XILINX_VIVADO'] = '/cvmfs/projects.cern.ch/hls4ml/vivado/2020.1_v1/vivado-2020.1_v1/opt/Xilinx/Vivado/2020.1' try: result = subprocess.run(['vivado', '-version'], capture_output=True, check=True, text=True) From 46ef4a76701f6909415e3820dbea63dde8e395d8 Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Mon, 10 Mar 2025 11:27:27 -0500 Subject: [PATCH 09/18] install ncurses lib --- test/pytest/ci-template.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/test/pytest/ci-template.yml b/test/pytest/ci-template.yml index 873fe0fec4..8009199c0c 100644 --- a/test/pytest/ci-template.yml +++ b/test/pytest/ci-template.yml @@ -11,6 +11,7 @@ - git submodule update --init --recursive hls4ml/templates/catapult/ - if [ $EXAMPLEMODEL == 1 ]; then git submodule update --init example-models; fi - pip install .[testing,sr,optimization] + - sudo yum install ncurses-compat-libs -y script: - cd test/pytest - pytest $PYTESTFILE -rA --cov-report xml --cov-report term --cov=hls4ml --junitxml=report.xml --randomly-seed=42 --randomly-dont-reorganize --randomly-dont-reset-seed From 55d9686ed03e9026f533d53cbec36a1889ac002b Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Mon, 10 Mar 2025 11:46:45 -0500 Subject: [PATCH 10/18] install ncurses-devel --- test/pytest/ci-template.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/pytest/ci-template.yml b/test/pytest/ci-template.yml index 8009199c0c..c6a9012218 100644 --- a/test/pytest/ci-template.yml +++ b/test/pytest/ci-template.yml @@ -11,7 +11,7 @@ - git submodule update --init --recursive hls4ml/templates/catapult/ - if [ $EXAMPLEMODEL == 1 ]; then git submodule update --init example-models; fi - pip install .[testing,sr,optimization] - - sudo yum install ncurses-compat-libs -y + - sudo yum install ncurses-devel -y script: - cd test/pytest - pytest $PYTESTFILE -rA --cov-report xml --cov-report term --cov=hls4ml --junitxml=report.xml --randomly-seed=42 --randomly-dont-reorganize --randomly-dont-reset-seed From 7d86960ebf232034c059a13bfef75afb359b0eba Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Wed, 12 Mar 2025 13:52:13 -0500 Subject: [PATCH 11/18] add install libtinfo.so.6 --- test/pytest/ci-template.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/test/pytest/ci-template.yml b/test/pytest/ci-template.yml index c6a9012218..b944314128 100644 --- a/test/pytest/ci-template.yml +++ b/test/pytest/ci-template.yml @@ -12,6 +12,7 @@ - if [ $EXAMPLEMODEL == 1 ]; then git submodule update --init example-models; fi - pip install .[testing,sr,optimization] - sudo yum install ncurses-devel -y + - sudo yum install libtinfo.so.6 -y script: - cd test/pytest - pytest $PYTESTFILE -rA --cov-report xml --cov-report term --cov=hls4ml --junitxml=report.xml --randomly-seed=42 --randomly-dont-reorganize --randomly-dont-reset-seed From 513045e2b08f485227e18f215311e6e74e2ac2b2 Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Wed, 12 Mar 2025 14:22:20 -0500 Subject: [PATCH 12/18] link libtinfo 5 to 6 --- test/pytest/ci-template.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/test/pytest/ci-template.yml b/test/pytest/ci-template.yml index b944314128..62fc62f231 100644 --- a/test/pytest/ci-template.yml +++ b/test/pytest/ci-template.yml @@ -13,6 +13,7 @@ - pip install .[testing,sr,optimization] - sudo yum install ncurses-devel -y - sudo yum install libtinfo.so.6 -y + - sudo ln -s /lib64/libtinfo.so.6 /lib64/libtinfo.so.5 script: - cd test/pytest - pytest $PYTESTFILE -rA --cov-report xml --cov-report term --cov=hls4ml --junitxml=report.xml --randomly-seed=42 --randomly-dont-reorganize --randomly-dont-reset-seed From 70059e38ee8eb865a911ef59a530754cb5f608c3 Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Wed, 12 Mar 2025 14:36:38 -0500 Subject: [PATCH 13/18] test synthesis keras api --- test/pytest/generate_ci_yaml.py | 2 +- test/pytest/test_keras_api.py | 450 ++------------------------------ 2 files changed, 18 insertions(+), 434 deletions(-) diff --git a/test/pytest/generate_ci_yaml.py b/test/pytest/generate_ci_yaml.py index efb317326d..919b41b83f 100644 --- a/test/pytest/generate_ci_yaml.py +++ b/test/pytest/generate_ci_yaml.py @@ -4,7 +4,7 @@ pytest.test_cvmfs_mount: extends: .pytest variables: - PYTESTFILE: test_cvmfs_mount.py + PYTESTFILE: test_cvmfs_mount.py test_keras_api.py EXAMPLEMODEL: 0 """ diff --git a/test/pytest/test_keras_api.py b/test/pytest/test_keras_api.py index 4bb9f03751..e8c0e0a8de 100644 --- a/test/pytest/test_keras_api.py +++ b/test/pytest/test_keras_api.py @@ -1,23 +1,13 @@ -import math +import json +import os from pathlib import Path import numpy as np import pytest import tensorflow as tf from tensorflow.keras.layers import ( - ELU, Activation, - AveragePooling1D, - AveragePooling2D, - Conv1D, - Conv2D, Dense, - DepthwiseConv1D, - DepthwiseConv2D, - LeakyReLU, - MaxPooling1D, - MaxPooling2D, - PReLU, ) import hls4ml @@ -25,7 +15,7 @@ test_root_path = Path(__file__).parent -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'oneAPI']) +@pytest.mark.parametrize('backend', ['Vivado']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_dense(backend, io_type): model = tf.keras.models.Sequential() @@ -62,6 +52,13 @@ def test_dense(backend, io_type): hls_prediction = hls_model.predict(X_input) + vivado_bin_dir = '/cvmfs/projects.cern.ch/hls4ml/vivado/2020.1_v1/vivado-2020.1_v1/opt/Xilinx/Vivado/2020.1/bin' + os.environ['PATH'] += os.pathsep + vivado_bin_dir + os.environ['XILINX_VIVADO'] = '/cvmfs/projects.cern.ch/hls4ml/vivado/2020.1_v1/vivado-2020.1_v1/opt/Xilinx/Vivado/2020.1' + + data = hls_model.build() + print(data) + np.testing.assert_allclose(hls_prediction, keras_prediction, rtol=1e-2, atol=0.01) assert len(model.layers) + 1 == len(hls_model.get_layers()) @@ -75,423 +72,10 @@ def test_dense(backend, io_type): assert list(hls_model.get_layers())[1].attributes['activation'] == str(model.layers[0].activation).split()[1] -# TODO: add ThresholdedReLU test when it can be made to pass -# https://github.com/fastmachinelearning/hls4ml/issues/376 -@pytest.mark.parametrize( - "activation_function", - [ - Activation(activation='relu', name='relu'), - LeakyReLU(alpha=1.0), - ELU(alpha=1.0), - PReLU( - alpha_initializer="zeros", - ), - Activation(activation='sigmoid', name='sigmoid'), - ], -) -# ThresholdedReLU(theta=1.0)]) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'oneAPI']) -@pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) -def test_activations(activation_function, backend, io_type): - model = tf.keras.models.Sequential() - model.add(Dense(64, input_shape=(1,), name='Dense', kernel_initializer='lecun_uniform', kernel_regularizer=None)) - model.add(activation_function) - - model.compile(optimizer='adam', loss='mse') - X_input = np.random.rand(100, 1) - keras_prediction = model.predict(X_input) - config = hls4ml.utils.config_from_keras_model(model) - output_dir = str(test_root_path / f'hls4mlprj_keras_api_activations_{activation_function.name}_{backend}_{io_type}') - hls_model = hls4ml.converters.convert_from_keras_model( - model, hls_config=config, output_dir=output_dir, backend=backend, io_type=io_type - ) - hls_model.compile() - hls_prediction = hls_model.predict(X_input) - - np.testing.assert_allclose(hls_prediction, keras_prediction, rtol=1e-2, atol=0.01) - - assert len(model.layers) + 1 == len(hls_model.get_layers()) - - assert list(hls_model.get_layers())[2].attributes['class_name'] == activation_function.__class__.__name__ - - -padds_options = ['same', 'valid'] - - -@pytest.mark.parametrize('padds', padds_options) -@pytest.mark.parametrize( - 'backend,strategy', - [ - ('Vivado', 'Resource'), - ('Vivado', 'Latency'), - ('Vitis', 'Resource'), - ('Vitis', 'Latency'), - ('Quartus', 'Resource'), - ('oneAPI', 'Resource'), - ], -) -@pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) -def test_conv1d(padds, backend, strategy, io_type): - model = tf.keras.models.Sequential() - input_shape = (10, 128, 4) - model.add( - Conv1D( - filters=32, - kernel_size=3, - strides=1, - padding=padds, - activation='relu', - input_shape=input_shape[1:], - kernel_initializer='normal', - use_bias=False, - data_format='channels_last', - ) - ) - model.add(Activation(activation='relu')) - model.compile(optimizer='adam', loss='mse') - - X_input = np.random.rand(10, 128, 4) - keras_prediction = model.predict(X_input) - - config = hls4ml.utils.config_from_keras_model(model) - config['Model']['Strategy'] = strategy - output_dir = str(test_root_path / f'hls4mlprj_keras_api_conv1d_{padds}_{backend}_{strategy}_{io_type}') - hls_model = hls4ml.converters.convert_from_keras_model( - model, hls_config=config, output_dir=output_dir, backend=backend, io_type=io_type - ) - hls_model.compile() - hls_prediction = hls_model.predict(X_input).reshape(keras_prediction.shape) - - # 5e-2 might be too high - np.testing.assert_allclose(hls_prediction, keras_prediction, rtol=0, atol=5e-2) - - if not (backend in ['Vivado', 'Vitis'] and io_type == 'io_stream' and padds == 'same'): - # Vivado/Vitis inserts and additional layer for 'same' padding in io_stream - assert len(model.layers) + 2 == len(hls_model.get_layers()) - assert list(hls_model.get_layers())[1].attributes['name'] == model.layers[0]._name - assert list(hls_model.get_layers())[1].attributes['class_name'] == 'Conv1D' - assert list(hls_model.get_layers())[1].attributes['activation'] == str(model.layers[0].activation).split()[1] - assert list(hls_model.get_layers())[1].attributes["in_width"] == model.layers[0]._batch_input_shape[1] - assert list(hls_model.get_layers())[1].attributes['filt_width'] == model.layers[0].kernel_size[0] - assert list(hls_model.get_layers())[1].attributes['n_chan'] == model.layers[0].input_shape[2] - assert list(hls_model.get_layers())[1].attributes['n_filt'] == model.layers[0].filters - assert list(hls_model.get_layers())[1].attributes['stride_width'] == model.layers[0].strides[0] - assert list(hls_model.get_layers())[1].attributes['data_format'] == model.layers[0].data_format - assert list(hls_model.get_layers())[1].attributes["out_width"] == list(model.layers[0].output_shape)[1] - - out_width = math.ceil(float(model.layers[0]._batch_input_shape[2]) / float(model.layers[0].strides[0])) - pad_along_width = max( - (out_width - 1) * model.layers[0].strides[0] - + model.layers[0].kernel_size[0] - - model.layers[0]._batch_input_shape[2], - 0, - ) - pad_left = pad_along_width // 2 - pad_right = pad_along_width - pad_left - - if model.layers[0].padding == 'same': - assert list(hls_model.get_layers())[1].attributes['pad_left'] == pad_left - assert list(hls_model.get_layers())[1].attributes['pad_right'] == pad_right - elif model.layers[0].padding == 'valid': - assert list(hls_model.get_layers())[1].attributes['pad_left'] == 0 - assert list(hls_model.get_layers())[1].attributes['pad_right'] == 0 - - -chans_options = ['channels_last'] -padds_options = ['same', 'valid'] - - -@pytest.mark.parametrize('chans', chans_options) -@pytest.mark.parametrize('padds', padds_options) -@pytest.mark.parametrize( - 'backend,strategy', - [ - ('Vivado', 'Resource'), - ('Vivado', 'Latency'), - ('Vitis', 'Resource'), - ('Vitis', 'Latency'), - ('Quartus', 'Resource'), - ('oneAPI', 'Resource'), - ], -) -@pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) -def test_conv2d(chans, padds, backend, strategy, io_type): - model = tf.keras.models.Sequential() - input_shape = (28, 28, 3) - model.add( - Conv2D( - filters=32, - kernel_size=(4, 4), - strides=(4, 4), - padding=padds, - input_shape=input_shape, - kernel_initializer='normal', - use_bias=False, - data_format=chans, - ) - ) - model.compile(optimizer='adam', loss='mse') - - X_input = np.random.rand(100, *input_shape) - keras_prediction = model.predict(X_input) - - config = hls4ml.utils.config_from_keras_model(model) - config['Model']['Strategy'] = strategy - output_dir = str(test_root_path / f'hls4mlprj_keras_api_conv2d_{backend}_{strategy}_{chans}_{padds}_{io_type}') - hls_model = hls4ml.converters.convert_from_keras_model( - model, hls_config=config, output_dir=output_dir, backend=backend, io_type=io_type - ) - hls_model.compile() - hls_prediction = hls_model.predict(X_input).reshape(keras_prediction.shape) - - # A high tolerance, simply to verify correct functionality - np.testing.assert_allclose(hls_prediction, keras_prediction, rtol=0, atol=5e-2) - - assert len(model.layers) + 1 == len(hls_model.get_layers()) - assert list(hls_model.get_layers())[1].attributes['name'] == model.layers[0]._name - assert list(hls_model.get_layers())[1].attributes['class_name'] == 'Conv2D' - assert list(hls_model.get_layers())[1].attributes['activation'] == str(model.layers[0].activation).split()[1] - assert list(hls_model.get_layers())[1].attributes['filt_width'] == model.layers[0].kernel_size[1] - assert list(hls_model.get_layers())[1].attributes['filt_height'] == model.layers[0].kernel_size[0] - assert list(hls_model.get_layers())[1].attributes['n_filt'] == model.layers[0].filters - assert list(hls_model.get_layers())[1].attributes['stride_width'] == model.layers[0].strides[1] - assert list(hls_model.get_layers())[1].attributes['stride_height'] == model.layers[0].strides[0] - assert list(hls_model.get_layers())[1].attributes['data_format'] == model.layers[0].data_format - - if model.layers[0].data_format == 'channels_first': - assert list(hls_model.get_layers())[1].attributes['n_chan'] == model.layers[0]._batch_input_shape[1] - assert list(hls_model.get_layers())[1].attributes['in_height'] == model.layers[0]._batch_input_shape[2] - assert list(hls_model.get_layers())[1].attributes['in_width'] == model.layers[0]._batch_input_shape[3] - assert list(hls_model.get_layers())[1].attributes['out_height'] == model.layers[0].output_shape[2] - assert list(hls_model.get_layers())[1].attributes['out_width'] == model.layers[0].output_shape[3] - elif model.layers[0].data_format == 'channels_last': - assert list(hls_model.get_layers())[1].attributes['n_chan'] == model.layers[0]._batch_input_shape[3] - assert list(hls_model.get_layers())[1].attributes['in_height'] == model.layers[0]._batch_input_shape[1] - assert list(hls_model.get_layers())[1].attributes['in_width'] == model.layers[0]._batch_input_shape[2] - assert list(hls_model.get_layers())[1].attributes['out_height'] == model.layers[0].output_shape[1] - assert list(hls_model.get_layers())[1].attributes['out_width'] == model.layers[0].output_shape[2] - - if model.layers[0].padding == 'same': - if model.layers[0].data_format == 'channels_first': - out_height = model.layers[0].output_shape[2] - out_width = model.layers[0].output_shape[3] - pad_along_height = max( - (out_height - 1) * model.layers[0].strides[0] - + model.layers[0].kernel_size[0] - - model.layers[0]._batch_input_shape[2], - 0, - ) - pad_along_width = max( - (out_width - 1) * model.layers[0].strides[1] - + model.layers[0].kernel_size[1] - - model.layers[0]._batch_input_shape[3], - 0, - ) - elif model.layers[0].data_format == 'channels_last': - out_height = model.layers[0].output_shape[1] - out_width = model.layers[0].output_shape[2] - pad_along_height = max( - (out_height - 1) * model.layers[0].strides[0] - + model.layers[0].kernel_size[0] - - model.layers[0]._batch_input_shape[1], - 0, - ) - pad_along_width = max( - (out_width - 1) * model.layers[0].strides[1] - + model.layers[0].kernel_size[1] - - model.layers[0]._batch_input_shape[2], - 0, - ) - pad_top = pad_along_height // 2 - pad_bottom = pad_along_height - pad_top - pad_left = pad_along_width // 2 - pad_right = pad_along_width - pad_left - assert list(hls_model.get_layers())[1].attributes['pad_top'] == pad_top - assert list(hls_model.get_layers())[1].attributes['pad_bottom'] == pad_bottom - assert list(hls_model.get_layers())[1].attributes['pad_left'] == pad_left - assert list(hls_model.get_layers())[1].attributes['pad_right'] == pad_right - elif model.layers[0].padding == 'valid': - assert list(hls_model.get_layers())[1].attributes['pad_top'] == 0 - assert list(hls_model.get_layers())[1].attributes['pad_bottom'] == 0 - assert list(hls_model.get_layers())[1].attributes['pad_left'] == 0 - assert list(hls_model.get_layers())[1].attributes['pad_right'] == 0 - - -# Currently only Vivado and Vitis is supported for io_stream. -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis']) -@pytest.mark.parametrize('io_type', ['io_stream']) -def test_depthwise2d(backend, io_type): - ''' - Test proper handling of DepthwiseConv2D - ''' - X = np.random.rand(10, 32, 32, 3) - X = np.round(X * 2**10) * 2**-10 # make it an exact ap_fixed<16,6> - model = tf.keras.models.Sequential() - model.add(DepthwiseConv2D(kernel_size=(3, 3), input_shape=(32, 32, 3))) - model.compile() - - config = hls4ml.utils.config_from_keras_model( - model, granularity='name', default_precision='fixed<32,12>', backend=backend - ) - output_dir = str(test_root_path / f'hls4mlprj_keras_api_depthwiseconv2d_{backend}_{io_type}') - hls_model = hls4ml.converters.convert_from_keras_model( - model, hls_config=config, output_dir=output_dir, backend=backend, io_type=io_type - ) - hls_model.compile() - - y_qkeras = model.predict(X) - y_hls4ml = hls_model.predict(X) - - np.testing.assert_allclose(y_qkeras, y_hls4ml.reshape(y_qkeras.shape), rtol=1e-2, atol=0.01) - - -# Currently only Vivado and Vitis is supported for io_stream. -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis']) -@pytest.mark.parametrize('io_type', ['io_stream']) -def test_depthwise1d(backend, io_type): - ''' - Test proper handling of DepthwiseConv1D. - ''' - X = np.random.rand(10, 32, 3) - X = np.round(X * 2**10) * 2**-10 # make it an exact ap_fixed<16,6> - model = tf.keras.models.Sequential() - model.add(DepthwiseConv1D(kernel_size=3, input_shape=(32, 3))) - model.compile() - - config = hls4ml.utils.config_from_keras_model(model, granularity='name', backend=backend) - output_dir = str(test_root_path / f'hls4mlprj_keras_api_depthwiseconv1d_{backend}_{io_type}') - hls_model = hls4ml.converters.convert_from_keras_model( - model, hls_config=config, output_dir=output_dir, backend=backend, io_type=io_type - ) - hls_model.compile() - - y_qkeras = model.predict(X) - y_hls4ml = hls_model.predict(X) - - np.testing.assert_allclose(y_qkeras, y_hls4ml.reshape(y_qkeras.shape), rtol=1e-2, atol=0.01) - - -pooling_layers = [MaxPooling1D, MaxPooling2D, AveragePooling1D, AveragePooling2D] - - -@pytest.mark.parametrize('pooling', pooling_layers) -@pytest.mark.parametrize('padds', padds_options) -@pytest.mark.parametrize('chans', chans_options) -@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'oneAPI']) -def test_pooling(pooling, padds, chans, backend): - assert '1D' in pooling.__name__ or '2D' in pooling.__name__ - - input_shape = (18, 15, 3) if '2D' in pooling.__name__ else (121, 3) - X_input = np.random.rand(100, *input_shape) - - keras_model = tf.keras.models.Sequential() - keras_model.add(pooling(padding=padds, input_shape=input_shape)) - keras_model.compile() - - hls_cfg = hls4ml.utils.config_from_keras_model(keras_model) - output_dir = str( - test_root_path / f'hls4mlprj_keras_api_pooling_{pooling.__name__}_channels_{chans}_padds_{padds}_backend_{backend}' - ) - hls_model = hls4ml.converters.convert_from_keras_model( - keras_model, hls_config=hls_cfg, output_dir=output_dir, backend=backend - ) - hls_model.compile() - - # Verify accuracy - keras_prediction = keras_model.predict(X_input) - hls_prediction = hls_model.predict(X_input).reshape(keras_prediction.shape) - np.testing.assert_allclose(hls_prediction, keras_prediction, rtol=0, atol=3e-2) - - # Verify correct parsing of layer - hls_pool = list(hls_model.get_layers())[-1] - ker_pool = keras_model.layers[-1] - if '2D' in pooling.__name__: - assert hls_pool.attributes['name'] == ker_pool._name - assert hls_pool.attributes['class_name'][-2] == str(2) - assert hls_pool.attributes['stride_height'] == ker_pool.strides[0] - assert hls_pool.attributes['stride_width'] == ker_pool.strides[1] - assert hls_pool.attributes['pool_height'] == ker_pool.pool_size[1] - assert hls_pool.attributes['pool_width'] == ker_pool.pool_size[0] - - if hls_pool.attributes['data_format'] == 'channels_last': - assert hls_pool.attributes['in_height'] == ker_pool.input_shape[1] - assert hls_pool.attributes['in_width'] == ker_pool.input_shape[2] - assert hls_pool.attributes['n_filt'] == ker_pool.input_shape[3] - elif hls_pool.attributes['data_format'] == 'channels_first': - assert hls_pool.attributes['in_height'] == ker_pool.input_shape[2] - assert hls_pool.attributes['in_width'] == ker_pool.input_shape[3] - assert hls_pool.attributes['n_filt'] == ker_pool.input_shape[1] - - if ker_pool.padding == 'same': - # Height - in_height = ker_pool.input_shape[1] - if ker_pool.data_format == 'channels_first': - in_height = ker_pool.input_shape[2] - out_height = int(math.ceil(float(in_height) / float(ker_pool.strides[0]))) - assert out_height == hls_pool.attributes['out_height'] - if in_height % ker_pool.strides[0] == 0: - pad_along_height = max(ker_pool.pool_size[1] - ker_pool.strides[0], 0) - else: - pad_along_height = max(ker_pool.pool_size[1] - (in_height % ker_pool.strides[0]), 0) - pad_top = pad_along_height // 2 - pad_bottom = pad_along_height - pad_top - assert pad_bottom == hls_pool.attributes['pad_bottom'] - assert pad_top == hls_pool.attributes['pad_top'] - - # Width - in_width = ker_pool.input_shape[2] - if ker_pool.data_format == 'channels_first': - in_height = keras_model.layers[1].input_shape[-1] - out_width = int(math.ceil(float(in_width) / float(ker_pool.strides[1]))) - assert out_width == hls_pool.attributes['out_width'] - if in_width % ker_pool.strides[1] == 0: - pad_along_width = max(ker_pool.pool_size[0] - ker_pool.strides[1], 0) - else: - pad_along_width = max(ker_pool.pool_size[0] - (in_width % ker_pool.strides[1]), 0) - pad_left = pad_along_width // 2 - pad_right = pad_along_width - pad_left - assert pad_left == hls_pool.attributes['pad_left'] - assert pad_right == hls_pool.attributes['pad_right'] - - elif ker_pool.padding == 'valid': - if hls_pool.attributes['data_format'] == 'channels_first': - in_height = ker_pool.input_shape[2] - in_width = ker_pool.input_shape[3] - elif hls_pool.attributes['data_format'] == 'channels_last': - in_height = ker_pool.input_shape[1] - in_width = ker_pool.input_shape[2] - - out_width = int(math.ceil(float(in_width - ker_pool.pool_size[0] + 1) / float(ker_pool.strides[1]))) - out_height = int(math.ceil(float(in_height - ker_pool.pool_size[1] + 1) / float(ker_pool.strides[0]))) - - assert hls_pool.attributes['out_height'] == out_height - assert hls_pool.attributes['out_width'] == out_width - assert hls_pool.attributes['pad_top'] == 0 - assert hls_pool.attributes['pad_bottom'] == 0 - assert hls_pool.attributes['pad_left'] == 0 - assert hls_pool.attributes['pad_right'] == 0 - - elif '1D' in pooling.__name__: - assert hls_pool.attributes['name'] == ker_pool._name - assert hls_pool.attributes['class_name'][-2] == str(1) - assert hls_pool.attributes['n_in'] == ker_pool.input_shape[1] - assert hls_pool.attributes['n_filt'] == ker_pool.input_shape[2] - assert hls_pool.attributes['pool_width'] == ker_pool.pool_size[0] - assert hls_pool.attributes['stride_width'] == ker_pool.strides[0] - - out_same = math.ceil(float(ker_pool.input_shape[1]) / float(ker_pool.strides[0])) - out_valid = math.ceil(float(ker_pool.input_shape[1] - ker_pool.pool_size[0] + 1) / ker_pool.strides[0]) - - if ker_pool.padding == 'same': - assert hls_pool.attributes['n_out'] == out_same - if ker_pool.input_shape[1] % ker_pool.strides[0] == 0: - pad_along_width = max(ker_pool.pool_size[0] - ker_pool.strides[0], 0) - else: - pad_along_width = max(ker_pool.pool_size[0] - (ker_pool.input_shape[1] % ker_pool.strides[0]), 0) - assert hls_pool.attributes['pad_left'] == pad_along_width // 2 - assert hls_pool.attributes['pad_right'] == pad_along_width - pad_along_width // 2 - - elif ker_pool.padding == 'valid': - assert hls_pool.attributes['n_out'] == out_valid - assert hls_pool.attributes['pad_left'] == 0 - assert hls_pool.attributes['pad_right'] == 0 +def compare_synthesis(data, filename): + with open(filename, "w") as fp: + baseline = json.dump(data, fp) + if data == baseline: + return True + else: + return False From f56c5d8a96510f33e001cc0fcaacccbe2dd75b25 Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Wed, 12 Mar 2025 15:12:00 -0500 Subject: [PATCH 14/18] test vitis installation --- test/pytest/test_cvmfs_mount.py | 42 +++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/test/pytest/test_cvmfs_mount.py b/test/pytest/test_cvmfs_mount.py index 7a81bd9cdc..17875ebf4c 100644 --- a/test/pytest/test_cvmfs_mount.py +++ b/test/pytest/test_cvmfs_mount.py @@ -28,3 +28,45 @@ def test_vivado_hls_availability(): print("Failed to execute vivado for version check:", e) print(e.stderr) pytest.fail("Vivado HLS version check failed.") + + +def test_vitis_availability(): + + base_path = '/cvmfs/projects.cern.ch/hls4ml/vivado/2020.1_v1/vivado-2020.1_v1/opt/Xilinx/Vitis/2020.1' + vitis_path = "/opt/Xilinx/Vitis/2020.1" + original_paths = ( + "/opt/Xilinx/Vitis/2020.1/bin:" + + "/opt/Xilinx/Vitis/2020.1/gnu/microblaze/lin/bin:" + + "/opt/Xilinx/Vitis/2020.1/gnu/arm/lin/bin:" + + "/opt/Xilinx/Vitis/2020.1/gnu/microblaze/linux_toolchain/lin64_le/bin:" + + "/opt/Xilinx/Vitis/2020.1/gnu/aarch32/lin/gcc-arm-linux-gnueabi/bin:" + + "/opt/Xilinx/Vitis/2020.1/gnu/aarch32/lin/gcc-arm-none-eabi/bin:" + + "/opt/Xilinx/Vitis/2020.1/gnu/aarch64/lin/aarch64-linux/bin:" + + "/opt/Xilinx/Vitis/2020.1/gnu/aarch64/lin/aarch64-none/bin:" + + "/opt/Xilinx/Vitis/2020.1/gnu/armr5/lin/gcc-arm-none-eabi/bin:" + + "/opt/Xilinx/Vitis/2020.1/tps/lnx64/cmake-3.3.2/bin:" + + "/opt/Xilinx/Vitis/2020.1/cardano/bin" + ) + + update_environment(base_path, original_paths, vitis_path) + + try: + result = subprocess.run(['vitis', '-version'], capture_output=True, check=True, text=True) + print("Vivado HLS Version Information:") + print(result.stdout) + if result.stderr: + print("Error:", result.stderr) + except subprocess.CalledProcessError as e: + print("Failed to execute vivado for version check:", e) + print(e.stderr) + pytest.fail("Vivado HLS version check failed.") + + +def update_environment(base_path, original_paths, vivado_path): + # Append the new paths to the PATH environment variable + for path in original_paths.split(':'): + full_path = os.path.join(base_path, path.lstrip('/')) # Remove leading '/' to correctly join paths + os.environ['PATH'] += os.pathsep + full_path + + # Set the XILINX_VIVADO environment variable + os.environ['XILINX_VIVADO'] = os.path.join(base_path, vivado_path.lstrip('/')) From c4f5618e159a4bc28ca367bb46e15dd931c77f81 Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Wed, 12 Mar 2025 15:28:26 -0500 Subject: [PATCH 15/18] bug fix base_dir --- test/pytest/test_cvmfs_mount.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/test/pytest/test_cvmfs_mount.py b/test/pytest/test_cvmfs_mount.py index 17875ebf4c..da756f3aab 100644 --- a/test/pytest/test_cvmfs_mount.py +++ b/test/pytest/test_cvmfs_mount.py @@ -4,7 +4,7 @@ import pytest -def test_vivado_hls_availability(): +def test_vivado_availability(): vivado_bin_dir = '/cvmfs/projects.cern.ch/hls4ml/vivado/2020.1_v1/vivado-2020.1_v1/opt/Xilinx/Vivado/2020.1/bin' @@ -20,19 +20,19 @@ def test_vivado_hls_availability(): try: result = subprocess.run(['vivado', '-version'], capture_output=True, check=True, text=True) - print("Vivado HLS Version Information:") + print("Vivado Version Information:") print(result.stdout) if result.stderr: print("Error:", result.stderr) except subprocess.CalledProcessError as e: print("Failed to execute vivado for version check:", e) print(e.stderr) - pytest.fail("Vivado HLS version check failed.") + pytest.fail("Vivado version check failed.") def test_vitis_availability(): - base_path = '/cvmfs/projects.cern.ch/hls4ml/vivado/2020.1_v1/vivado-2020.1_v1/opt/Xilinx/Vitis/2020.1' + base_path = '/cvmfs/projects.cern.ch/hls4ml/vivado/2020.1_v1/vivado-2020.1_v1' vitis_path = "/opt/Xilinx/Vitis/2020.1" original_paths = ( "/opt/Xilinx/Vitis/2020.1/bin:" @@ -50,16 +50,20 @@ def test_vitis_availability(): update_environment(base_path, original_paths, vitis_path) + vivado_bin_dir = '/cvmfs/projects.cern.ch/hls4ml/vivado/2020.1_v1/vivado-2020.1_v1/opt/Xilinx/Vivado/2020.1/bin' + os.environ['PATH'] += os.pathsep + vivado_bin_dir + os.environ['XILINX_VIVADO'] = '/cvmfs/projects.cern.ch/hls4ml/vivado/2020.1_v1/vivado-2020.1_v1/opt/Xilinx/Vivado/2020.1' + try: result = subprocess.run(['vitis', '-version'], capture_output=True, check=True, text=True) - print("Vivado HLS Version Information:") + print("Vitis Version Information:") print(result.stdout) if result.stderr: print("Error:", result.stderr) except subprocess.CalledProcessError as e: - print("Failed to execute vivado for version check:", e) + print("Failed to execute vitis for version check:", e) print(e.stderr) - pytest.fail("Vivado HLS version check failed.") + pytest.fail("Vitis version check failed.") def update_environment(base_path, original_paths, vivado_path): @@ -69,4 +73,4 @@ def update_environment(base_path, original_paths, vivado_path): os.environ['PATH'] += os.pathsep + full_path # Set the XILINX_VIVADO environment variable - os.environ['XILINX_VIVADO'] = os.path.join(base_path, vivado_path.lstrip('/')) + os.environ['XILINX_VITIS'] = os.path.join(base_path, vivado_path.lstrip('/')) From 56540296f63c2aa0aea70c5634aa64a79f6db47e Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Wed, 12 Mar 2025 15:38:40 -0500 Subject: [PATCH 16/18] test synthesis vitis keras api --- test/pytest/test_cvmfs_mount.py | 2 +- test/pytest/test_keras_api.py | 30 +++++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/test/pytest/test_cvmfs_mount.py b/test/pytest/test_cvmfs_mount.py index da756f3aab..a0c4f8f182 100644 --- a/test/pytest/test_cvmfs_mount.py +++ b/test/pytest/test_cvmfs_mount.py @@ -55,7 +55,7 @@ def test_vitis_availability(): os.environ['XILINX_VIVADO'] = '/cvmfs/projects.cern.ch/hls4ml/vivado/2020.1_v1/vivado-2020.1_v1/opt/Xilinx/Vivado/2020.1' try: - result = subprocess.run(['vitis', '-version'], capture_output=True, check=True, text=True) + result = subprocess.run(['vitis', '--help'], capture_output=True, check=True, text=True) print("Vitis Version Information:") print(result.stdout) if result.stderr: diff --git a/test/pytest/test_keras_api.py b/test/pytest/test_keras_api.py index e8c0e0a8de..ef629df76c 100644 --- a/test/pytest/test_keras_api.py +++ b/test/pytest/test_keras_api.py @@ -15,7 +15,7 @@ test_root_path = Path(__file__).parent -@pytest.mark.parametrize('backend', ['Vivado']) +@pytest.mark.parametrize('backend', ['Vivado', 'Vitis']) @pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream']) def test_dense(backend, io_type): model = tf.keras.models.Sequential() @@ -56,6 +56,24 @@ def test_dense(backend, io_type): os.environ['PATH'] += os.pathsep + vivado_bin_dir os.environ['XILINX_VIVADO'] = '/cvmfs/projects.cern.ch/hls4ml/vivado/2020.1_v1/vivado-2020.1_v1/opt/Xilinx/Vivado/2020.1' + base_path = '/cvmfs/projects.cern.ch/hls4ml/vivado/2020.1_v1/vivado-2020.1_v1' + vitis_path = "/opt/Xilinx/Vitis/2020.1" + original_paths = ( + "/opt/Xilinx/Vitis/2020.1/bin:" + + "/opt/Xilinx/Vitis/2020.1/gnu/microblaze/lin/bin:" + + "/opt/Xilinx/Vitis/2020.1/gnu/arm/lin/bin:" + + "/opt/Xilinx/Vitis/2020.1/gnu/microblaze/linux_toolchain/lin64_le/bin:" + + "/opt/Xilinx/Vitis/2020.1/gnu/aarch32/lin/gcc-arm-linux-gnueabi/bin:" + + "/opt/Xilinx/Vitis/2020.1/gnu/aarch32/lin/gcc-arm-none-eabi/bin:" + + "/opt/Xilinx/Vitis/2020.1/gnu/aarch64/lin/aarch64-linux/bin:" + + "/opt/Xilinx/Vitis/2020.1/gnu/aarch64/lin/aarch64-none/bin:" + + "/opt/Xilinx/Vitis/2020.1/gnu/armr5/lin/gcc-arm-none-eabi/bin:" + + "/opt/Xilinx/Vitis/2020.1/tps/lnx64/cmake-3.3.2/bin:" + + "/opt/Xilinx/Vitis/2020.1/cardano/bin" + ) + + update_environment(base_path, original_paths, vitis_path) + data = hls_model.build() print(data) @@ -79,3 +97,13 @@ def compare_synthesis(data, filename): return True else: return False + + +def update_environment(base_path, original_paths, vivado_path): + # Append the new paths to the PATH environment variable + for path in original_paths.split(':'): + full_path = os.path.join(base_path, path.lstrip('/')) # Remove leading '/' to correctly join paths + os.environ['PATH'] += os.pathsep + full_path + + # Set the XILINX_VIVADO environment variable + os.environ['XILINX_VITIS'] = os.path.join(base_path, vivado_path.lstrip('/')) From e6d96659583cf52936005e2dae1b7f6d102ba23e Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Wed, 12 Mar 2025 15:52:46 -0500 Subject: [PATCH 17/18] test symbolic link for vivado --- test/pytest/ci-template.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/pytest/ci-template.yml b/test/pytest/ci-template.yml index 62fc62f231..09d18b136f 100644 --- a/test/pytest/ci-template.yml +++ b/test/pytest/ci-template.yml @@ -14,9 +14,12 @@ - sudo yum install ncurses-devel -y - sudo yum install libtinfo.so.6 -y - sudo ln -s /lib64/libtinfo.so.6 /lib64/libtinfo.so.5 + - sudo ln -s /cvmfs/projects.cern.ch/hls4ml/vivado/2020.1_v1/vivado-2020.1_v1/opt/Xilinx /opt/Xilinx + - source /opt/Xilinx/Vivado/2020.1/settings64.sh + - vivado -version script: - cd test/pytest - - pytest $PYTESTFILE -rA --cov-report xml --cov-report term --cov=hls4ml --junitxml=report.xml --randomly-seed=42 --randomly-dont-reorganize --randomly-dont-reset-seed + # - pytest $PYTESTFILE -rA --cov-report xml --cov-report term --cov=hls4ml --junitxml=report.xml --randomly-seed=42 --randomly-dont-reorganize --randomly-dont-reset-seed artifacts: when: always reports: From 31af5e754ba0067361789dcead3499ab125b99bf Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Wed, 12 Mar 2025 16:16:11 -0500 Subject: [PATCH 18/18] code cleanup --- test/pytest/ci-template.yml | 4 +- test/pytest/generate_ci_yaml.py | 2 +- test/pytest/test_cvmfs_mount.py | 76 --------------------------------- test/pytest/test_keras_api.py | 43 ------------------- 4 files changed, 2 insertions(+), 123 deletions(-) delete mode 100644 test/pytest/test_cvmfs_mount.py diff --git a/test/pytest/ci-template.yml b/test/pytest/ci-template.yml index 09d18b136f..378f13ccfc 100644 --- a/test/pytest/ci-template.yml +++ b/test/pytest/ci-template.yml @@ -11,15 +11,13 @@ - git submodule update --init --recursive hls4ml/templates/catapult/ - if [ $EXAMPLEMODEL == 1 ]; then git submodule update --init example-models; fi - pip install .[testing,sr,optimization] - - sudo yum install ncurses-devel -y - sudo yum install libtinfo.so.6 -y - sudo ln -s /lib64/libtinfo.so.6 /lib64/libtinfo.so.5 - sudo ln -s /cvmfs/projects.cern.ch/hls4ml/vivado/2020.1_v1/vivado-2020.1_v1/opt/Xilinx /opt/Xilinx - source /opt/Xilinx/Vivado/2020.1/settings64.sh - - vivado -version script: - cd test/pytest - # - pytest $PYTESTFILE -rA --cov-report xml --cov-report term --cov=hls4ml --junitxml=report.xml --randomly-seed=42 --randomly-dont-reorganize --randomly-dont-reset-seed + - pytest $PYTESTFILE -rA --cov-report xml --cov-report term --cov=hls4ml --junitxml=report.xml --randomly-seed=42 --randomly-dont-reorganize --randomly-dont-reset-seed artifacts: when: always reports: diff --git a/test/pytest/generate_ci_yaml.py b/test/pytest/generate_ci_yaml.py index 919b41b83f..b54906ef97 100644 --- a/test/pytest/generate_ci_yaml.py +++ b/test/pytest/generate_ci_yaml.py @@ -4,7 +4,7 @@ pytest.test_cvmfs_mount: extends: .pytest variables: - PYTESTFILE: test_cvmfs_mount.py test_keras_api.py + PYTESTFILE: test_keras_api.py EXAMPLEMODEL: 0 """ diff --git a/test/pytest/test_cvmfs_mount.py b/test/pytest/test_cvmfs_mount.py deleted file mode 100644 index a0c4f8f182..0000000000 --- a/test/pytest/test_cvmfs_mount.py +++ /dev/null @@ -1,76 +0,0 @@ -import os -import subprocess - -import pytest - - -def test_vivado_availability(): - - vivado_bin_dir = '/cvmfs/projects.cern.ch/hls4ml/vivado/2020.1_v1/vivado-2020.1_v1/opt/Xilinx/Vivado/2020.1/bin' - - try: - contents = os.listdir(vivado_bin_dir) - print("Contents of Vivado HLS bin directory:", contents) - except Exception as e: - print("Failed to list directory contents:", e) - pytest.fail(f"Unable to access the directory {vivado_bin_dir}") - - os.environ['PATH'] += os.pathsep + vivado_bin_dir - os.environ['XILINX_VIVADO'] = '/cvmfs/projects.cern.ch/hls4ml/vivado/2020.1_v1/vivado-2020.1_v1/opt/Xilinx/Vivado/2020.1' - - try: - result = subprocess.run(['vivado', '-version'], capture_output=True, check=True, text=True) - print("Vivado Version Information:") - print(result.stdout) - if result.stderr: - print("Error:", result.stderr) - except subprocess.CalledProcessError as e: - print("Failed to execute vivado for version check:", e) - print(e.stderr) - pytest.fail("Vivado version check failed.") - - -def test_vitis_availability(): - - base_path = '/cvmfs/projects.cern.ch/hls4ml/vivado/2020.1_v1/vivado-2020.1_v1' - vitis_path = "/opt/Xilinx/Vitis/2020.1" - original_paths = ( - "/opt/Xilinx/Vitis/2020.1/bin:" - + "/opt/Xilinx/Vitis/2020.1/gnu/microblaze/lin/bin:" - + "/opt/Xilinx/Vitis/2020.1/gnu/arm/lin/bin:" - + "/opt/Xilinx/Vitis/2020.1/gnu/microblaze/linux_toolchain/lin64_le/bin:" - + "/opt/Xilinx/Vitis/2020.1/gnu/aarch32/lin/gcc-arm-linux-gnueabi/bin:" - + "/opt/Xilinx/Vitis/2020.1/gnu/aarch32/lin/gcc-arm-none-eabi/bin:" - + "/opt/Xilinx/Vitis/2020.1/gnu/aarch64/lin/aarch64-linux/bin:" - + "/opt/Xilinx/Vitis/2020.1/gnu/aarch64/lin/aarch64-none/bin:" - + "/opt/Xilinx/Vitis/2020.1/gnu/armr5/lin/gcc-arm-none-eabi/bin:" - + "/opt/Xilinx/Vitis/2020.1/tps/lnx64/cmake-3.3.2/bin:" - + "/opt/Xilinx/Vitis/2020.1/cardano/bin" - ) - - update_environment(base_path, original_paths, vitis_path) - - vivado_bin_dir = '/cvmfs/projects.cern.ch/hls4ml/vivado/2020.1_v1/vivado-2020.1_v1/opt/Xilinx/Vivado/2020.1/bin' - os.environ['PATH'] += os.pathsep + vivado_bin_dir - os.environ['XILINX_VIVADO'] = '/cvmfs/projects.cern.ch/hls4ml/vivado/2020.1_v1/vivado-2020.1_v1/opt/Xilinx/Vivado/2020.1' - - try: - result = subprocess.run(['vitis', '--help'], capture_output=True, check=True, text=True) - print("Vitis Version Information:") - print(result.stdout) - if result.stderr: - print("Error:", result.stderr) - except subprocess.CalledProcessError as e: - print("Failed to execute vitis for version check:", e) - print(e.stderr) - pytest.fail("Vitis version check failed.") - - -def update_environment(base_path, original_paths, vivado_path): - # Append the new paths to the PATH environment variable - for path in original_paths.split(':'): - full_path = os.path.join(base_path, path.lstrip('/')) # Remove leading '/' to correctly join paths - os.environ['PATH'] += os.pathsep + full_path - - # Set the XILINX_VIVADO environment variable - os.environ['XILINX_VITIS'] = os.path.join(base_path, vivado_path.lstrip('/')) diff --git a/test/pytest/test_keras_api.py b/test/pytest/test_keras_api.py index ef629df76c..d84ce0b6d0 100644 --- a/test/pytest/test_keras_api.py +++ b/test/pytest/test_keras_api.py @@ -1,5 +1,3 @@ -import json -import os from pathlib import Path import numpy as np @@ -52,28 +50,6 @@ def test_dense(backend, io_type): hls_prediction = hls_model.predict(X_input) - vivado_bin_dir = '/cvmfs/projects.cern.ch/hls4ml/vivado/2020.1_v1/vivado-2020.1_v1/opt/Xilinx/Vivado/2020.1/bin' - os.environ['PATH'] += os.pathsep + vivado_bin_dir - os.environ['XILINX_VIVADO'] = '/cvmfs/projects.cern.ch/hls4ml/vivado/2020.1_v1/vivado-2020.1_v1/opt/Xilinx/Vivado/2020.1' - - base_path = '/cvmfs/projects.cern.ch/hls4ml/vivado/2020.1_v1/vivado-2020.1_v1' - vitis_path = "/opt/Xilinx/Vitis/2020.1" - original_paths = ( - "/opt/Xilinx/Vitis/2020.1/bin:" - + "/opt/Xilinx/Vitis/2020.1/gnu/microblaze/lin/bin:" - + "/opt/Xilinx/Vitis/2020.1/gnu/arm/lin/bin:" - + "/opt/Xilinx/Vitis/2020.1/gnu/microblaze/linux_toolchain/lin64_le/bin:" - + "/opt/Xilinx/Vitis/2020.1/gnu/aarch32/lin/gcc-arm-linux-gnueabi/bin:" - + "/opt/Xilinx/Vitis/2020.1/gnu/aarch32/lin/gcc-arm-none-eabi/bin:" - + "/opt/Xilinx/Vitis/2020.1/gnu/aarch64/lin/aarch64-linux/bin:" - + "/opt/Xilinx/Vitis/2020.1/gnu/aarch64/lin/aarch64-none/bin:" - + "/opt/Xilinx/Vitis/2020.1/gnu/armr5/lin/gcc-arm-none-eabi/bin:" - + "/opt/Xilinx/Vitis/2020.1/tps/lnx64/cmake-3.3.2/bin:" - + "/opt/Xilinx/Vitis/2020.1/cardano/bin" - ) - - update_environment(base_path, original_paths, vitis_path) - data = hls_model.build() print(data) @@ -88,22 +64,3 @@ def test_dense(backend, io_type): assert list(hls_model.get_layers())[1].attributes['n_out'] == model.layers[0].output_shape[1:][0] assert list(hls_model.get_layers())[2].attributes['activation'] == str(model.layers[1].activation).split()[1] assert list(hls_model.get_layers())[1].attributes['activation'] == str(model.layers[0].activation).split()[1] - - -def compare_synthesis(data, filename): - with open(filename, "w") as fp: - baseline = json.dump(data, fp) - if data == baseline: - return True - else: - return False - - -def update_environment(base_path, original_paths, vivado_path): - # Append the new paths to the PATH environment variable - for path in original_paths.split(':'): - full_path = os.path.join(base_path, path.lstrip('/')) # Remove leading '/' to correctly join paths - os.environ['PATH'] += os.pathsep + full_path - - # Set the XILINX_VIVADO environment variable - os.environ['XILINX_VITIS'] = os.path.join(base_path, vivado_path.lstrip('/'))