Quantization in practice
Generated by tools/gen_example_pages.py from mnd_scripts/examples/example_quantization.mnd — the output below is a real run, not a transcription. Regenerating the page reruns it.
the quantization preamble — the declaration every Maned program is REQUIRED to open with.
The program#
# ============================================================================
# example_quantization.mnd
# ----------------------------------------------------------------------------
# FUNCTIONALITY: the quantization preamble -- the declaration every Maned
# program is REQUIRED to open with.
#
# A .mnd file that does not declare its numeric contract does not parse. That
# is deliberate: Maned's runtime has no floating point at all, so "what does
# the integer 500 mean?" is not an implementation detail to be discovered later
# -- it is the first thing the file says.
#
# mnd::quantres=N values are reals x 10^N (alias: decimal_places)
# mnd::quantmax=HI upper bound of the represented range
# mnd::quantmin=LO lower bound
#
# The preamble must supply quantres (or decimal_places) AND quantmax+quantmin.
# From those the toolchain derives the wire dtype: an INT8 MNPK envelope for
# narrow ranges, INT32 for wide ones.
#
# gap_003 honesty note: the historical alternatives are now REFUSED with
# numbered errors instead of being silently ignored -- mnd::quantrange
# (E010: nothing consumed it, so a quantrange-only preamble yielded a 0/0
# quantizer), legacy mnd::quant (E011), and every _fwd/_grad suffixed
# variant (E011: there is no separate forward/gradient pass to consume them).
#
# WHY IT IS A LANGUAGE FEATURE AND NOT A LIBRARY CALL: these directives fill
# the MNPK quant block that ships with the compiled IR to a remote worker.
# The scale is part of the program's identity, so the same file cannot mean one
# thing locally and another on an FPGA.
#
# RUN: ../maned-run examples/example_quantization.mnd
# ============================================================================
# --- the standard preamble --------------------------------------------------
mnd::quantmax=1000;
mnd::quantmin=-1000;
mnd::quantres=3; # 10^3: the integer 1000 denotes 1.000
# Values at scale 1000: 0.250, 0.500, -0.750, 1.000
in::x = [[250,500],[-750,1000]];
in::one = 1000; # the number 1.000 at this scale
in::half = 500; # 0.500
calc::lambda_flow scaled_math(x, one, half) {
# Addition and subtraction are scale-preserving: no rescale needed.
x half add shifted =
# Multiplication is NOT. Two operands at 10^3 give a result at 10^6, so
# the program divides the scale back out explicitly. Nothing is implicit.
x x elemwise_mul squared_raw =
squared_raw one div squared =
# Multiplying by the unit element leaves a value unchanged only AFTER the
# rescale -- a useful sanity check on the contract.
x one elemwise_mul times_one_raw =
times_one_raw one div times_one =
times_one x == unit_law_holds =
# Activations decode against 10^quantres, so sigmoid(0.250) reads as 0.562
# here and would read as 562 at any other scale only after rescaling.
x sigmoid activated =
x tanh hyperbolic =
} return shifted, squared_raw, squared, times_one, unit_law_holds,
activated, hyperbolic;
The run#
$ maned-run mnd_scripts/examples/example_quantization.mnd
flow 'scaled_math' outputs:
shifted : [2x2] = [750, 1000, -250, 1500]
squared_raw : [2x2] = [62500, 250000, 562500, 1000000]
squared : [2x2] = [62, 250, 562, 1000]
times_one : [2x2] = [250, 500, -750, 1000]
unit_law_holds : [2x2] = [1, 1, 1, 1]
activated : [2x2] = [562, 622, 321, 731]
hyperbolic : [2x2] = [245, 462, -635, 761]
Next#
- Language guide — every operation the examples use, in the full table.
- RPN syntax specification — the normative rules.