@@ -98,3 +98,126 @@ def test_generate_8bit_table_values_matches_reference_for_qmin_minus_127() -> No
9898 assert torch .equal (lut_values , expected_lut_values )
9999 assert int (lut_values [0 ]) == int (lut_values [1 ])
100100 assert int (lut_values [zero_input_index ]) == expected_zero_output
101+
102+
103+ def test_generate_16bit_identity_table_is_antisymmetric () -> None :
104+ """Equal zero-point-zero qparams produce an antisymmetric full-range LUT."""
105+ qargs = QuantArgs (
106+ scale = 1.0 ,
107+ zp = 0 ,
108+ qmin = - 32767 ,
109+ qmax = 32767 ,
110+ dtype = torch .int16 ,
111+ )
112+
113+ lut_values , lshift = InsertTableOpsPass .generate_16_bit_table_values (
114+ torch .nn .Identity (),
115+ qargs ,
116+ qargs ,
117+ )
118+
119+ assert lshift == - 7
120+ for i in range (len (lut_values ) // 2 ):
121+ neg_value = int (lut_values [i ])
122+ pos_value = int (lut_values [~ i ])
123+ assert neg_value == - pos_value , (
124+ "Expected a zero-point-zero identity LUT to be antisymmetric, but "
125+ f"lut_values[{ i } ]={ neg_value } and "
126+ f"lut_values[{ ~ i } ]={ pos_value } "
127+ )
128+
129+
130+ def test_generate_16bit_full_range_identity_table_applies_zero_point_delta () -> None :
131+ """Changing only zero point offsets each LUT value by output_zp - input_zp."""
132+ input_qargs = QuantArgs (
133+ scale = 1.0 ,
134+ zp = 1 ,
135+ qmin = - 32767 ,
136+ qmax = 32767 ,
137+ dtype = torch .int16 ,
138+ )
139+ output_qargs = QuantArgs (
140+ scale = 1.0 ,
141+ zp = - 1 ,
142+ qmin = - 32767 ,
143+ qmax = 32767 ,
144+ dtype = torch .int16 ,
145+ )
146+
147+ lut_values , lshift = InsertTableOpsPass .generate_16_bit_table_values (
148+ torch .nn .Identity (),
149+ input_qargs ,
150+ output_qargs ,
151+ )
152+
153+ table_input_codes = torch .arange (- 32768 , 32769 , 128 , dtype = torch .int32 )
154+ zero_point_delta = (
155+ output_qargs .get_zp_per_tensor () - input_qargs .get_zp_per_tensor ()
156+ )
157+ expected_lut_values = (table_input_codes + zero_point_delta ).clamp (
158+ output_qargs .qmin ,
159+ output_qargs .qmax ,
160+ )
161+
162+ assert lshift == - 7
163+ assert torch .equal (lut_values .to (torch .int32 ), expected_lut_values )
164+
165+
166+ def test_generate_16bit_full_range_identity_table_applies_scale_ratio () -> None :
167+ """Changing only scale multiplies each LUT value by input / output scale."""
168+ input_qargs = QuantArgs (
169+ scale = 0.5 ,
170+ zp = 0 ,
171+ qmin = - 32767 ,
172+ qmax = 32767 ,
173+ dtype = torch .int16 ,
174+ )
175+ output_qargs = QuantArgs (
176+ scale = 1.0 ,
177+ zp = 0 ,
178+ qmin = - 32767 ,
179+ qmax = 32767 ,
180+ dtype = torch .int16 ,
181+ )
182+
183+ lut_values , lshift = InsertTableOpsPass .generate_16_bit_table_values (
184+ torch .nn .Identity (),
185+ input_qargs ,
186+ output_qargs ,
187+ )
188+
189+ table_input_codes = torch .arange (- 32768 , 32769 , 128 , dtype = torch .int32 )
190+ scale_ratio = (
191+ input_qargs .get_scale_per_tensor () / output_qargs .get_scale_per_tensor ()
192+ )
193+ expected_lut_values = (table_input_codes * scale_ratio ).round ().to (torch .int32 )
194+
195+ assert lshift == - 7
196+ assert torch .equal (lut_values .to (torch .int32 ), expected_lut_values )
197+
198+
199+ def test_generate_16bit_table_clamps_endpoint_for_restricted_range () -> None :
200+ """A restricted input qmax clamps the final LUT value."""
201+ input_qargs = QuantArgs (
202+ scale = 1.0 ,
203+ zp = 0 ,
204+ qmin = - 32767 ,
205+ qmax = 30080 ,
206+ dtype = torch .int16 ,
207+ )
208+ output_qargs = QuantArgs (
209+ scale = 1.0 ,
210+ zp = 0 ,
211+ qmin = - 32767 ,
212+ qmax = 32767 ,
213+ dtype = torch .int16 ,
214+ )
215+
216+ lut_values , lshift = InsertTableOpsPass .generate_16_bit_table_values (
217+ torch .nn .Identity (),
218+ input_qargs ,
219+ output_qargs ,
220+ )
221+
222+ assert lshift == - 7
223+ assert int (lut_values [- 1 ]) == input_qargs .qmax
0 commit comments