Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/ir/attributes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2191,7 +2191,7 @@ Backend-specific attributes

* The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency.

* Available in: Quartus, Catapult, Altera
* Available in: Quartus, Catapult

LSTM
====
Expand Down Expand Up @@ -2295,7 +2295,7 @@ Backend-specific attributes

* The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency.

* Available in: Quartus, Catapult, Altera
* Available in: Quartus, Catapult

GRU
===
Expand Down Expand Up @@ -2401,7 +2401,7 @@ Backend-specific attributes

* The number of times each multiplier is used by controlling the amount of pipelining/unrolling. A lower number results in more parallelism and lower latency at the expense of the resources used. Reuse factor = 1 corresponds to all multiplications executed in parallel, and hence, the lowest possible latency.

* Available in: Quartus, Catapult, Altera
* Available in: Quartus, Catapult

Bidirectional
=============
Expand Down
6 changes: 4 additions & 2 deletions hls4ml/backends/altera/altera_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def __init__(self):
self._register_flows()

def _register_layer_attributes(self):
# Add RNN-specific recurrent_reuse_factor attribute
# Add RNN-specific table_size and table attributes
rnn_layers = [
SimpleRNN,
LSTM,
Expand All @@ -46,7 +46,6 @@ def _register_layer_attributes(self):

for layer in rnn_layers:
attrs = self.attribute_map.get(layer, [])
attrs.append(ConfigurableAttribute('recurrent_reuse_factor', default=1, description=descriptions.reuse_factor))
attrs.append(ConfigurableAttribute('table_size', default=1024, description=descriptions.table_size))
attrs.append(TypeAttribute('table', default=FixedPrecisionType(18, 8), description=descriptions.table_type))
self.attribute_map[layer] = attrs
Expand Down Expand Up @@ -280,6 +279,7 @@ def init_embed(self, layer):

@layer_optimizer(GRU)
def init_gru(self, layer):
self._warn_removed_reuse_factors(layer, ['recurrent_reuse_factor'])
reuse_factor = layer.model.config.get_reuse_factor(layer)
layer.set_attr('recurrent_reuse_factor', reuse_factor)

Expand Down Expand Up @@ -367,6 +367,7 @@ def init_conv2d(self, layer):

@layer_optimizer(LSTM)
def init_lstm(self, layer):
self._warn_removed_reuse_factors(layer, ['recurrent_reuse_factor'])
reuse_factor = layer.model.config.get_reuse_factor(layer)
layer.set_attr('recurrent_reuse_factor', reuse_factor)

Expand Down Expand Up @@ -412,6 +413,7 @@ def init_lstm(self, layer):

@layer_optimizer(SimpleRNN)
def init_simple_rnn(self, layer):
self._warn_removed_reuse_factors(layer, ['recurrent_reuse_factor'])
reuse_factor = layer.model.config.get_reuse_factor(layer)
layer.set_attr('recurrent_reuse_factor', reuse_factor)

Expand Down
12 changes: 12 additions & 0 deletions hls4ml/backends/fpga/fpga_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import subprocess
from bisect import bisect_left
from collections.abc import Iterable
from warnings import warn

import numpy as np

Expand Down Expand Up @@ -50,6 +51,7 @@
XnorPrecisionType,
)
from hls4ml.utils import attribute_descriptions as descriptions
from hls4ml.utils.string_utils import convert_to_pascal_case
from hls4ml.writer import get_writer


Expand Down Expand Up @@ -299,6 +301,16 @@ def get_closest_reuse_factor(self, valid_rf, chosen_rf):
else:
return before

def _warn_removed_reuse_factors(self, layer, attributes):
"""Warn about reuse factor settings that are no longer used but are still in the config."""

for attribute in attributes:
if attribute in layer.attributes:
warn(
f'{convert_to_pascal_case(attribute)} of layer "{layer.name}" ({layer.class_name}) is no longer used. '
'The ReuseFactor of the layer applies to all of its multiplications.'
)

def set_closest_reuse_factor(self, layer, n_in, n_out, attribute='reuse_factor', include_max_rf=True):
assert attribute is not None, 'Reuse factor attribute cannot be None'

Expand Down
11 changes: 0 additions & 11 deletions hls4ml/backends/vivado/vivado_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
from hls4ml.report import parse_vivado_report
from hls4ml.utils import attribute_descriptions as descriptions
from hls4ml.utils.einsum_utils import parse_einsum
from hls4ml.utils.string_utils import convert_to_pascal_case


class VivadoBackend(FPGABackend):
Expand Down Expand Up @@ -688,16 +687,6 @@ def _validate_recurrent_activations(self, layer, attributes):
f'({layer.class_name}) is not supported. Use "linear", "relu", "sigmoid" or "tanh".'
)

def _warn_removed_reuse_factors(self, layer, attributes):
"""Warn about reuse factor settings that are no longer used but are still in the config."""

for attribute in attributes:
if attribute in layer.attributes:
warn(
f'{convert_to_pascal_case(attribute)} of layer "{layer.name}" ({layer.class_name}) is no longer used. '
'The ReuseFactor of the layer applies to all of its multiplications.'
)

def _set_recurrent_cell_schedule(self, layer, n_in, n_state, prefix=''):
"""Set the schedule of the cell of the Resource strategy (see nnet_recurrent.h) from the reuse factor, after
replacing a reuse factor the cell cannot build with the closest one it can."""
Expand Down
32 changes: 32 additions & 0 deletions test/pytest/test_rnn.py
Original file line number Diff line number Diff line change
Expand Up @@ -872,3 +872,35 @@ def test_bidirectional_gru_reset_after_accuracy(test_case_id, reset_after, stati
keras_prediction = model.predict(X)
hls_prediction = hls_model.predict(X).reshape(keras_prediction.shape)
np.testing.assert_allclose(hls_prediction, keras_prediction, rtol=0.0, atol=5e-2)


@pytest.mark.parametrize('rnn_layer', [SimpleRNN, LSTM, GRU], ids=['SimpleRNN', 'LSTM', 'GRU'])
def test_altera_recurrent_reuse_factor_removed(test_case_id, rnn_layer):
"""Altera ignores RecurrentReuseFactor and warns; the recurrent multiplication uses ReuseFactor."""
model = Sequential()
model.add(Input(shape=(8, 6)))
model.add(rnn_layer(16, name='rnn'))

config = hls4ml.utils.config_from_keras_model(model, granularity='name', backend='Altera')
assert 'RecurrentReuseFactor' not in config['LayerName']['rnn']

config['LayerName']['rnn']['ReuseFactor'] = 8
config['LayerName']['rnn']['RecurrentReuseFactor'] = 4
output_dir = str(test_root_path / test_case_id)
with pytest.warns(UserWarning, match='RecurrentReuseFactor'):
hls_model = hls4ml.converters.convert_from_keras_model(
model, hls_config=config, output_dir=output_dir, backend='Altera'
)

assert hls_model.graph['rnn'].get_attr('recurrent_reuse_factor') == 8

del config['LayerName']['rnn']['RecurrentReuseFactor']
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter('always')
hls4ml.converters.convert_from_keras_model(
model,
hls_config=config,
output_dir=str(test_root_path / f'{test_case_id}_nowarn'),
backend='Altera',
)
assert not any('RecurrentReuseFactor' in str(w.message) for w in caught)
Loading