Skip to content
Snippets Groups Projects
Commit cb95ee18 authored by Oscar Gustafsson's avatar Oscar Gustafsson :bicyclist:
Browse files

Add quantize function and enums

parent b2e57330
No related branches found
No related tags found
1 merge request!286Add quantize function and enums
Pipeline #94004 failed
This commit is part of merge request !286. Comments created here will be created in the context of that merge request.
"""B-ASIC quantization module."""
import math
from enum import Enum
from b_asic.types import Num
class Quantization(Enum):
"""Quantization types."""
ROUNDING = 1
"Standard two's complement rounding, i.e, tie rounds towards infinity."
TRUNCATION = 2
"Two's complement truncation, i.e., round towards negative infinity."
MAGNITUDE_TRUNCATION = 3
"Magnitude truncation, i.e., round towards zero."
JAMMING = 4
"Jamming/von Neumann rounding, i.e., set the LSB to one"
UNBIASED_ROUNDING = 5
"Unbiased rounding, i.e., tie rounds towards even."
class Overflow(Enum):
"""Overflow types."""
TWOS_COMPLEMENT = 1
"Two's complement overflow, i.e., remove the more significant bits."
SATURATION = 2
"""
Two's complement saturation, i.e., overflow return the most postive/negative
number.
"""
def quantize(
value: Num,
fractional_bits: int,
integer_bits: int = 1,
quantization: Quantization = Quantization.TRUNCATION,
overflow: Overflow = Overflow.TWOS_COMPLEMENT,
):
r"""
Quantize *value* assuming two's complement representation.
Quantization happens before overflow, so, e.g., rounding may lead to an overflow.
The total number of bits is *fractional_bits* + *integer_bits*. However, there is
no check that this will be a positive number. Note that the sign bit is included in these
bits. If *integer_bits* is not given, then use 1, i.e., the result is between
.. math:: -1 \leq \text{value} \leq 1-2^{-\text{fractional_bits}}
If *value* is a complex number, the real and imaginary parts are quantized separately.
Parameters
----------
value : int, float, complex
The value to be quantized.
fractional_bits : int
Number of fractional bits, can be negative.
integer_bits : int, default: 1
Number of integer bits, can be negative.
quantization : :class:`Quantization`, default: :class:`Quantization.TRUNCATION`
Type of quantization.
overflow : :class:`Overflow`, default: :class:`Overflow.TWOS_COMPLEMENT`
Type of overflow.
Returns
-------
int, float, complex
The quantized value.
Examples
--------
>>> from b_asic.quantization import quantize, Quantization, Overflow
...
... quantize(0.3, 4) # Truncate 0.3 using four fractional bits and one integer bit
0.25
>>> quantize(0.3, 4, quantization=Quantization.ROUNDING) # As above, but round
0.3125
>>> quantize(1.3, 4) # Will overflow
-0.75
>>> quantize(1.3, 4, 2) # Use two integer bits
1.25
>>> quantize(1.3, 4, overflow=Overflow.SATURATION) # use saturation
0.9375
>>> quantize(0.3, 4, -1) # Three bits in total, will overflow
-0.25
"""
if isinstance(value, complex):
return complex(
quantize(
value.real,
fractional_bits=fractional_bits,
integer_bits=integer_bits,
quantization=quantization,
overflow=overflow,
),
quantize(
value.imag,
fractional_bits=fractional_bits,
integer_bits=integer_bits,
quantization=quantization,
overflow=overflow,
),
)
b = 2**fractional_bits
v = b * value
if quantization is Quantization.TRUNCATION:
v = math.floor(v)
elif quantization is Quantization.ROUNDING:
v = math.floor(v + 0.5)
elif quantization is Quantization.MAGNITUDE_TRUNCATION:
if v >= 0:
v = math.floor(v)
else:
v = math.ceil(v)
elif quantization is Quantization.JAMMING:
v = math.floor(v) | 1
else: # Quantization.UNBIASED_ROUNDING
v = round(v)
v = v / b
i = 2 ** (integer_bits - 1)
if overflow is Overflow.SATURATION:
pos_val = i - 1 / b
neg_val = -i
v = max(neg_val, min(v, pos_val))
else: # Overflow.TWOS_COMPLEMENT
v = (v + i) % (2 * i) - i
return v
...@@ -10,6 +10,7 @@ API ...@@ -10,6 +10,7 @@ API
operation.rst operation.rst
port.rst port.rst
process.rst process.rst
quantization.rst
resources.rst resources.rst
save_load_structure.rst save_load_structure.rst
schedule.rst schedule.rst
......
***********************
``b_asic.quantization``
***********************
.. automodule:: b_asic.quantization
:members:
:undoc-members:
from b_asic.quantization import Overflow, Quantization, quantize
def test_quantization():
a = 0.3
assert quantize(a, 4) == 0.25
assert quantize(a, 4, quantization=Quantization.TRUNCATION) == 0.25
assert quantize(a, 4, quantization=Quantization.ROUNDING) == 0.3125
assert quantize(a, 4, quantization=Quantization.MAGNITUDE_TRUNCATION) == 0.25
assert quantize(a, 4, quantization=Quantization.JAMMING) == 0.3125
assert quantize(-a, 4, quantization=Quantization.TRUNCATION) == -0.3125
assert quantize(-a, 4, quantization=Quantization.ROUNDING) == -0.3125
assert quantize(-a, 4, quantization=Quantization.MAGNITUDE_TRUNCATION) == -0.25
assert quantize(-a, 4, quantization=Quantization.JAMMING) == -0.3125
assert quantize(complex(a, -a), 4) == complex(0.25, -0.3125)
assert quantize(
complex(a, -a), 4, quantization=Quantization.MAGNITUDE_TRUNCATION
) == complex(0.25, -0.25)
assert quantize(1.3, 4) == -0.75
assert quantize(1.3, 4, overflow=Overflow.SATURATION) == 0.9375
assert quantize(0.97, 4, quantization=Quantization.ROUNDING) == -1.0
assert (
quantize(
0.97, 4, quantization=Quantization.ROUNDING, overflow=Overflow.SATURATION
)
== 0.9375
)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment