1- import numpy as np
21from copy import copy
32
4- from hls4ml . model . optimizer import OptimizerPass
5- from hls4ml . model . layers import register_layer
3+ import numpy as np
4+
65from hls4ml .backends .fpga .fpga_layers import PointwiseConv1D , PointwiseConv2D
7- from hls4ml .backends .quartus .passes .convolution_templates import Conv1DConfigTemplate , Conv1DFunctionTemplate , Conv2DConfigTemplate , Conv2DFunctionTemplate , conv1d_config_template , conv2d_config_template , conv_mult_config_template
6+ from hls4ml .backends .quartus .passes .convolution_templates import (
7+ Conv1DConfigTemplate ,
8+ Conv1DFunctionTemplate ,
9+ Conv2DConfigTemplate ,
10+ Conv2DFunctionTemplate ,
11+ conv1d_config_template ,
12+ conv2d_config_template ,
13+ conv_mult_config_template ,
14+ )
15+ from hls4ml .model .layers import register_layer
16+ from hls4ml .model .optimizer import OptimizerPass
817
918'''
1019Custom hls4ml layer implementation for 1x1 Conv filters using im2col
1120Allows lower latency andresource usage, due to less loop invocations
1221'''
1322
14- pointwise_conv1d_function_template = 'nnet::pointwise_conv_1d_{data_format}<{input_t}, {output_t}, {config}>({input}, {output}, {w}, {b});'
15- pointwise_conv2d_function_template = 'nnet::pointwise_conv_2d_{data_format}<{input_t}, {output_t}, {config}>({input}, {output}, {w}, {b});'
23+ pointwise_conv1d_function_template = (
24+ 'nnet::pointwise_conv_1d_{data_format}<{input_t}, {output_t}, {config}>({input}, {output}, {w}, {b});'
25+ )
26+ pointwise_conv2d_function_template = (
27+ 'nnet::pointwise_conv_2d_{data_format}<{input_t}, {output_t}, {config}>({input}, {output}, {w}, {b});'
28+ )
1629
1730sepconv1d_include_list = ['nnet_utils/nnet_conv1d.h' ]
1831sepconv2d_include_list = ['nnet_utils/nnet_conv2d.h' ]
1932
33+
2034class PointwiseConv1DConfigTemplate (Conv1DConfigTemplate ):
2135 def __init__ (self ):
2236 super (Conv1DConfigTemplate , self ).__init__ (PointwiseConv1D )
2337 self .template = conv1d_config_template
2438 self .mult_template = conv_mult_config_template
2539
40+
2641class PointwiseConv1DFunctionTemplate (Conv1DFunctionTemplate ):
2742 def __init__ (self ):
2843 super (Conv1DFunctionTemplate , self ).__init__ (PointwiseConv1D , include_header = sepconv1d_include_list )
2944 self .template = pointwise_conv1d_function_template
3045
46+
3147class PointwiseConv2DConfigTemplate (Conv2DConfigTemplate ):
3248 def __init__ (self ):
3349 super (Conv2DConfigTemplate , self ).__init__ (PointwiseConv2D )
3450 self .template = conv2d_config_template
3551 self .mult_template = conv_mult_config_template
3652
53+
3754class PointwiseConv2DFunctionTemplate (Conv2DFunctionTemplate ):
3855 def __init__ (self ):
3956 super (Conv2DFunctionTemplate , self ).__init__ (PointwiseConv2D , include_header = sepconv2d_include_list )
@@ -54,19 +71,25 @@ def register_pointwise(backend):
5471 backend .register_template (PointwiseConv2DConfigTemplate )
5572 backend .register_template (PointwiseConv2DFunctionTemplate )
5673
74+
5775class OptimizePointwiseConv (OptimizerPass ):
5876 def match (self , node ):
59- return node .class_name in ('Conv1D' , 'Conv2D' ) and \
60- node .get_attr ('filt_height' , 1 ) == 1 and \
61- node .get_attr ('filt_width' ) == 1 and \
62- node .model .config .get_config_value ('IOType' ) == 'io_parallel'
77+ return (
78+ node .class_name in ('Conv1D' , 'Conv2D' )
79+ and node .get_attr ('filt_height' , 1 ) == 1
80+ and node .get_attr ('filt_width' ) == 1
81+ and node .model .config .get_config_value ('IOType' ) == 'io_parallel'
82+ )
6383
6484 def transform (self , model , node ):
65- dim = node .__class__ .__name__ [- 2 :] # '1D' or '2D'
66- pw_node = model .make_node ('PointwiseConv' + dim , node .name , copy (node .attributes ), node .inputs .copy (), outputs = node .outputs .copy ())
67- if len (node .weights ['weight' ].data .shape ) == 2 : # This can happen if we assign weights of Dense layer to 1x1 Conv2D
68- pw_node .weights ['weight' ].data = np .expand_dims (node .weights ['weight' ].data , axis = (0 ,1 ))
85+ dim = node .__class__ .__name__ [- 2 :] # '1D' or '2D'
86+ pw_node = model .make_node (
87+ 'PointwiseConv' + dim , node .name , copy (node .attributes ), node .inputs .copy (), outputs = node .outputs .copy ()
88+ )
89+ if len (node .weights ['weight' ].data .shape ) == 2 : # This can happen if we assign weights of Dense layer to 1x1 Conv2D
90+ expand_axis = tuple (range (int (dim [0 ])))
91+ pw_node .weights ['weight' ].data = np .expand_dims (node .weights ['weight' ].data , axis = expand_axis )
6992 pw_node .weights ['bias' ].data = node .weights ['bias' ].data
7093 model .replace_node (node , pw_node )
71-
94+
7295 return True
0 commit comments