File size: 1,696 Bytes
b86c469
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
"""

MACROGROK Q-format simulator — matches infer4.asm semantics.

Q1.14: 1 sign + 1 int + 14 frac (range [-2, 2) but used as [-1,1) for INPUT)

Q3.12: 3 int + 12 frac, Q7.24 32-bit ACC.

"""
WEIGHTS_Q1_14 = [2458, -1638, 819, 3277]
BIAS_Q3_12 = -256
ALPHA_Q1_14 = 12288  # 0.75

def q1_14_to_float(x): return x / 16384.0
def float_to_q1_14(f): return max(-16384, min(16383, int(round(f*16384))))
def q3_12_to_float(x): return x / 4096.0

def infer4(input_q1_14):
    assert len(input_q1_14)==4
    # DOT4 Q1.14*Q1.14 -> Q3.24 in 32-bit, >>4 per product
    acc = 0  # Q7.24 but we use Python int as Q3.24 accum
    for w, inp in zip(WEIGHTS_Q1_14, input_q1_14):
        prod = w * inp  # Q2.28
        prod_q3_24 = prod >> 4  # arithmetic shift
        acc += prod_q3_24
    # ACC Q3.24 -> Q3.12
    tmp0 = acc >> 12  # arithmetic
    tmp0 = tmp0 + BIAS_Q3_12
    # SAT Q3.12 [-8192,8191]
    sat = False
    if tmp0 < -8192: tmp0=-8192; sat=True
    if tmp0 > 8191: tmp0=8191; sat=True
    tmp1 = 16384 if tmp0 >=0 else -16384
    flags = 0
    if tmp0>=0: flags|=0x4
    if sat: flags|=0x2
    # UPDATE_STATE 3/4 low-pass (STATE persists, init 0)
    global STATE
    try: STATE
    except NameError: globals()['STATE']=0
    STATE = (3*STATE + tmp1)//4  # arithmetic, Q1.14 scale
    flags|=0x1
    return STATE, tmp0, flags

if __name__=="__main__":
    for vec in [[16384,0,0,0],[0,16384,0,0],[-16384,-16384,0,0],[8192,8192,8192,8192]]:
        out, score, flags = infer4(vec)
        print(f"in={[q1_14_to_float(x) for x in vec]} -> score Q3.12 {score} ({q3_12_to_float(score):.3f}) out Q1.14 {out} ({q1_14_to_float(out):.3f}) flags {flags:03b}")