Skip to content

Commit 863a4e9

Browse files
authored
Remove RecurrentReuseFactor from the Altera backend (#1581)
* Remove RecurrentReuseFactor from the Altera backend On Altera, the SimpleRNN, LSTM and GRU initializers overwrote the user's RecurrentReuseFactor with the layer's ReuseFactor, so the setting that config_from_keras_model wrote into every config had no effect. Stop registering the setting, matching what #1577 does for Vivado and Vitis, and warn when a config still sets it. The internal recurrent_reuse_factor attribute is still set from ReuseFactor. Fixes #1580 * Move _warn_removed_reuse_factors into FPGABackend The Vivado and Altera backends had identical copies of the helper; keep a single one in the shared FPGABackend class.
1 parent fe74b6b commit 863a4e9

5 files changed

Lines changed: 51 additions & 16 deletions

File tree

‎docs/ir/attributes.rst‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2191,7 +2191,7 @@ Backend-specific attributes
21912191

21922192
* 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.
21932193

2194-
* Available in: Quartus, Catapult, Altera
2194+
* Available in: Quartus, Catapult
21952195

21962196
LSTM
21972197
====
@@ -2295,7 +2295,7 @@ Backend-specific attributes
22952295

22962296
* 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.
22972297

2298-
* Available in: Quartus, Catapult, Altera
2298+
* Available in: Quartus, Catapult
22992299

23002300
GRU
23012301
===
@@ -2401,7 +2401,7 @@ Backend-specific attributes
24012401

24022402
* 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.
24032403

2404-
* Available in: Quartus, Catapult, Altera
2404+
* Available in: Quartus, Catapult
24052405

24062406
Bidirectional
24072407
=============

‎hls4ml/backends/altera/altera_backend.py‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def __init__(self):
3737
self._register_flows()
3838

3939
def _register_layer_attributes(self):
40-
# Add RNN-specific recurrent_reuse_factor attribute
40+
# Add RNN-specific table_size and table attributes
4141
rnn_layers = [
4242
SimpleRNN,
4343
LSTM,
@@ -46,7 +46,6 @@ def _register_layer_attributes(self):
4646

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

281280
@layer_optimizer(GRU)
282281
def init_gru(self, layer):
282+
self._warn_removed_reuse_factors(layer, ['recurrent_reuse_factor'])
283283
reuse_factor = layer.model.config.get_reuse_factor(layer)
284284
layer.set_attr('recurrent_reuse_factor', reuse_factor)
285285

@@ -367,6 +367,7 @@ def init_conv2d(self, layer):
367367

368368
@layer_optimizer(LSTM)
369369
def init_lstm(self, layer):
370+
self._warn_removed_reuse_factors(layer, ['recurrent_reuse_factor'])
370371
reuse_factor = layer.model.config.get_reuse_factor(layer)
371372
layer.set_attr('recurrent_reuse_factor', reuse_factor)
372373

@@ -412,6 +413,7 @@ def init_lstm(self, layer):
412413

413414
@layer_optimizer(SimpleRNN)
414415
def init_simple_rnn(self, layer):
416+
self._warn_removed_reuse_factors(layer, ['recurrent_reuse_factor'])
415417
reuse_factor = layer.model.config.get_reuse_factor(layer)
416418
layer.set_attr('recurrent_reuse_factor', reuse_factor)
417419

‎hls4ml/backends/fpga/fpga_backend.py‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import subprocess
44
from bisect import bisect_left
55
from collections.abc import Iterable
6+
from warnings import warn
67

78
import numpy as np
89

@@ -50,6 +51,7 @@
5051
XnorPrecisionType,
5152
)
5253
from hls4ml.utils import attribute_descriptions as descriptions
54+
from hls4ml.utils.string_utils import convert_to_pascal_case
5355
from hls4ml.writer import get_writer
5456

5557

@@ -299,6 +301,16 @@ def get_closest_reuse_factor(self, valid_rf, chosen_rf):
299301
else:
300302
return before
301303

304+
def _warn_removed_reuse_factors(self, layer, attributes):
305+
"""Warn about reuse factor settings that are no longer used but are still in the config."""
306+
307+
for attribute in attributes:
308+
if attribute in layer.attributes:
309+
warn(
310+
f'{convert_to_pascal_case(attribute)} of layer "{layer.name}" ({layer.class_name}) is no longer used. '
311+
'The ReuseFactor of the layer applies to all of its multiplications.'
312+
)
313+
302314
def set_closest_reuse_factor(self, layer, n_in, n_out, attribute='reuse_factor', include_max_rf=True):
303315
assert attribute is not None, 'Reuse factor attribute cannot be None'
304316

‎hls4ml/backends/vivado/vivado_backend.py‎

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
from hls4ml.report import parse_vivado_report
3939
from hls4ml.utils import attribute_descriptions as descriptions
4040
from hls4ml.utils.einsum_utils import parse_einsum
41-
from hls4ml.utils.string_utils import convert_to_pascal_case
4241

4342

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

691-
def _warn_removed_reuse_factors(self, layer, attributes):
692-
"""Warn about reuse factor settings that are no longer used but are still in the config."""
693-
694-
for attribute in attributes:
695-
if attribute in layer.attributes:
696-
warn(
697-
f'{convert_to_pascal_case(attribute)} of layer "{layer.name}" ({layer.class_name}) is no longer used. '
698-
'The ReuseFactor of the layer applies to all of its multiplications.'
699-
)
700-
701690
def _set_recurrent_cell_schedule(self, layer, n_in, n_state, prefix=''):
702691
"""Set the schedule of the cell of the Resource strategy (see nnet_recurrent.h) from the reuse factor, after
703692
replacing a reuse factor the cell cannot build with the closest one it can."""

‎test/pytest/test_rnn.py‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -872,3 +872,35 @@ def test_bidirectional_gru_reset_after_accuracy(test_case_id, reset_after, stati
872872
keras_prediction = model.predict(X)
873873
hls_prediction = hls_model.predict(X).reshape(keras_prediction.shape)
874874
np.testing.assert_allclose(hls_prediction, keras_prediction, rtol=0.0, atol=5e-2)
875+
876+
877+
@pytest.mark.parametrize('rnn_layer', [SimpleRNN, LSTM, GRU], ids=['SimpleRNN', 'LSTM', 'GRU'])
878+
def test_altera_recurrent_reuse_factor_removed(test_case_id, rnn_layer):
879+
"""Altera ignores RecurrentReuseFactor and warns; the recurrent multiplication uses ReuseFactor."""
880+
model = Sequential()
881+
model.add(Input(shape=(8, 6)))
882+
model.add(rnn_layer(16, name='rnn'))
883+
884+
config = hls4ml.utils.config_from_keras_model(model, granularity='name', backend='Altera')
885+
assert 'RecurrentReuseFactor' not in config['LayerName']['rnn']
886+
887+
config['LayerName']['rnn']['ReuseFactor'] = 8
888+
config['LayerName']['rnn']['RecurrentReuseFactor'] = 4
889+
output_dir = str(test_root_path / test_case_id)
890+
with pytest.warns(UserWarning, match='RecurrentReuseFactor'):
891+
hls_model = hls4ml.converters.convert_from_keras_model(
892+
model, hls_config=config, output_dir=output_dir, backend='Altera'
893+
)
894+
895+
assert hls_model.graph['rnn'].get_attr('recurrent_reuse_factor') == 8
896+
897+
del config['LayerName']['rnn']['RecurrentReuseFactor']
898+
with warnings.catch_warnings(record=True) as caught:
899+
warnings.simplefilter('always')
900+
hls4ml.converters.convert_from_keras_model(
901+
model,
902+
hls_config=config,
903+
output_dir=str(test_root_path / f'{test_case_id}_nowarn'),
904+
backend='Altera',
905+
)
906+
assert not any('RecurrentReuseFactor' in str(w.message) for w in caught)

0 commit comments

Comments
 (0)