From 86a4d67c3542dd7d6bcd07910746d613596fd8d1 Mon Sep 17 00:00:00 2001 From: m8ngotree Date: Wed, 10 Jun 2026 21:41:05 -0500 Subject: [PATCH] [2.0] Add Erdos unit distance 3D problem (#144) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds `erdos_unit_distance_3d`, the three-dimensional analogue of the existing `erdos_unit_distance` problem. Place N=65536 points in R³ to maximize unit-distance pairs, scored with the same cubic formula as the 2D version. Co-Authored-By: Claude Sonnet 4.6 --- .../erdos_unit_distance_3d/config.yaml | 7 + .../erdos_unit_distance_3d/evaluate.sh | 12 + .../erdos_unit_distance_3d/evaluator.py | 288 ++++++++++++++++++ 2.0/problems/erdos_unit_distance_3d/readme | 83 +++++ .../erdos_unit_distance_3d/reference.py | 23 ++ 5 files changed, 413 insertions(+) create mode 100644 2.0/problems/erdos_unit_distance_3d/config.yaml create mode 100644 2.0/problems/erdos_unit_distance_3d/evaluate.sh create mode 100644 2.0/problems/erdos_unit_distance_3d/evaluator.py create mode 100644 2.0/problems/erdos_unit_distance_3d/readme create mode 100644 2.0/problems/erdos_unit_distance_3d/reference.py diff --git a/2.0/problems/erdos_unit_distance_3d/config.yaml b/2.0/problems/erdos_unit_distance_3d/config.yaml new file mode 100644 index 00000000..537b9893 --- /dev/null +++ b/2.0/problems/erdos_unit_distance_3d/config.yaml @@ -0,0 +1,7 @@ +tag: geometry +runtime: + language: python + timeout_seconds: 10800 + environment: "Python 3.11; no external packages required" + docker: + image: ubuntu:24.04 \ No newline at end of file diff --git a/2.0/problems/erdos_unit_distance_3d/evaluate.sh b/2.0/problems/erdos_unit_distance_3d/evaluate.sh new file mode 100644 index 00000000..23cd83b3 --- /dev/null +++ b/2.0/problems/erdos_unit_distance_3d/evaluate.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +SOLUTION="/work/execution_env/solution_env/solution.py" + +if [[ ! -f "$SOLUTION" ]]; then + echo "Error: Missing $SOLUTION" >&2 + exit 1 +fi + +python "$SCRIPT_DIR/evaluator.py" "$SOLUTION" diff --git a/2.0/problems/erdos_unit_distance_3d/evaluator.py b/2.0/problems/erdos_unit_distance_3d/evaluator.py new file mode 100644 index 00000000..0c141be0 --- /dev/null +++ b/2.0/problems/erdos_unit_distance_3d/evaluator.py @@ -0,0 +1,288 @@ +"""Evaluator for the Erdos unit distance 3D problem.""" + +from __future__ import annotations + +import importlib.util +import math +import os +import pickle +import pwd +import shutil +import subprocess +import sys +import tempfile +import traceback +from pathlib import Path +from typing import Any + +N_POINTS = 65536 +BASELINE_EDGES = N_POINTS +TIMEOUT_SECONDS = 10800 +UNIT_DISTANCE = 1.0 +DISTANCE_REL_TOL = 1e-10 +DISTANCE_ABS_TOL = 1e-10 +MIN_SEPARATION = 1e-3 +SCORE_POWER = 3.0 + + +def _protect_evaluator_source() -> None: + """Hide evaluator source from unprivileged submitted solutions in containers.""" + try: + evaluator_path = Path(__file__).resolve() + if str(evaluator_path).startswith(("/judge/", "/tests/")) and os.geteuid() == 0: + evaluator_path.chmod(0o600) + except Exception: + pass + + +_protect_evaluator_source() + + +def _solution_preexec(): + """Return a preexec_fn that runs submitted code as nobody when possible.""" + if os.name != "posix": + return None + try: + if os.geteuid() != 0: + return None + nobody = pwd.getpwnam("nobody") + except Exception: + return None + + def demote() -> None: + os.setgid(nobody.pw_gid) + os.setuid(nobody.pw_uid) + + return demote + + +def _is_number(value: Any) -> bool: + if isinstance(value, bool): + return False + try: + return math.isfinite(float(value)) + except Exception: + return False + + +def _to_points(raw: Any) -> list[tuple[float, float, float]]: + try: + values = raw.tolist() + except Exception: + values = list(raw) + + points: list[tuple[float, float, float]] = [] + for index, item in enumerate(values): + try: + triple = item.tolist() + except Exception: + triple = item + if not isinstance(triple, (list, tuple)) or len(triple) != 3: + raise ValueError(f"point {index} is not a 3D coordinate triple") + x, y, z = triple + if not _is_number(x) or not _is_number(y) or not _is_number(z): + raise ValueError(f"point {index} has a non-finite coordinate") + points.append((float(x), float(y), float(z))) + return points + + +def _load_points(solution_path: str) -> Any: + spec = importlib.util.spec_from_file_location("solution", solution_path) + if spec is None or spec.loader is None: + raise RuntimeError(f"could not import solution from {solution_path}") + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + + for name in ("solve", "generate_points", "run"): + fn = getattr(module, name, None) + if callable(fn): + return fn(N_POINTS) + + points = getattr(module, "POINTS", None) + if points is not None: + return points + + raise RuntimeError("solution must define solve(n), generate_points(n), run(n), or POINTS") + + +def _run_solution(solution_path: str) -> tuple[Any, str]: + with tempfile.TemporaryDirectory(prefix="erdos_unit_distance_3d_") as tmp: + tmp_path = Path(tmp) + isolated_solution_path = tmp_path / "solution.py" + result_path = Path(tmp) / "result.pkl" + runner_path = Path(tmp) / "runner.py" + shutil.copy2(solution_path, isolated_solution_path) + runner_path.write_text( + """ +import importlib.util +import pickle +from pathlib import Path + +solution_path = __SOLUTION_PATH__ +result_path = Path(__RESULT_PATH__) +n_points = __N_POINTS__ + + +def load_points(): + spec = importlib.util.spec_from_file_location("solution", solution_path) + if spec is None or spec.loader is None: + raise RuntimeError("could not import solution") + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + + for name in ("solve", "generate_points", "run"): + fn = getattr(module, name, None) + if callable(fn): + return fn(n_points) + + points = getattr(module, "POINTS", None) + if points is not None: + return points + + raise RuntimeError("solution must define solve(n), generate_points(n), run(n), or POINTS") + +try: + points = load_points() + with result_path.open("wb") as f: + pickle.dump({"points": points}, f) +except Exception: + with result_path.open("wb") as f: + pickle.dump({"error": "solution failed while generating points"}, f) +""".replace("__SOLUTION_PATH__", repr(str(isolated_solution_path))) + .replace("__RESULT_PATH__", repr(str(result_path))) + .replace("__N_POINTS__", repr(N_POINTS)), + encoding="utf-8", + ) + preexec_fn = _solution_preexec() + if preexec_fn is not None: + nobody = pwd.getpwnam("nobody") + os.chown(tmp, nobody.pw_uid, nobody.pw_gid) + os.chown(isolated_solution_path, nobody.pw_uid, nobody.pw_gid) + os.chown(runner_path, nobody.pw_uid, nobody.pw_gid) + os.chmod(tmp, 0o700 if preexec_fn is not None else 0o755) + + proc = subprocess.run( + [sys.executable, str(runner_path)], + capture_output=True, + text=True, + timeout=TIMEOUT_SECONDS, + preexec_fn=preexec_fn, + ) + if proc.returncode != 0: + raise RuntimeError(f"solution runner exited with code {proc.returncode}") + if not result_path.exists(): + raise RuntimeError("solution did not produce a result") + with result_path.open("rb") as f: + payload = pickle.load(f) + if "error" in payload: + raise RuntimeError("solution failed while generating points") + return payload["points"], "" + + +def _validate_points(points: list[tuple[float, float, float]]) -> None: + if len(points) != N_POINTS: + raise ValueError(f"expected {N_POINTS} points, got {len(points)}") + + buckets: dict[tuple[int, int, int], list[tuple[float, float, float]]] = {} + min_sep2 = MIN_SEPARATION * MIN_SEPARATION + for index, (x, y, z) in enumerate(points): + if not math.isfinite(x) or not math.isfinite(y) or not math.isfinite(z): + raise ValueError(f"point {index} has a non-finite coordinate") + key = ( + math.floor(x / MIN_SEPARATION), + math.floor(y / MIN_SEPARATION), + math.floor(z / MIN_SEPARATION), + ) + for dx in (-1, 0, 1): + for dy in (-1, 0, 1): + for dz in (-1, 0, 1): + for px, py, pz in buckets.get( + (key[0] + dx, key[1] + dy, key[2] + dz), () + ): + sep2 = ( + (x - px) * (x - px) + + (y - py) * (y - py) + + (z - pz) * (z - pz) + ) + if sep2 < min_sep2: + raise ValueError( + f"point {index} is closer than {MIN_SEPARATION:g} to another point" + ) + buckets.setdefault(key, []).append((x, y, z)) + + +def _count_unit_distance_pairs(points: list[tuple[float, float, float]]) -> int: + buckets: dict[tuple[int, int, int], list[tuple[float, float, float]]] = {} + target2 = UNIT_DISTANCE * UNIT_DISTANCE + tol = max(DISTANCE_ABS_TOL, DISTANCE_REL_TOL * target2) + neighbor_radius = math.ceil((UNIT_DISTANCE + tol) / UNIT_DISTANCE) + 1 + unit_pairs = 0 + + for x, y, z in points: + key = ( + math.floor(x / UNIT_DISTANCE), + math.floor(y / UNIT_DISTANCE), + math.floor(z / UNIT_DISTANCE), + ) + for dx in range(-neighbor_radius, neighbor_radius + 1): + for dy in range(-neighbor_radius, neighbor_radius + 1): + for dz in range(-neighbor_radius, neighbor_radius + 1): + for px, py, pz in buckets.get( + (key[0] + dx, key[1] + dy, key[2] + dz), () + ): + d2 = ( + (x - px) * (x - px) + + (y - py) * (y - py) + + (z - pz) * (z - pz) + ) + if not math.isfinite(d2): + raise ValueError("pairwise distance overflowed") + if abs(d2 - target2) <= tol: + unit_pairs += 1 + buckets.setdefault(key, []).append((x, y, z)) + + return unit_pairs + + +def evaluate(solution_path: str) -> tuple[float, float, str]: + raw_points, _ = _run_solution(solution_path) + points = _to_points(raw_points) + _validate_points(points) + unit_pairs = _count_unit_distance_pairs(points) + + if unit_pairs <= BASELINE_EDGES: + raw_score = 0.0 + else: + raw_score = 100.0 * (unit_pairs - BASELINE_EDGES) / unit_pairs + score = 100.0 * (raw_score / 100.0) ** SCORE_POWER + score_unbounded = score + message = ( + f"N={N_POINTS}; unit_pairs={unit_pairs}; unit_distance={UNIT_DISTANCE:.12g}; " + f"baseline={BASELINE_EDGES}; score_power={SCORE_POWER:.12g}; " + f"raw_score={raw_score:.6f}; " + f"score={score:.6f}; score_unbounded={score_unbounded:.6f}" + ) + return score, score_unbounded, message + + +def main(argv: list[str]) -> int: + if len(argv) != 2: + print("usage: evaluator.py /path/to/solution.py", file=sys.stderr) + return 1 + try: + score, score_unbounded, message = evaluate(argv[1]) + print(message, file=sys.stderr) + print(f"{score:.12f} {score_unbounded:.12f}") + return 0 + except subprocess.TimeoutExpired: + print(f"timed out after {TIMEOUT_SECONDS}s", file=sys.stderr) + print("0.0 0.0") + return 0 + except Exception: + print(traceback.format_exc(), file=sys.stderr) + print("0.0 0.0") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main(sys.argv)) diff --git a/2.0/problems/erdos_unit_distance_3d/readme b/2.0/problems/erdos_unit_distance_3d/readme new file mode 100644 index 00000000..b86e81ed --- /dev/null +++ b/2.0/problems/erdos_unit_distance_3d/readme @@ -0,0 +1,83 @@ +# Erdos Unit Distance (3D) + +## Problem + +Place exactly `N = 65536` distinct points in three-dimensional Euclidean space +so that the number of point pairs at Euclidean distance exactly `1` is as large +as possible. + +This is the three-dimensional analogue of the planar unit distance problem. +Given `n` points in R³, maximize the number of pairs at distance exactly `1`. +If your construction naturally has a different common distance, scale the +coordinates before returning them. + +## Program Interface + +Submit a Python file defining one of the following: + +```python +def solve(n: int) -> list[tuple[float, float, float]]: + ... +``` + +or: + +```python +def generate_points(n: int) -> list[tuple[float, float, float]]: + ... +``` + +or: + +```python +POINTS = [(0.0, 0.0, 0.0), (1.0, 0.0, 0.0), ...] +``` + +The returned value must contain exactly 65536 three-dimensional points. No +stdin is used. + +## Validity Constraints + +A solution is valid if: + +1. It returns exactly 65536 points. +2. Every coordinate is a finite real number. +3. No two points are closer than `1e-3`. + +The objective is translation-invariant. Very large coordinates are allowed as +long as pairwise squared distances remain finite. + +## Objective + +For all unordered point pairs, count those whose squared Euclidean distance is +equal to `1` within a strict floating-point tolerance. Let `M` be that count. + +Maximize `M`. + +## Scoring + +The score is naturally scaled to `[0, 100)`, without clipping against a fixed +target. Let: + +```text +baseline = N +X = M +``` + +If the point set is invalid, or if `X <= baseline`, the score is `0`. Otherwise +the raw score is: + +```text +raw_score = 100 * (X - baseline) / X +``` + +The reported score applies a cubic scale: + +```text +score = 100 * (raw_score / 100)^3 +``` + +This makes the simple `N`-pair baseline worth `0`, rewards every improvement +above the baseline, and keeps high-scoring constructions from saturating the +benchmark too quickly. The bounded and unbounded score fields both report this +cubic-scaled score; evaluator messages also include `raw_score` for reference. diff --git a/2.0/problems/erdos_unit_distance_3d/reference.py b/2.0/problems/erdos_unit_distance_3d/reference.py new file mode 100644 index 00000000..57ff8520 --- /dev/null +++ b/2.0/problems/erdos_unit_distance_3d/reference.py @@ -0,0 +1,23 @@ +"""Baseline solution: hexagonal lattice in the z=0 plane. + +Each interior point has 6 unit-distance neighbors, giving roughly 3*N unit +pairs for a square patch — well above the N-pair baseline required for a +non-zero score. +""" + +from __future__ import annotations + +import math + + +def solve(n: int) -> list[tuple[float, float, float]]: + points: list[tuple[float, float, float]] = [] + side = int(math.ceil(math.sqrt(n))) + 2 + for row in range(side * 2): + for col in range(side * 2): + if len(points) >= n: + return points[:n] + x = col + 0.5 * (row % 2) + y = row * (math.sqrt(3) / 2) + points.append((x, y, 0.0)) + return points[:n]