@@ -63,6 +63,18 @@ def _build_model(config):
6363 return keras .Model ([img_in , seq_in ], x )
6464
6565
66+ def _rtl_predict (comb , path , data ):
67+ """Write the RTL project, compile the simulation emulator, and run bit-accurate inference."""
68+ rtl_model = RTLModel (comb , str (path ), "model" , flavor = "verilog" , latency_cutoff = 5 , clock_period = 5.0 , print_latency = False )
69+ rtl_model .write ()
70+ rtl_model .compile ()
71+ if isinstance (data , list ):
72+ data = [a .astype (np .float64 ) for a in data ]
73+ else :
74+ data = data .astype (np .float64 )
75+ return rtl_model .predict (data )
76+
77+
6678def _random_prune (layer , fraction , rng ):
6779 """Zero exactly ``fraction`` of the layer's weights via its pruning mask."""
6880 mask = layer .pruning_layer .mask
@@ -123,20 +135,12 @@ def test_alkaid_rtl_matches_model(tmp_path):
123135 seq = rng .integers (0 , 16 , size = (n_samples ,) + SEQ_SHAPE ).astype ("float32" ) / 16.0
124136
125137 reference = np .asarray (model ([img , seq ]), dtype = np .float64 ) # (n_samples, OUT_FEATURES)
126- emulated = np .stack (
127- [
128- np .asarray (comb (np .concatenate ([img [n ].ravel (), seq [n ].ravel ()]), quantize = True ), dtype = np .float64 )
129- for n in range (n_samples )
130- ]
131- )
138+ emulated = _rtl_predict (comb , tmp_path , [img , seq ])
139+ assert (tmp_path / "src" / "model.v" ).exists ()
132140
133141 assert np .any (reference != 0 ) # the comparison is non-trivial
134142 np .testing .assert_allclose (emulated , reference , rtol = 0 , atol = 1e-9 )
135143
136- # Generate the actual RTL project from the same combinational logic.
137- RTLModel (comb , str (tmp_path ), "model" , flavor = "verilog" , print_latency = False ).write ()
138- assert (tmp_path / "src" / "model.v" ).exists ()
139-
140144
141145# --- Coverage of every PQ layer the keras Alkaid plugin handles ---------------
142146
@@ -231,19 +235,12 @@ def test_alkaid_conversion_all_layer_types(tmp_path):
231235 img = rng .integers (0 , 16 , size = (n_samples ,) + ALL_IMG_SHAPE ).astype ("float32" ) / 16.0
232236 seq = rng .integers (0 , 16 , size = (n_samples ,) + ALL_SEQ_SHAPE ).astype ("float32" ) / 16.0
233237 reference = np .asarray (model ([img , seq ]), dtype = np .float64 )
234- emulated = np .stack (
235- [
236- np .asarray (comb (np .concatenate ([img [n ].ravel (), seq [n ].ravel ()]), quantize = True ), dtype = np .float64 )
237- for n in range (n_samples )
238- ]
239- )
238+ emulated = _rtl_predict (comb , tmp_path , [img , seq ])
239+ assert (tmp_path / "src" / "model.v" ).exists ()
240240
241241 assert np .any (reference != 0 )
242242 np .testing .assert_allclose (emulated , reference , rtol = 0 , atol = 1e-9 )
243243
244- RTLModel (comb , str (tmp_path ), "model" , flavor = "verilog" , print_latency = False ).write ()
245- assert (tmp_path / "src" / "model.v" ).exists ()
246-
247244
248245# --- Per-layer conversion: a model that is a single layer ---------------------
249246
@@ -313,7 +310,7 @@ def _single_layer_model(input_shape, layer, tail=None):
313310
314311
315312@pytest .mark .parametrize ("case_id" , list (_SINGLE_LAYER_CASES ))
316- def test_alkaid_single_layer (case_id ):
313+ def test_alkaid_single_layer (case_id , tmp_path ):
317314 config = pdp_config ()
318315 config .quantization_parameters .enable_quantization = True
319316 input_shape , model = _SINGLE_LAYER_CASES [case_id ](config )
@@ -328,7 +325,7 @@ def test_alkaid_single_layer(case_id):
328325 n_samples = 16
329326 x = rng .integers (0 , 16 , size = (n_samples ,) + input_shape ).astype ("float32" ) / 16.0
330327 reference = np .asarray (model (x ), dtype = np .float64 ).reshape (n_samples , - 1 )
331- emulated = np . stack ([ np . asarray ( comb ( x [ i ]. ravel (), quantize = True ), dtype = np . float64 ) for i in range ( n_samples )] )
328+ emulated = _rtl_predict ( comb , tmp_path , x )
332329
333330 assert np .any (reference != 0 )
334331 np .testing .assert_allclose (emulated , reference , rtol = 0 , atol = 1e-9 )
@@ -370,10 +367,8 @@ def test_alkaid_multihead_attention(tmp_path):
370367 n_samples = 16
371368 x = rng .integers (0 , 16 , size = (n_samples , MHA_SEQ_LEN , MHA_EMBED_DIM )).astype ("float32" ) / 16.0
372369 reference = np .asarray (model (x ), dtype = np .float64 ).reshape (n_samples , - 1 )
373- emulated = np .stack ([np .asarray (comb (x [i ].ravel (), quantize = True ), dtype = np .float64 ) for i in range (n_samples )])
370+ emulated = _rtl_predict (comb , tmp_path , x )
371+ assert (tmp_path / "src" / "model.v" ).exists ()
374372
375373 assert np .any (reference != 0 )
376374 np .testing .assert_allclose (emulated , reference , rtol = 0 , atol = 1e-9 )
377-
378- RTLModel (comb , str (tmp_path ), "model" , flavor = "verilog" , print_latency = False ).write ()
379- assert (tmp_path / "src" / "model.v" ).exists ()
0 commit comments