- Add sexp_to_jax.py: JAX compiler for S-expression effects - Use jax.random.fold_in for deterministic but varying random per frame - Pass seed from recipe config through to JAX effects - Fix NVENC detection to do actual encode test - Add set_random_seed for deterministic Python random The fold_in approach allows frame_num to be traced (not static) while still producing different random patterns per frame, fixing the interference pattern issue. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
295 lines
5.5 KiB
Python
295 lines
5.5 KiB
Python
"""
|
|
Core Primitives - Always available, minimal essential set.
|
|
|
|
These are the primitives that form the foundation of the language.
|
|
They cannot be overridden by libraries.
|
|
"""
|
|
|
|
|
|
# Arithmetic
|
|
def prim_add(*args):
|
|
if len(args) == 0:
|
|
return 0
|
|
result = args[0]
|
|
for arg in args[1:]:
|
|
result = result + arg
|
|
return result
|
|
|
|
|
|
def prim_sub(a, b=None):
|
|
if b is None:
|
|
return -a
|
|
return a - b
|
|
|
|
|
|
def prim_mul(*args):
|
|
if len(args) == 0:
|
|
return 1
|
|
result = args[0]
|
|
for arg in args[1:]:
|
|
result = result * arg
|
|
return result
|
|
|
|
|
|
def prim_div(a, b):
|
|
return a / b
|
|
|
|
|
|
def prim_mod(a, b):
|
|
return a % b
|
|
|
|
|
|
def prim_abs(x):
|
|
return abs(x)
|
|
|
|
|
|
def prim_min(*args):
|
|
return min(args)
|
|
|
|
|
|
def prim_max(*args):
|
|
return max(args)
|
|
|
|
|
|
def prim_round(x):
|
|
import numpy as np
|
|
if hasattr(x, '_data'): # Xector
|
|
from .xector import Xector
|
|
return Xector(np.round(x._data), x._shape)
|
|
if isinstance(x, np.ndarray):
|
|
return np.round(x)
|
|
return round(x)
|
|
|
|
|
|
def prim_floor(x):
|
|
import numpy as np
|
|
if hasattr(x, '_data'): # Xector
|
|
from .xector import Xector
|
|
return Xector(np.floor(x._data), x._shape)
|
|
if isinstance(x, np.ndarray):
|
|
return np.floor(x)
|
|
import math
|
|
return math.floor(x)
|
|
|
|
|
|
def prim_ceil(x):
|
|
import numpy as np
|
|
if hasattr(x, '_data'): # Xector
|
|
from .xector import Xector
|
|
return Xector(np.ceil(x._data), x._shape)
|
|
if isinstance(x, np.ndarray):
|
|
return np.ceil(x)
|
|
import math
|
|
return math.ceil(x)
|
|
|
|
|
|
# Comparison
|
|
def prim_lt(a, b):
|
|
return a < b
|
|
|
|
|
|
def prim_gt(a, b):
|
|
return a > b
|
|
|
|
|
|
def prim_le(a, b):
|
|
return a <= b
|
|
|
|
|
|
def prim_ge(a, b):
|
|
return a >= b
|
|
|
|
|
|
def prim_eq(a, b):
|
|
if isinstance(a, float) or isinstance(b, float):
|
|
return abs(a - b) < 1e-9
|
|
return a == b
|
|
|
|
|
|
def prim_ne(a, b):
|
|
return not prim_eq(a, b)
|
|
|
|
|
|
# Logic
|
|
def prim_not(x):
|
|
return not x
|
|
|
|
|
|
def prim_and(*args):
|
|
for a in args:
|
|
if not a:
|
|
return False
|
|
return True
|
|
|
|
|
|
def prim_or(*args):
|
|
for a in args:
|
|
if a:
|
|
return True
|
|
return False
|
|
|
|
|
|
# Basic data access
|
|
def prim_get(obj, key, default=None):
|
|
"""Get value from dict or list."""
|
|
if isinstance(obj, dict):
|
|
return obj.get(key, default)
|
|
elif isinstance(obj, (list, tuple)):
|
|
try:
|
|
return obj[int(key)]
|
|
except (IndexError, ValueError):
|
|
return default
|
|
return default
|
|
|
|
|
|
def prim_nth(seq, i):
|
|
i = int(i)
|
|
if 0 <= i < len(seq):
|
|
return seq[i]
|
|
return None
|
|
|
|
|
|
def prim_first(seq):
|
|
return seq[0] if seq else None
|
|
|
|
|
|
def prim_length(seq):
|
|
return len(seq)
|
|
|
|
|
|
def prim_list(*args):
|
|
return list(args)
|
|
|
|
|
|
# Type checking
|
|
def prim_is_number(x):
|
|
return isinstance(x, (int, float))
|
|
|
|
|
|
def prim_is_string(x):
|
|
return isinstance(x, str)
|
|
|
|
|
|
def prim_is_list(x):
|
|
return isinstance(x, (list, tuple))
|
|
|
|
|
|
def prim_is_dict(x):
|
|
return isinstance(x, dict)
|
|
|
|
|
|
def prim_is_nil(x):
|
|
return x is None
|
|
|
|
|
|
# Higher-order / iteration
|
|
def prim_reduce(seq, init, fn):
|
|
"""(reduce seq init fn) — fold left: fn(fn(fn(init, s0), s1), s2) ..."""
|
|
acc = init
|
|
for item in seq:
|
|
acc = fn(acc, item)
|
|
return acc
|
|
|
|
|
|
def prim_map(seq, fn):
|
|
"""(map seq fn) — apply fn to each element, return new list."""
|
|
return [fn(item) for item in seq]
|
|
|
|
|
|
def prim_range(*args):
|
|
"""(range end), (range start end), or (range start end step) — integer range."""
|
|
if len(args) == 1:
|
|
return list(range(int(args[0])))
|
|
elif len(args) == 2:
|
|
return list(range(int(args[0]), int(args[1])))
|
|
elif len(args) >= 3:
|
|
return list(range(int(args[0]), int(args[1]), int(args[2])))
|
|
return []
|
|
|
|
|
|
# Random
|
|
import random
|
|
_rng = random.Random()
|
|
|
|
def set_random_seed(seed):
|
|
"""Set the random seed for deterministic output."""
|
|
global _rng
|
|
_rng = random.Random(seed)
|
|
|
|
def prim_rand():
|
|
"""Return random float in [0, 1)."""
|
|
return _rng.random()
|
|
|
|
def prim_rand_int(lo, hi):
|
|
"""Return random integer in [lo, hi]."""
|
|
return _rng.randint(int(lo), int(hi))
|
|
|
|
def prim_rand_range(lo, hi):
|
|
"""Return random float in [lo, hi)."""
|
|
return lo + _rng.random() * (hi - lo)
|
|
|
|
def prim_map_range(val, from_lo, from_hi, to_lo, to_hi):
|
|
"""Map value from one range to another."""
|
|
if from_hi == from_lo:
|
|
return to_lo
|
|
t = (val - from_lo) / (from_hi - from_lo)
|
|
return to_lo + t * (to_hi - to_lo)
|
|
|
|
|
|
# Core primitives dict
|
|
PRIMITIVES = {
|
|
# Arithmetic
|
|
'+': prim_add,
|
|
'-': prim_sub,
|
|
'*': prim_mul,
|
|
'/': prim_div,
|
|
'mod': prim_mod,
|
|
'abs': prim_abs,
|
|
'min': prim_min,
|
|
'max': prim_max,
|
|
'round': prim_round,
|
|
'floor': prim_floor,
|
|
'ceil': prim_ceil,
|
|
|
|
# Comparison
|
|
'<': prim_lt,
|
|
'>': prim_gt,
|
|
'<=': prim_le,
|
|
'>=': prim_ge,
|
|
'=': prim_eq,
|
|
'!=': prim_ne,
|
|
|
|
# Logic
|
|
'not': prim_not,
|
|
'and': prim_and,
|
|
'or': prim_or,
|
|
|
|
# Data access
|
|
'get': prim_get,
|
|
'nth': prim_nth,
|
|
'first': prim_first,
|
|
'length': prim_length,
|
|
'len': prim_length,
|
|
'list': prim_list,
|
|
|
|
# Type predicates
|
|
'number?': prim_is_number,
|
|
'string?': prim_is_string,
|
|
'list?': prim_is_list,
|
|
'dict?': prim_is_dict,
|
|
'nil?': prim_is_nil,
|
|
'is-nil': prim_is_nil,
|
|
|
|
# Higher-order / iteration
|
|
'reduce': prim_reduce,
|
|
'fold': prim_reduce,
|
|
'map': prim_map,
|
|
'range': prim_range,
|
|
|
|
# Random
|
|
'rand': prim_rand,
|
|
'rand-int': prim_rand_int,
|
|
'rand-range': prim_rand_range,
|
|
'map-range': prim_map_range,
|
|
}
|