Skip to content

Remove RecurrentReuseFactor from the Altera backend - #1581

Merged
JanFSchulte merged 4 commits into
fastmachinelearning:mainfrom
Anandb71:fix/1580-altera-recurrent-reuse-factor
Oct 30, 2026
Merged

JanFSchulte merged 4 commits into
fastmachinelearning:mainfrom
Anandb71:fix/1580-altera-recurrent-reuse-factor

Conversation

@Anandb71

Copy link
Copy Markdown
Contributor

Description

On the Altera backend, RecurrentReuseFactor of SimpleRNN, LSTM and GRU layers had no effect. _register_layer_attributes registered it as a ConfigurableAttribute, so config_from_keras_model(..., backend='Altera') wrote it into every generated config, but init_gru, init_lstm and init_simple_rnn each began with layer.set_attr('recurrent_reuse_factor', reuse_factor), which replaced the user's value with the layer's ReuseFactor.

This follows the recommended option in the issue, matching what #1577 does for Vivado and Vitis:

  • The recurrent_reuse_factor ConfigurableAttribute is no longer registered for SimpleRNN, LSTM and GRU on Altera, so generated configs no longer contain RecurrentReuseFactor. The table_size and table registrations in the same loop are unchanged.
  • The internal recurrent_reuse_factor attribute is still set from ReuseFactor in the three initializers, so the GRU template and set_closest_reuse_factor keep working as before.
  • A config that still sets RecurrentReuseFactor now gets a warning. Layer.__init__ copies every config key into layer.attributes, registered or not, so the key is only present if the user's config set it. The check runs at the start of each initializer, before the internal value is written. _warn_removed_reuse_factors is copied from Fix recurrent layers on Vivado and Vitis, and add SimpleRNN #1577's Vivado backend, since that PR isn't merged yet and the Altera backend doesn't inherit from the Vivado one.
  • docs/ir/attributes.rst: Altera is removed from the recurrent_reuse_factor "Available in" lines for SimpleRNN, LSTM and GRU. Running docs/attr_doc_gen.py on current main also adds VitisUnified to every "Available in" line. That has nothing to do with this change and Fix recurrent layers on Vivado and Vitis, and add SimpleRNN #1577 mentions a separate docs PR that regenerates the whole file, so only the three relevant hunks of the generated output are kept here.

Vivado, Vitis, Catapult and Quartus are not touched. Catapult has the same problem, but as the issue notes, converting an LSTM with Catapult currently fails for an unrelated reason.

Fixes #1580

Type of change

  • Bug fix (non-breaking)
  • New feature (non-breaking)
  • Breaking change (existing configs, APIs or generated code behave differently)
  • New frontend / backend
  • New layer / operator support
  • New configuration option — a new io_type, Strategy, or config attribute
  • Research paper implementation
  • Documentation
  • Build, CI or tooling
  • Refactor / cleanup (no functional change)

Affected areas

Backends:

  • Altera
  • Backend-independent (core IR, optimizer, hls4ml.model)

Frontends:

  • [ ]
  • Not related to a frontend

Components:

  • IR (layers + model graph) / optimizer passes
  • C++ templates / HLS sources under hls4ml/templates/
  • Profiling, reporting or the CLI
  • Packaging, build or CI

Configurations affected and exercised

Backend io_type Strategy Verified
Altera io_parallel n/a (Altera always uses resource for RNNs) pytest (conversion only)

New configuration axis introduced by this PR (if any): none

Impact on generated HLS

  • This PR does not change the generated HLS for existing models.
  • This PR changes the generated HLS. Numbers below.

Numerical behaviour: bit-exact vs main. The value used for the recurrent multiplication was already ReuseFactor; only the config surface and the warning change. No synthesis was run.

Tests

Added test_altera_recurrent_reuse_factor_removed to test/pytest/test_rnn.py, parametrized over SimpleRNN, LSTM and GRU. It checks that config_from_keras_model(..., backend='Altera') no longer writes RecurrentReuseFactor, that setting it (ReuseFactor 8, RecurrentReuseFactor 4) raises a UserWarning and the layer's recurrent_reuse_factor is 8, and that no warning is raised when the key isn't in the config. It only converts, so no HLS tools are needed. All three cases fail on main and pass with the change.

The existing Altera cases in test_rnn.py (-k Altera) give the same 16 failures before and after the change, all at compile() with Could not find ahls, because the Altera HLS compiler isn't installed here. test_rnn.py -k parsing passes (8). test_recurrent_pytorch.py was not run (no PyTorch in this environment).

Test configuration (OS, Python, ML framework version, HLS tool version): Linux, Python 3.12, Keras 3.15.1 with the JAX 0.11.2 backend, NumPy 2.5.4; no HLS tools.

AI assistance disclosure

  • None — no AI tool was used.
  • Assisted — completion, refactoring, docstrings, tests; design and code are mine.
  • Substantial — significant AI-generated portions, reviewed and edited by me.
  • Agentic — produced largely end-to-end by an AI agent from my prompts.

Tool(s) and model(s): Cursor agent CLI (grok-4.7-xhigh)

Where it was used: the whole change (backend edit, test, docs hunk); the test and the scope were trimmed afterwards.

If anything other than None is ticked, confirm all of the following:

  • I am the author of this contribution and take full responsibility for
    it.
    I have read and understood every line and can explain and defend it
    in review.
  • I verified the generated code against real hls4ml and HLS semantics —
    no invented APIs, config keys, pragmas or citations.
  • All numbers, logs and test results quoted in this PR come from runs I
    actually performed, not from agent output.
  • I have the right to submit this work under the project's licence, and to
    my knowledge it does not reproduce third-party code that would conflict
    with it.
  • No AI tool is credited as an author in any commit in this branch.

Checklist

Required:

  • I have read the contributing guidelines.
  • I installed and ran pre-commit on the files I edited.
  • I added tests under test/pytest covering this change (a bug fix should
    add a test that fails on main and passes here).
  • I self-reviewed the full diff and it contains no leftover debug code,
    commented-out blocks or unrelated changes.
  • The AI assistance disclosure above is complete and accurate.

If applicable:

  • Documentation under docs/ updated.
  • No new build or synthesis warnings.
  • Public API / config schema changes are documented.

Release note

On the Altera backend, SimpleRNN, LSTM and GRU layers no longer offer RecurrentReuseFactor, which had no effect; a config that still sets it now warns, and the layer's ReuseFactor applies to both multiplications.

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 fastmachinelearning#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 fastmachinelearning#1580

@JanFSchulte JanFSchulte left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have one relevant comment and one nitpick.

if layer.attributes['n_in'] is None:
raise Exception('Input length of Embedding layer must be specified.')

def _warn_removed_reuse_factors(self, layer, attributes):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This duplicates a function that #1577 adds to the vivado backend. Both should be moved into the generic FPGABackend class.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 474738a: merged main (now that #1577 is in) and moved _warn_removed_reuse_factors into FPGABackend; the Vivado and Altera copies are removed.

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.'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very slightly inaccurate since the reuse value for the LSTM and GRU recurrent multiplications is set to the closest valid value to the ReuseFactor of the layer. Not sure if we need to specify that in the warning for clarity @vloncar?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I left the wording as is for now; happy to mention the closest-valid adjustment for LSTM/GRU if @vloncar thinks it's worth spelling out.

The Vivado and Altera backends had identical copies of the helper; keep a
single one in the shared FPGABackend class.
@JanFSchulte

Copy link
Copy Markdown
Contributor

@copilot resolve the merge conflicts in this pull request

…urrent-reuse-factor

# Conflicts:
#	docs/ir/attributes.rst
#	hls4ml/backends/vivado/vivado_backend.py
@Anandb71

Copy link
Copy Markdown
Contributor Author

@JanFSchulte Copilot can't push to my fork, so I merged main myself in 897aa3b. The conflicts were in vivado_backend.py (kept the new _set_recurrent_cell_schedule; _warn_removed_reuse_factors stays in FPGABackend) and docs/ir/attributes.rst, where RecurrentReuseFactor for RNN layers now lists only Quartus and Catapult. test_attribute_docs.py and the new Altera tests in test_rnn.py pass locally.

@JanFSchulte JanFSchulte added the please test Trigger testing by creating local PR branch label Oct 29, 2026
@JanFSchulte
JanFSchulte merged commit 863a4e9 into fastmachinelearning:main Oct 30, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

please test Trigger testing by creating local PR branch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Altera: RecurrentReuseFactor has no effect

2 participants