fix(bit_exact): add produce_kif handler for ZeroPadding1D/2D#1482
fix(bit_exact): add produce_kif handler for ZeroPadding1D/2D#1482LarocheC wants to merge 2 commits into
Conversation
ZeroPadding1D and ZeroPadding2D had no _produce_kif handler, so any
bit-exact propagation through a model containing them raised
NotImplementedError. Padding inserts exact zeros that fit any signed
fixed-point grid, so the output kif equals the input kif padded with
zeros along the spatial axes.
Adds three pytest cases:
* ZeroPadding1D + Conv1D converts and matches Keras bit-exactly
* ZeroPadding2D + Conv2D converts and matches Keras bit-exactly
* ZeroPadding1D(K-1) + Conv1D(valid) equals Conv1D(causal), guarding
against future drift between the two paths
calad0i
left a comment
There was a problem hiding this comment.
Minor changes needed, good otherwise.
| import hls4ml # noqa: E402 | ||
|
|
||
|
|
||
| def _convert(model, output_dir): |
There was a problem hiding this comment.
bit_exact flow won't be triggered this way. Either use some hgq layers, or use qkeras and set bit_exact=True to trigger it.
There was a problem hiding this comment.
I rewrote the tests to use HGQ2 QConv layers around the ZeroPadding layer so the bit_exact pass actually fires (verified it now hits the handlers; reverting them makes the test raise NotImplementedError). Done in d686d72.
| pad_left = int(layer.attributes['pad_left']) | ||
| pad_right = int(layer.attributes['pad_right']) | ||
| # channels_last: kif shape is (in_width, n_chan); pad axis 0. | ||
| pad_shape = ((pad_left, pad_right),) + ((0, 0),) * (k_in.ndim - 1) |
There was a problem hiding this comment.
Maybe assert for channels_last?
There was a problem hiding this comment.
Added, matching the existing Conv1D/Conv2D handler.
| y_keras = model.predict(xin) | ||
| y_hls = hls_model.predict(xin).reshape(y_keras.shape) | ||
|
|
||
| np.testing.assert_allclose(y_keras, y_hls, atol=1e-2) |
There was a problem hiding this comment.
You can check for array_equal if bit_exact pass fires, if no super large bitwidth is used.
There was a problem hiding this comment.
Switched to assert_array_equal now.
…els_last Address review feedback: * The previous tests used plain Keras layers, which never engage the precision-propagation (bit_exact) pass, so the new ZeroPadding produce_kif handlers were not actually exercised. Rebuild the tests with HGQ2 QConv layers around a plain Keras ZeroPadding layer, which triggers the bit_exact flow and routes kif through the new handlers. * Since the flow is now bit-exact, assert exact equality (assert_array_equal) instead of an approximate tolerance. * Assert channels_last in both ZeroPadding handlers, matching the existing Conv1D/Conv2D handler. * Drop the pure-Keras causal-equivalence test; it never exercised hls4ml and was out of scope for this fix.
Description
ZeroPadding1DandZeroPadding2Dhad no_produce_kifhandler inhls4ml/model/optimizer/passes/bit_exact.py, so bit-exact propagation through any model containing them raisedNotImplementedError. This came up when bringing a TCM-based model with explicit zero-padding (used as a causal-padding alternative for depthwise dilated Conv1D) throughconvert_from_keras_model.Fix: padding inserts exact zeros, which fit any signed fixed-point grid with
k=0, i=0, f=0. The output kif is therefore the input kif padded with zeros along the spatial axis/axes (1D pads axis 0, 2D pads axes 0 and 1 —channels_last).The change is local to
bit_exact.py: two new@_produce_kif.registerhandlers plus the corresponding imports.Type of change
Tests
A new pytest module
test/pytest/test_bit_exact_zeropadding.pycovers three cases:test_zeropadding1d_produce_kif—ZeroPadding1D + Conv1D(valid)converts via Vitis backend andhls_model.predictmatcheskeras.Model.predicttoatol=1e-2.test_zeropadding2d_produce_kif— analogous forZeroPadding2D + Conv2D(valid).test_causal_via_zeropadding1d_matches_native_causal—ZeroPadding1D(pad_left=K-1) + Conv1D(valid)produces exactly the same Keras output asConv1D(padding='causal')(pure-Keras regression guard).Without the patch, tests 1 and 2 fail at conversion time with
NotImplementedError. With the patch, all three pass.Test Configuration:
model.predict()and so are backend-agnostic).pytest test/pytest/test_bit_exact_zeropadding.py -v→ 3 passed.Checklist
pre-commiton the files I edited or added.