sparsepixels 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sparsepixels-0.1.0/LICENSE +21 -0
- sparsepixels-0.1.0/PKG-INFO +56 -0
- sparsepixels-0.1.0/README.md +38 -0
- sparsepixels-0.1.0/pyproject.toml +3 -0
- sparsepixels-0.1.0/setup.cfg +31 -0
- sparsepixels-0.1.0/setup.py +4 -0
- sparsepixels-0.1.0/sparsepixels/__init__.py +0 -0
- sparsepixels-0.1.0/sparsepixels/img/logo.png +0 -0
- sparsepixels-0.1.0/sparsepixels/layers.py +153 -0
- sparsepixels-0.1.0/sparsepixels.egg-info/PKG-INFO +56 -0
- sparsepixels-0.1.0/sparsepixels.egg-info/SOURCES.txt +13 -0
- sparsepixels-0.1.0/sparsepixels.egg-info/dependency_links.txt +1 -0
- sparsepixels-0.1.0/sparsepixels.egg-info/requires.txt +3 -0
- sparsepixels-0.1.0/sparsepixels.egg-info/top_level.txt +8 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ho Fung Tsoi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sparsepixels
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Efficient convolution for sparse data on FPGAs
|
|
5
|
+
Home-page: https://github.com/hftsoi/sparse-pixels
|
|
6
|
+
Author: Ho Fung Tsoi
|
|
7
|
+
Author-email: ho.fung.tsoi@cern.ch
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: tensorflow<2.15.0,>=2.14.0
|
|
15
|
+
Requires-Dist: keras<2.15.0,>=2.14.0
|
|
16
|
+
Requires-Dist: qkeras<0.10.0,>=0.9.0
|
|
17
|
+
Dynamic: license-file
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<img src="https://raw.githubusercontent.com/hftsoi/sparse-pixels/main/docs/figs/logo.png" width="300" />
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
<p align="center">
|
|
24
|
+
<img src="https://raw.githubusercontent.com/hftsoi/sparse-pixels/main/docs/figs/sparsepixels.png" width="900"/>
|
|
25
|
+
</p>
|
|
26
|
+
|
|
27
|
+
<p align="center">
|
|
28
|
+
<img src="https://raw.githubusercontent.com/hftsoi/sparse-pixels/main/docs/figs/cnn_standard.gif" width="400" />
|
|
29
|
+
<img src="https://raw.githubusercontent.com/hftsoi/sparse-pixels/main/docs/figs/cnn_sparse.gif" width="400" />
|
|
30
|
+
</p>
|
|
31
|
+
|
|
32
|
+
# SparsePixels: Efficient convolution for sparse data on FPGAs
|
|
33
|
+
|
|
34
|
+
[](https://arxiv.org/abs/2512.06208)
|
|
35
|
+
|
|
36
|
+
> **Note:** code packaging in preparation, stay tuned :)
|
|
37
|
+
|
|
38
|
+
## Installation
|
|
39
|
+
|
|
40
|
+
## Getting Started
|
|
41
|
+
|
|
42
|
+
## Documentation
|
|
43
|
+
|
|
44
|
+
## Citation
|
|
45
|
+
If you find this useful in your research, please consider citing:
|
|
46
|
+
```
|
|
47
|
+
@article{Tsoi:2025nvg,
|
|
48
|
+
author = "Tsoi, Ho Fung and Rankin, Dylan and Loncar, Vladimir and Harris, Philip",
|
|
49
|
+
title = "{SparsePixels: Efficient Convolution for Sparse Data on FPGAs}",
|
|
50
|
+
eprint = "2512.06208",
|
|
51
|
+
archivePrefix = "arXiv",
|
|
52
|
+
primaryClass = "cs.AR",
|
|
53
|
+
month = "12",
|
|
54
|
+
year = "2025"
|
|
55
|
+
}
|
|
56
|
+
```
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/hftsoi/sparse-pixels/main/docs/figs/logo.png" width="300" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<img src="https://raw.githubusercontent.com/hftsoi/sparse-pixels/main/docs/figs/sparsepixels.png" width="900"/>
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<img src="https://raw.githubusercontent.com/hftsoi/sparse-pixels/main/docs/figs/cnn_standard.gif" width="400" />
|
|
11
|
+
<img src="https://raw.githubusercontent.com/hftsoi/sparse-pixels/main/docs/figs/cnn_sparse.gif" width="400" />
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
# SparsePixels: Efficient convolution for sparse data on FPGAs
|
|
15
|
+
|
|
16
|
+
[](https://arxiv.org/abs/2512.06208)
|
|
17
|
+
|
|
18
|
+
> **Note:** code packaging in preparation, stay tuned :)
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
## Getting Started
|
|
23
|
+
|
|
24
|
+
## Documentation
|
|
25
|
+
|
|
26
|
+
## Citation
|
|
27
|
+
If you find this useful in your research, please consider citing:
|
|
28
|
+
```
|
|
29
|
+
@article{Tsoi:2025nvg,
|
|
30
|
+
author = "Tsoi, Ho Fung and Rankin, Dylan and Loncar, Vladimir and Harris, Philip",
|
|
31
|
+
title = "{SparsePixels: Efficient Convolution for Sparse Data on FPGAs}",
|
|
32
|
+
eprint = "2512.06208",
|
|
33
|
+
archivePrefix = "arXiv",
|
|
34
|
+
primaryClass = "cs.AR",
|
|
35
|
+
month = "12",
|
|
36
|
+
year = "2025"
|
|
37
|
+
}
|
|
38
|
+
```
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[metadata]
|
|
2
|
+
name = sparsepixels
|
|
3
|
+
version = 0.1.0
|
|
4
|
+
description = Efficient convolution for sparse data on FPGAs
|
|
5
|
+
author = Ho Fung Tsoi
|
|
6
|
+
author_email = ho.fung.tsoi@cern.ch
|
|
7
|
+
license = MIT
|
|
8
|
+
license_files = LICENSE
|
|
9
|
+
long_description = file: README.md
|
|
10
|
+
long_description_content_type = text/markdown
|
|
11
|
+
url = https://github.com/hftsoi/sparse-pixels
|
|
12
|
+
classifiers =
|
|
13
|
+
Programming Language :: Python :: 3
|
|
14
|
+
License :: OSI Approved :: MIT License
|
|
15
|
+
|
|
16
|
+
[options]
|
|
17
|
+
packages = find_namespace:
|
|
18
|
+
python_requires = >=3.10
|
|
19
|
+
install_requires =
|
|
20
|
+
tensorflow>=2.14.0,<2.15.0
|
|
21
|
+
keras>=2.14.0,<2.15.0
|
|
22
|
+
qkeras>=0.9.0,<0.10.0
|
|
23
|
+
include_package_data = True
|
|
24
|
+
|
|
25
|
+
[options.package_data]
|
|
26
|
+
sparsepixels = img/logo.png
|
|
27
|
+
|
|
28
|
+
[egg_info]
|
|
29
|
+
tag_build =
|
|
30
|
+
tag_date = 0
|
|
31
|
+
|
|
File without changes
|
|
Binary file
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import tensorflow as tf
|
|
2
|
+
from qkeras import QConv2D, quantizers
|
|
3
|
+
from tensorflow.keras.layers import AveragePooling2D, MaxPooling2D
|
|
4
|
+
|
|
5
|
+
class InputReduce(tf.keras.layers.Layer):
|
|
6
|
+
def __init__(self, n_max_pixels, threshold, **kwargs):
|
|
7
|
+
super(InputReduce, self).__init__(**kwargs)
|
|
8
|
+
self.n_max_pixels = n_max_pixels
|
|
9
|
+
self.threshold = threshold
|
|
10
|
+
|
|
11
|
+
def call(self, inputs):
|
|
12
|
+
batch_size = tf.shape(inputs)[0]
|
|
13
|
+
h = tf.shape(inputs)[1]
|
|
14
|
+
w = tf.shape(inputs)[2]
|
|
15
|
+
|
|
16
|
+
'''
|
|
17
|
+
if self.threshold is not None:
|
|
18
|
+
cond = inputs > self.threshold
|
|
19
|
+
else:
|
|
20
|
+
cond = inputs != 0
|
|
21
|
+
active_flag = tf.cast(tf.reduce_any(cond, axis=-1), tf.int32)
|
|
22
|
+
'''
|
|
23
|
+
|
|
24
|
+
# to be consistent with hls, check only the first input channel
|
|
25
|
+
if self.threshold is not None:
|
|
26
|
+
active_flag = tf.cast(inputs[..., 0] > self.threshold, tf.int32)
|
|
27
|
+
else:
|
|
28
|
+
active_flag = tf.cast(inputs[..., 0] != 0, tf.int32)
|
|
29
|
+
|
|
30
|
+
active_flag_flat = tf.reshape(active_flag, [batch_size, h * w])
|
|
31
|
+
active_count = tf.cumsum(active_flag_flat, axis=1)
|
|
32
|
+
|
|
33
|
+
keep_mask_flat = tf.cast(tf.logical_and(active_flag_flat == 1, active_count <= self.n_max_pixels), inputs.dtype)
|
|
34
|
+
keep_mask = tf.reshape(keep_mask_flat, [batch_size, h, w, 1])
|
|
35
|
+
|
|
36
|
+
inputs_reduced = inputs * keep_mask
|
|
37
|
+
return inputs_reduced, keep_mask
|
|
38
|
+
|
|
39
|
+
def get_config(self):
|
|
40
|
+
config = super(InputReduce, self).get_config()
|
|
41
|
+
config.update({
|
|
42
|
+
"n_max_pixels": self.n_max_pixels,
|
|
43
|
+
"threshold": self.threshold
|
|
44
|
+
})
|
|
45
|
+
return config
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class RemoveDilatedPixels(tf.keras.layers.Layer):
|
|
49
|
+
def __init__(self, **kwargs):
|
|
50
|
+
super(RemoveDilatedPixels, self).__init__(**kwargs)
|
|
51
|
+
|
|
52
|
+
def call(self, inputs):
|
|
53
|
+
x, mask = inputs
|
|
54
|
+
mask = tf.cast(mask, x.dtype)
|
|
55
|
+
removed = x * mask
|
|
56
|
+
return removed
|
|
57
|
+
|
|
58
|
+
def get_config(self):
|
|
59
|
+
config = super(RemoveDilatedPixels, self).get_config()
|
|
60
|
+
return config
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class QConv2DSparse(tf.keras.layers.Layer):
|
|
64
|
+
def __init__(self, *conv_args, **conv_kwargs):
|
|
65
|
+
super().__init__(name=conv_kwargs.get("name", None))
|
|
66
|
+
self._bias_quant_cfg = conv_kwargs.pop("bias_quantizer", None)
|
|
67
|
+
self._bias_quantizer = (quantizers.get_quantizer(self._bias_quant_cfg) if self._bias_quant_cfg is not None else None)
|
|
68
|
+
|
|
69
|
+
conv_kwargs["use_bias"] = False
|
|
70
|
+
self.conv = QConv2D(*conv_args, **conv_kwargs)
|
|
71
|
+
self.bias = self.add_weight(
|
|
72
|
+
name = "bias",
|
|
73
|
+
shape = (self.conv.filters,),
|
|
74
|
+
initializer = "zeros",
|
|
75
|
+
trainable = True,
|
|
76
|
+
dtype = self.conv.dtype,
|
|
77
|
+
)
|
|
78
|
+
self.masker = RemoveDilatedPixels()
|
|
79
|
+
|
|
80
|
+
def call(self, inputs, **kwargs):
|
|
81
|
+
x, keep_mask = inputs
|
|
82
|
+
x = self.masker((x, keep_mask))
|
|
83
|
+
y = self.conv(x, **kwargs)
|
|
84
|
+
|
|
85
|
+
b = self.bias
|
|
86
|
+
if self._bias_quantizer is not None:
|
|
87
|
+
b = self._bias_quantizer(b)
|
|
88
|
+
b = tf.reshape(b, shape=(1, 1, 1, -1))
|
|
89
|
+
|
|
90
|
+
non_zero = tf.cast(tf.not_equal(y, 0), y.dtype)
|
|
91
|
+
y = y + b * non_zero
|
|
92
|
+
|
|
93
|
+
y = self.masker((y, keep_mask))
|
|
94
|
+
return y
|
|
95
|
+
|
|
96
|
+
def get_config(self):
|
|
97
|
+
cfg = super().get_config()
|
|
98
|
+
cfg["conv_config"] = self.conv.get_config()
|
|
99
|
+
cfg["bias_quantizer"] = self._bias_quant_cfg
|
|
100
|
+
return cfg
|
|
101
|
+
|
|
102
|
+
@classmethod
|
|
103
|
+
def from_config(cls, config):
|
|
104
|
+
conv_cfg = config.pop("conv_config")
|
|
105
|
+
bias_quant_cfg = config.pop("bias_quantizer", None)
|
|
106
|
+
layer = cls(**conv_cfg, bias_quantizer=bias_quant_cfg)
|
|
107
|
+
return layer
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class AveragePooling2DSparse(tf.keras.layers.Layer):
|
|
111
|
+
def __init__(self, *pool_args, **pool_kwargs):
|
|
112
|
+
super().__init__(name=pool_kwargs.get("name", None))
|
|
113
|
+
self.avg_pool = AveragePooling2D(*pool_args, **pool_kwargs)
|
|
114
|
+
self.max_pool = MaxPooling2D(*pool_args, **pool_kwargs)
|
|
115
|
+
|
|
116
|
+
def call(self, inputs, **kwargs):
|
|
117
|
+
x, keep_mask = inputs
|
|
118
|
+
y = self.avg_pool(x, **kwargs)
|
|
119
|
+
keep_mask_pooled = self.max_pool(keep_mask)
|
|
120
|
+
return y, keep_mask_pooled
|
|
121
|
+
|
|
122
|
+
def get_config(self):
|
|
123
|
+
cfg = super().get_config()
|
|
124
|
+
cfg["pool_config"] = self.avg_pool.get_config()
|
|
125
|
+
return cfg
|
|
126
|
+
|
|
127
|
+
@classmethod
|
|
128
|
+
def from_config(cls, config):
|
|
129
|
+
pool_cfg = config.pop("pool_config")
|
|
130
|
+
return cls(**pool_cfg)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class MaxPooling2DSparse(tf.keras.layers.Layer):
|
|
134
|
+
def __init__(self, *pool_args, **pool_kwargs):
|
|
135
|
+
super().__init__(name=pool_kwargs.get("name", None))
|
|
136
|
+
self.max_pool = MaxPooling2D(*pool_args, **pool_kwargs)
|
|
137
|
+
|
|
138
|
+
def call(self, inputs, **kwargs):
|
|
139
|
+
x, keep_mask = inputs
|
|
140
|
+
y = self.max_pool(x, **kwargs)
|
|
141
|
+
keep_mask_pooled = self.max_pool(keep_mask)
|
|
142
|
+
return y, keep_mask_pooled
|
|
143
|
+
|
|
144
|
+
def get_config(self):
|
|
145
|
+
cfg = super().get_config()
|
|
146
|
+
cfg["pool_config"] = self.max_pool.get_config()
|
|
147
|
+
return cfg
|
|
148
|
+
|
|
149
|
+
@classmethod
|
|
150
|
+
def from_config(cls, config):
|
|
151
|
+
pool_cfg = config.pop("pool_config")
|
|
152
|
+
return cls(**pool_cfg)
|
|
153
|
+
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sparsepixels
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Efficient convolution for sparse data on FPGAs
|
|
5
|
+
Home-page: https://github.com/hftsoi/sparse-pixels
|
|
6
|
+
Author: Ho Fung Tsoi
|
|
7
|
+
Author-email: ho.fung.tsoi@cern.ch
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: tensorflow<2.15.0,>=2.14.0
|
|
15
|
+
Requires-Dist: keras<2.15.0,>=2.14.0
|
|
16
|
+
Requires-Dist: qkeras<0.10.0,>=0.9.0
|
|
17
|
+
Dynamic: license-file
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<img src="https://raw.githubusercontent.com/hftsoi/sparse-pixels/main/docs/figs/logo.png" width="300" />
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
<p align="center">
|
|
24
|
+
<img src="https://raw.githubusercontent.com/hftsoi/sparse-pixels/main/docs/figs/sparsepixels.png" width="900"/>
|
|
25
|
+
</p>
|
|
26
|
+
|
|
27
|
+
<p align="center">
|
|
28
|
+
<img src="https://raw.githubusercontent.com/hftsoi/sparse-pixels/main/docs/figs/cnn_standard.gif" width="400" />
|
|
29
|
+
<img src="https://raw.githubusercontent.com/hftsoi/sparse-pixels/main/docs/figs/cnn_sparse.gif" width="400" />
|
|
30
|
+
</p>
|
|
31
|
+
|
|
32
|
+
# SparsePixels: Efficient convolution for sparse data on FPGAs
|
|
33
|
+
|
|
34
|
+
[](https://arxiv.org/abs/2512.06208)
|
|
35
|
+
|
|
36
|
+
> **Note:** code packaging in preparation, stay tuned :)
|
|
37
|
+
|
|
38
|
+
## Installation
|
|
39
|
+
|
|
40
|
+
## Getting Started
|
|
41
|
+
|
|
42
|
+
## Documentation
|
|
43
|
+
|
|
44
|
+
## Citation
|
|
45
|
+
If you find this useful in your research, please consider citing:
|
|
46
|
+
```
|
|
47
|
+
@article{Tsoi:2025nvg,
|
|
48
|
+
author = "Tsoi, Ho Fung and Rankin, Dylan and Loncar, Vladimir and Harris, Philip",
|
|
49
|
+
title = "{SparsePixels: Efficient Convolution for Sparse Data on FPGAs}",
|
|
50
|
+
eprint = "2512.06208",
|
|
51
|
+
archivePrefix = "arXiv",
|
|
52
|
+
primaryClass = "cs.AR",
|
|
53
|
+
month = "12",
|
|
54
|
+
year = "2025"
|
|
55
|
+
}
|
|
56
|
+
```
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
setup.cfg
|
|
5
|
+
setup.py
|
|
6
|
+
sparsepixels/__init__.py
|
|
7
|
+
sparsepixels/layers.py
|
|
8
|
+
sparsepixels.egg-info/PKG-INFO
|
|
9
|
+
sparsepixels.egg-info/SOURCES.txt
|
|
10
|
+
sparsepixels.egg-info/dependency_links.txt
|
|
11
|
+
sparsepixels.egg-info/requires.txt
|
|
12
|
+
sparsepixels.egg-info/top_level.txt
|
|
13
|
+
sparsepixels/img/logo.png
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|