tico 0.1.0.dev250824__py3-none-any.whl → 0.1.0.dev250825__py3-none-any.whl

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.
tico/__init__.py CHANGED
@@ -29,7 +29,7 @@ __all__ = [
29
29
  ]
30
30
 
31
31
  # THIS LINE IS AUTOMATICALLY GENERATED BY setup.py
32
- __version__ = "0.1.0.dev250824"
32
+ __version__ = "0.1.0.dev250825"
33
33
 
34
34
  MINIMUM_SUPPORTED_VERSION = "2.5.0"
35
35
  SECURE_TORCH_VERSION = "2.6.0"
@@ -1,5 +1,11 @@
1
+ from tico.experimental.quantization.ptq.wrappers.nn.quant_layernorm import (
2
+ QuantLayerNorm,
3
+ )
1
4
  from tico.experimental.quantization.ptq.wrappers.nn.quant_linear import QuantLinear
5
+ from tico.experimental.quantization.ptq.wrappers.nn.quant_silu import QuantSiLU
2
6
 
3
7
  __all__ = [
8
+ "QuantLayerNorm",
4
9
  "QuantLinear",
10
+ "QuantSiLU",
5
11
  ]
@@ -0,0 +1,184 @@
1
+ # Copyright (c) 2025 Samsung Electronics Co., Ltd. All Rights Reserved
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from typing import Iterable, Optional, Tuple
16
+
17
+ import torch
18
+ import torch.nn as nn
19
+
20
+ from tico.experimental.quantization.ptq.mode import Mode
21
+ from tico.experimental.quantization.ptq.quant_config import QuantConfig
22
+ from tico.experimental.quantization.ptq.wrappers.quant_module_base import (
23
+ QuantModuleBase,
24
+ )
25
+ from tico.experimental.quantization.ptq.wrappers.registry import register
26
+
27
+
28
+ @register(nn.LayerNorm)
29
+ class QuantLayerNorm(QuantModuleBase):
30
+ """
31
+ QuantLayerNorm — drop-in replacement for nn.LayerNorm that quantizes
32
+ the elementary steps:
33
+ 1) μ = mean(x, dims) (mean)
34
+ 2) c = x - μ (sub)
35
+ 3) s = c * c (square)
36
+ 4) v = mean(s, dims) (variance)
37
+ 5) e = v + eps (add-eps)
38
+ 6) r = rsqrt(e) (rsqrt)
39
+ 7) n = c * r (normalize)
40
+ 8) y = (n * γ) + β (affine), with:
41
+ • affine_mul : n * γ
42
+ • affine_add : (n * γ) + β
43
+ """
44
+
45
+ def __init__(
46
+ self,
47
+ fp: nn.LayerNorm,
48
+ *,
49
+ qcfg: Optional[QuantConfig] = None,
50
+ fp_name: Optional[str] = None
51
+ ):
52
+ super().__init__(qcfg, fp_name=fp_name)
53
+ self.module = fp
54
+ self.eps = torch.tensor(self.module.eps)
55
+ # Number of trailing dims participating in normalization
56
+ # (PyTorch stores normalized_shape as a tuple even if an int was passed)
57
+ self._norm_ndim: int = len(fp.normalized_shape) # safe for int→tuple
58
+
59
+ # Activation / intermediate observers
60
+ self.act_in_obs = self._make_obs("act_in")
61
+ self.mean_obs = self._make_obs("mean")
62
+ self.centered_obs = self._make_obs("centered")
63
+ self.square_obs = self._make_obs("square")
64
+ self.var_obs = self._make_obs("var")
65
+ self.eps_obs = self._make_obs("eps")
66
+ self.add_eps_obs = self._make_obs("add_eps")
67
+ self.inv_std_obs = self._make_obs("inv_std")
68
+ self.norm_obs = self._make_obs("norm")
69
+ self.act_out_obs = self._make_obs("act_out")
70
+
71
+ # Optional affine parameter observers (γ, β)
72
+ self.weight_obs = None
73
+ self.bias_obs = None
74
+ self.affine_mul_obs = None
75
+ self.affine_add_obs = None
76
+ if self.module.elementwise_affine:
77
+ if self.module.weight is not None:
78
+ self.weight_obs = self._make_obs("weight")
79
+ if self.module.bias is not None:
80
+ self.bias_obs = self._make_obs("bias")
81
+ # Per-op observers for (n * w) and (+ b)
82
+ self.affine_mul_obs = self._make_obs("affine_mul")
83
+ self.affine_add_obs = self._make_obs("affine_add")
84
+
85
+ def enable_calibration(self) -> None:
86
+ """
87
+ Switch to CALIB mode and collect *fixed* ranges for affine params
88
+ immediately, since they do not change across inputs.
89
+ """
90
+ super().enable_calibration()
91
+ if self.module.elementwise_affine:
92
+ if self.weight_obs is not None and self.module.weight is not None:
93
+ self.weight_obs.collect(self.module.weight)
94
+ if self.bias_obs is not None and self.module.bias is not None:
95
+ self.bias_obs.collect(self.module.bias)
96
+
97
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
98
+ # Determine reduction dims (last self._norm_ndim axes)
99
+ # Example: if x.ndim=4 and norm_ndim=2 → dims=(2,3)
100
+ dims = tuple(range(x.dim() - self._norm_ndim, x.dim()))
101
+
102
+ # 0) input
103
+ x_q = self._fq(x, self.act_in_obs)
104
+
105
+ # 1) mean
106
+ mu = x_q.mean(dim=dims, keepdim=True)
107
+ mu_q = self._fq(mu, self.mean_obs)
108
+
109
+ # 2) center
110
+ c = x_q - mu_q
111
+ c_q = self._fq(c, self.centered_obs)
112
+
113
+ # 3) square (elementwise mul)
114
+ s = c_q * c_q
115
+ s_q = self._fq(s, self.square_obs)
116
+
117
+ # 4) variance (via squared mean)
118
+ v = s_q.mean(dim=dims, keepdim=True)
119
+ v_q = self._fq(v, self.var_obs)
120
+
121
+ # 5) add eps
122
+ eps_q = self._fq(self.eps, self.eps_obs)
123
+ e = v_q + eps_q
124
+ e_q = self._fq(e, self.add_eps_obs)
125
+
126
+ # 6) inverse std
127
+ r = torch.rsqrt(e_q)
128
+ r_q = self._fq(r, self.inv_std_obs)
129
+
130
+ # 7) normalize
131
+ n = c_q * r_q
132
+ n_q = self._fq(n, self.norm_obs)
133
+
134
+ # 8) optional affine
135
+ if self.module.elementwise_affine:
136
+ w = self.module.weight
137
+ b = self.module.bias
138
+ if self._mode is Mode.QUANT:
139
+ if self.weight_obs is not None and w is not None:
140
+ w = self.weight_obs.fake_quant(w) # type: ignore[assignment]
141
+ if self.bias_obs is not None and b is not None:
142
+ b = self.bias_obs.fake_quant(b) # type: ignore[assignment]
143
+ y = n_q
144
+ # 8a) n * w (fake-quant the result of the mul)
145
+ if w is not None:
146
+ y = y * w
147
+ if self.affine_mul_obs is not None:
148
+ y = self._fq(y, self.affine_mul_obs)
149
+
150
+ # 8b) (+ b) (fake-quant the result of the add)
151
+ if b is not None:
152
+ y = y + b
153
+ if self.affine_add_obs is not None:
154
+ y = self._fq(y, self.affine_add_obs)
155
+ else:
156
+ y = n_q
157
+
158
+ # 9) output activation
159
+ return self._fq(y, self.act_out_obs)
160
+
161
+ def _all_observers(self) -> Iterable:
162
+ obs: Tuple = (
163
+ self.act_in_obs,
164
+ self.mean_obs,
165
+ self.centered_obs,
166
+ self.square_obs,
167
+ self.var_obs,
168
+ self.eps_obs,
169
+ self.add_eps_obs,
170
+ self.inv_std_obs,
171
+ self.norm_obs,
172
+ self.act_out_obs,
173
+ )
174
+ # Insert affine param observers if present
175
+ if self.module.elementwise_affine:
176
+ if self.weight_obs is not None:
177
+ obs = (self.weight_obs,) + obs
178
+ if self.bias_obs is not None:
179
+ obs = obs + (self.bias_obs,)
180
+ if self.affine_mul_obs is not None:
181
+ obs = obs + (self.affine_mul_obs,)
182
+ if self.affine_add_obs is not None:
183
+ obs = obs + (self.affine_add_obs,)
184
+ return obs
@@ -0,0 +1,61 @@
1
+ # Copyright (c) 2025 Samsung Electronics Co., Ltd. All Rights Reserved
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from typing import Optional
16
+
17
+ import torch
18
+ import torch.nn as nn
19
+
20
+ from tico.experimental.quantization.ptq.quant_config import QuantConfig
21
+ from tico.experimental.quantization.ptq.wrappers.quant_module_base import (
22
+ QuantModuleBase,
23
+ )
24
+ from tico.experimental.quantization.ptq.wrappers.registry import register
25
+
26
+
27
+ @register(nn.SiLU)
28
+ class QuantSiLU(QuantModuleBase):
29
+ """
30
+ QuantSiLU — drop-in replacement for nn.SiLU that quantizes
31
+ both intermediate tensors:
32
+ • s = sigmoid(x) (logistic)
33
+ • y = x * s (mul)
34
+ """
35
+
36
+ def __init__(
37
+ self,
38
+ fp: nn.SiLU,
39
+ *,
40
+ qcfg: Optional[QuantConfig] = None,
41
+ fp_name: Optional[str] = None
42
+ ):
43
+ super().__init__(qcfg, fp_name=fp_name)
44
+ self.act_in_obs = self._make_obs("act_in")
45
+ self.sig_obs = self._make_obs("sigmoid")
46
+ self.mul_obs = self._make_obs("mul")
47
+ self.module = fp
48
+
49
+ def forward(self, x: torch.Tensor):
50
+ x_q = self._fq(x, self.act_in_obs)
51
+
52
+ s = torch.sigmoid(x_q)
53
+ s = self._fq(s, self.sig_obs)
54
+
55
+ y = x * s
56
+ y = self._fq(y, self.mul_obs)
57
+
58
+ return y
59
+
60
+ def _all_observers(self):
61
+ return (self.act_in_obs, self.sig_obs, self.mul_obs)
@@ -46,10 +46,24 @@ class QuantModuleBase(nn.Module, ABC):
46
46
  self.fp_name = fp_name
47
47
 
48
48
  def _child_quant_modules(self):
49
- """Yield direct children that are QuantModuleBase."""
50
- for m in self.children():
49
+ """
50
+ Yield immediate QuantModuleBase *descendants*, skipping over pure containers
51
+ (e.g., ModuleList/Sequential/ModuleDict). Once a QuantModuleBase is found,
52
+ do NOT descend into it here—let recursion happen level by level.
53
+ """
54
+ seen = set()
55
+ stack = list(self.children()) # start from direct children
56
+
57
+ while stack:
58
+ m = stack.pop()
51
59
  if isinstance(m, QuantModuleBase):
52
- yield m
60
+ if id(m) not in seen:
61
+ seen.add(id(m))
62
+ yield m
63
+ # IMPORTANT: do not recurse into `m` here; its own call will handle its subtree
64
+ elif isinstance(m, (nn.ModuleList, nn.ModuleDict, nn.Sequential)):
65
+ # `m` is a container or a non-quant leaf: keep descending until we hit quant modules
66
+ stack.extend(list(m.children()))
53
67
 
54
68
  def enable_calibration(self) -> None:
55
69
  self._mode = Mode.CALIB
@@ -24,7 +24,10 @@ from tico.experimental.quantization.ptq.wrappers.quant_module_base import (
24
24
  _WRAPPERS: Dict[Type[nn.Module], Type[QuantModuleBase]] = {}
25
25
  _IMPORT_ONCE = False
26
26
  _CORE_MODULES = (
27
+ "tico.experimental.quantization.ptq.wrappers.quant_elementwise",
28
+ "tico.experimental.quantization.ptq.wrappers.nn.quant_layernorm",
27
29
  "tico.experimental.quantization.ptq.wrappers.nn.quant_linear",
30
+ "tico.experimental.quantization.ptq.wrappers.nn.quant_silu",
28
31
  # add future core wrappers here
29
32
  )
30
33
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tico
3
- Version: 0.1.0.dev250824
3
+ Version: 0.1.0.dev250825
4
4
  Summary: Convert exported Torch module to circle
5
5
  Home-page: UNKNOWN
6
6
  License: UNKNOWN
@@ -1,4 +1,4 @@
1
- tico/__init__.py,sha256=KpoO-uGcDRC4A9St5fZzxTPNGa4_zegc4omDan4X0qg,1883
1
+ tico/__init__.py,sha256=qQ6AhQGEdqTIol9a3pZd8krL41W7mwvr0HoA1tPCeqU,1883
2
2
  tico/pt2_to_circle.py,sha256=gu3MD4Iqc0zMZcCZ2IT8oGbyj21CTSbT3Rgd9s2B_9A,2767
3
3
  tico/config/__init__.py,sha256=xZzCXjZ84qE-CsBi-dfaL05bqpQ3stKKfTXhnrJRyVs,142
4
4
  tico/config/base.py,sha256=q5xMqGxTUZs4mFqt5c7i_y9U00fYgdMGl9nUqIVMlCo,1248
@@ -73,10 +73,12 @@ tico/experimental/quantization/ptq/utils/reduce_utils.py,sha256=3kWawLB91EcvvHlC
73
73
  tico/experimental/quantization/ptq/wrappers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
74
74
  tico/experimental/quantization/ptq/wrappers/ptq_wrapper.py,sha256=KRw_VvFJYvd2OBj4K1sYEXxUwZk9QghMw3NsgjKIAGk,1857
75
75
  tico/experimental/quantization/ptq/wrappers/quant_elementwise.py,sha256=LhEoobfvto6zKrBOKL4gmxfFFc31jHzyQV_zfps-iQM,3604
76
- tico/experimental/quantization/ptq/wrappers/quant_module_base.py,sha256=6RK4bn9G1pzFmkIdBdFf7liBOpb-b7rpthgD83AgkbQ,5256
77
- tico/experimental/quantization/ptq/wrappers/registry.py,sha256=exXl2wNNzVgC2P9gMjpF_-PqIBgYERGruzh0u1Pril0,4367
78
- tico/experimental/quantization/ptq/wrappers/nn/__init__.py,sha256=q4A9BiGlsa8ZdGV3y0SDiSkzkdVugsK2iz2daiJqBCY,118
76
+ tico/experimental/quantization/ptq/wrappers/quant_module_base.py,sha256=vkcDos_knGSS29rIZuEIWkAJLHrENbGz8nCH2-iara8,5969
77
+ tico/experimental/quantization/ptq/wrappers/registry.py,sha256=562nKSlp9qF-w4-aQeJbx2V_wMGE2FRrjIKUfRwC4Mg,4571
78
+ tico/experimental/quantization/ptq/wrappers/nn/__init__.py,sha256=I9uTt5HfcRoMEDYHpAeATMv2TbCQiX0ZbfUFMzSJ4Qw,336
79
+ tico/experimental/quantization/ptq/wrappers/nn/quant_layernorm.py,sha256=G5Sgt-tXnzh0Rxyk-2honmZIfEQOZlRfOsoDBdSGmA4,6887
79
80
  tico/experimental/quantization/ptq/wrappers/nn/quant_linear.py,sha256=xW-VEPB7RJoslS3xLVCdhIuMjppknvpkZleRGK4JFVQ,2240
81
+ tico/experimental/quantization/ptq/wrappers/nn/quant_silu.py,sha256=XnJDggkWUTfXC1-BLeAbcCUtp687XLIkIIbuQlqycDw,1864
80
82
  tico/interpreter/__init__.py,sha256=IO6FP_xYbGy0dW0HL26GXD3ouxARaxCK7bz9dn4blPQ,26
81
83
  tico/interpreter/infer.py,sha256=1ZFe3DVMR2mlwBosoedqoL0-CGN_01CKLgMgxuw62KA,4861
82
84
  tico/interpreter/interpreter.py,sha256=tGbluCbrehTCqBu8mtGDNzby_ieJ2ry8_RH_eC0CQxk,3828
@@ -231,9 +233,9 @@ tico/utils/mx/__init__.py,sha256=IO6FP_xYbGy0dW0HL26GXD3ouxARaxCK7bz9dn4blPQ,26
231
233
  tico/utils/mx/elemwise_ops.py,sha256=V6glyAHsVR1joqpsgnNytatCD_ew92xNWZ19UFDoMTA,10281
232
234
  tico/utils/mx/formats.py,sha256=uzNWyu-1onUlwQfX5cZ6fZSUfHMRqorper7_T1k3jfk,3404
233
235
  tico/utils/mx/mx_ops.py,sha256=RcfUTYVi-wilGB2sC35OeARdwDqnixv7dG5iyZ-fQT8,8555
234
- tico-0.1.0.dev250824.dist-info/LICENSE,sha256=kp4JLII7bzRhPb0CPD5XTDZMh22BQ7h3k3B7t8TiSbw,12644
235
- tico-0.1.0.dev250824.dist-info/METADATA,sha256=5PX-E9sIG566-2M_MBf8gnBXrQTSpyXzsPTeL-BLPEQ,8450
236
- tico-0.1.0.dev250824.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
237
- tico-0.1.0.dev250824.dist-info/entry_points.txt,sha256=kBKYSS_IYrSXmUYevmmepqIVPScq5vF8ulQRu3I_Zf0,59
238
- tico-0.1.0.dev250824.dist-info/top_level.txt,sha256=oqs7UPoNSKZEwqsX8B-KAWdQwfAa7i60pbxW_Jk7P3w,5
239
- tico-0.1.0.dev250824.dist-info/RECORD,,
236
+ tico-0.1.0.dev250825.dist-info/LICENSE,sha256=kp4JLII7bzRhPb0CPD5XTDZMh22BQ7h3k3B7t8TiSbw,12644
237
+ tico-0.1.0.dev250825.dist-info/METADATA,sha256=7wBkNIwJG_prscPdY7Rn_Muit4OuPN29Q8C_isHlEdI,8450
238
+ tico-0.1.0.dev250825.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
239
+ tico-0.1.0.dev250825.dist-info/entry_points.txt,sha256=kBKYSS_IYrSXmUYevmmepqIVPScq5vF8ulQRu3I_Zf0,59
240
+ tico-0.1.0.dev250825.dist-info/top_level.txt,sha256=oqs7UPoNSKZEwqsX8B-KAWdQwfAa7i60pbxW_Jk7P3w,5
241
+ tico-0.1.0.dev250825.dist-info/RECORD,,