mjev-spatial / src /spatial_jev /numeric_tasks.py
multimodalart's picture
multimodalart HF Staff
Upload folder using huggingface_hub
85300f6 verified
Raw History Blame Contribute Delete
3.38 kB
"""Input-selected numeric codebooks; never interpret counts or degrees as meters."""
import math
COUNT_CODEBOOK = {"version": "integer-32x32-v1", "unit": "count", "fine_bins": 32,
"maximum": 1023}
ANGLE_CODEBOOK = {"version": "degrees-30x32-v1", "unit": "deg", "fine_bins": 32,
"edges_m": list(range(0, 361, 30)), "overflow_representative_m": 360.0}
# edges_m is the historical controller's internal key, not the physical unit.
# Public answer spaces, prompts, validators and decoded units remain explicit.
def codebook_for(inp, metric):
unit = inp["answer_space"].get("unit", "m")
if unit == "m":
return metric
if unit == "count":
return COUNT_CODEBOOK
if unit == "deg":
return ANGLE_CODEBOOK
raise ValueError(f"Unsupported numeric unit: {unit}")
def numeric_path(value, codebook):
from .hierarchy import scalar_path
if codebook.get("unit", "m") != "count":
if codebook.get("unit") == "deg" and not 0 <= value <= 360:
raise ValueError("Angle outside [0, 360]")
return scalar_path(value, codebook)
if isinstance(value, bool) or not math.isfinite(value) or int(value) != value or not 0 <= value <= 1023:
raise ValueError("Count outside integer codebook [0, 1023]")
return [int(value) // 32, int(value) % 32]
def numeric_done(path, codebook):
return len(path) == 2 or (codebook.get("unit", "m") != "count" and path[0] in (0, len(codebook["edges_m"])))
def decode_numeric(path, codebook):
from .hierarchy import decode_scalar
if codebook.get("unit", "m") != "count":
return decode_scalar(path, codebook)
if len(path) != 2 or any(not 0 <= n < 32 for n in path):
raise ValueError("Invalid integer decision path")
return 32 * path[0] + path[1]
def numeric_prompt(inp, root, codebook):
"""Non-metric prompts. Metric prompts intentionally keep their old exact bytes."""
if codebook["unit"] == "count":
options = ([f"count {32*i} through {32*i+31} (inclusive)" for i in range(32)]
if root is None else [f"count exactly {32*root+i}" for i in range(32)])
instruction = "Answer the counting question. Select the integer count or inclusive count range."
else:
from .hierarchy import scalar_interval
edges = codebook["edges_m"]
if root is None:
options = ["exactly zero degrees"]
options += [f"{lo:g} to {hi:g} degrees" for lo, hi in zip(edges, edges[1:])]
options[1] = "greater than zero and less than 30 degrees"
options += ["greater than 360 degrees (out of supported range)"]
else:
lo, hi = scalar_interval(root, codebook)
step = (hi - lo) / codebook["fine_bins"]
options = [f"{lo+i*step:.6g} to {lo+(i+1)*step:.6g} degrees" for i in range(32)]
instruction = ("Estimate the angle in degrees, preserving the reference frame and rotation "
"direction specified in the question. Ranges include the lower endpoint and "
"exclude the upper, except that 360 is included. Exact zero has its own class.")
return (f"Question: {inp['question']}\n{instruction}\nOptions:\n"
+ "\n".join(f"{i+1}. {s}" for i, s in enumerate(options))
+ "\nSelect exactly one option."), len(options)