diff --git a/.github/workflows/build-bingo.yml b/.github/workflows/build-bingo.yml new file mode 100755 index 00000000..d04f55bd --- /dev/null +++ b/.github/workflows/build-bingo.yml @@ -0,0 +1,35 @@ +name: Build Bingo Docker Image + +on: + workflow_dispatch: # manual trigger + push: + branches: + - image.pull + paths: + - 'algorithms/bingo/**' + - 'Dockerfile.bingo' + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile.bingo + push: true + tags: ${{ secrets.DOCKERHUB_USERNAME }}/srbench-bingo:latest + build-args: | + ALGORITHM=bingo \ No newline at end of file diff --git a/Dockerfile.bingo b/Dockerfile.bingo new file mode 100755 index 00000000..52c4ec76 --- /dev/null +++ b/Dockerfile.bingo @@ -0,0 +1,28 @@ +FROM srbench/base:latest + +# Build argument for algorithm name +ARG ALGORITHM=bingo + +# Copy install script and algorithm files +COPY scripts/install_algorithm.sh /install/install_algorithm.sh +COPY base_environment.yml /install/base_environment.yml +COPY algorithms/${ALGORITHM} /install/algorithms/${ALGORITHM} + +# Run installation +WORKDIR /install +RUN eval "$(micromamba shell hook --shell bash)" && \ + micromamba activate base && \ + bash install_algorithm.sh "/install/algorithms/${ALGORITHM}" && \ + micromamba clean --all --yes && \ + mkdir -p /srbench + +# Set environment +ENV ALGORITHM=${ALGORITHM} +ENV LD_LIBRARY_PATH=/opt/conda/lib:${LD_LIBRARY_PATH} + +# Default working directory +WORKDIR /srbench + +# Entrypoint that activates micromamba +ENTRYPOINT ["/bin/bash", "-c", "eval \"$(micromamba shell hook --shell bash)\" && micromamba activate base && cd /srbench && exec \"$@\"", "--"] +CMD ["bash"] diff --git a/experiment/hall_of_fame_2026-10-16_124419.865.pkl b/experiment/hall_of_fame_2026-10-16_124419.865.pkl new file mode 100644 index 00000000..2e47a6ba Binary files /dev/null and b/experiment/hall_of_fame_2026-10-16_124419.865.pkl differ diff --git a/experiment/hall_of_fame_2026-10-16_124605.771.pkl b/experiment/hall_of_fame_2026-10-16_124605.771.pkl new file mode 100644 index 00000000..9c0a2a3e Binary files /dev/null and b/experiment/hall_of_fame_2026-10-16_124605.771.pkl differ diff --git a/scripts/run_srbench.sh b/scripts/run_srbench.sh new file mode 100755 index 00000000..4a478cc1 --- /dev/null +++ b/scripts/run_srbench.sh @@ -0,0 +1,105 @@ +#!/bin/bash +# Submit one SLURM job per SRBench algorithm, each running analyze.py +# inside its Apptainer/Singularity container. Modeled on run_distributed.sh. +# +# Usage (from the srbench repo root): +# bash scripts/run_srbench.sh -a gplearn -d /path/to/pmlb/datasets +# bash scripts/run_srbench.sh -a gplearn,feat -d /path/to/pmlb/datasets -- -n_trials 10 -time_limit 9:00 +# +# Options: +# -a ALGS Comma-separated algorithms (default: gplearn). Each must have +# a SIF at $SIF_DIR/srbench-.sif (see scripts/singularity_pull_all.sh). +# -d DIR PMLB datasets directory (required). Bound read-only into the container. +# -c CPUS CPUs per task / parallel jobs per algorithm (default: 8) +# -t TIME SLURM walltime, e.g. 24:00:00 (default: 24:00:00) +# -m MEM Memory per node, e.g. 32G (default: 32G) +# -p PART SLURM partition (default: cpu_batch) +# -r RDIR Results dir, relative to experiment/ (default: results) +# -n Dry run: print the sbatch commands without submitting +# -- ARGS Everything after -- is passed verbatim to analyze.py +# (e.g. -n_trials 10 -tuned -target_noise 0.01) +set -uo pipefail + +CURRENT_DIR=$(pwd) +if [[ $CURRENT_DIR == */scripts ]]; then + cd .. +fi +echo "[srbench] Running from $(pwd)" + +# --- Configuration (override via environment) --------------------------------- +SIF_DIR=${SIF_DIR:-/pgeoprj/dxt6/maromba/notebooks/container/output_dir} +LOG_DIRECTORY=${LOG_DIRECTORY:-logs} +mkdir -p "$LOG_DIRECTORY" + +# --- Defaults ----------------------------------------------------------------- +ALGS="gplearn" +DATASETS="" +CPUS=8 +TIME="24:00:00" +MEM="32G" +PARTITION="cpu_batch" +RDIR="results" +DRYRUN=0 +ANALYZE_ARGS="" + +# --- Parse arguments ------------------------------------------------------------ +while [[ "$#" -gt 0 ]]; do + case $1 in + -a) ALGS="$2"; shift 2 ;; + -d) DATASETS="$2"; shift 2 ;; + -c) CPUS="$2"; shift 2 ;; + -t) TIME="$2"; shift 2 ;; + -m) MEM="$2"; shift 2 ;; + -p) PARTITION="$2"; shift 2 ;; + -r) RDIR="$2"; shift 2 ;; + -n) DRYRUN=1; shift ;; + --) shift; ANALYZE_ARGS="$@"; break ;; + *) echo "[Error] Unknown option: $1"; exit 1 ;; + esac +done + +if [ -z "$DATASETS" ]; then + echo "[Error] Missing required -d /path/to/pmlb/datasets" + exit 1 +fi +if [ ! -d "$DATASETS" ]; then + echo "[Error] Datasets directory not found: $DATASETS" + exit 1 +fi + +# --- Submit one job per algorithm --------------------------------------------- +IFS=',' read -ra ALG_LIST <<< "$ALGS" +for ALG in "${ALG_LIST[@]}"; do + SIF="$SIF_DIR/srbench-${ALG}.sif" + if [ ! -f "$SIF" ]; then + echo "[Error] Missing image: $SIF" + echo " Pull it first: bash scripts/singularity_pull_all.sh $ALG" + continue + fi + + NOW=$(date +"%Y-%m-%d_%H-%M-%S") + echo "[srbench] Submitting $ALG (cpus=$CPUS, time=$TIME, mem=$MEM, partition=$PARTITION)" + echo "[srbench] image: $SIF" + echo "[srbench] analyze: python analyze.py /pmlb/datasets --local -ml $ALG -n_jobs $CPUS -results $RDIR $ANALYZE_ARGS" + + SBATCH_CMD=( + sbatch + --job-name="srbench-${ALG}" + --output="${LOG_DIRECTORY}/srbench-${ALG}-${NOW}.out" + --error="${LOG_DIRECTORY}/srbench-${ALG}-${NOW}.err" + --nodes=1 + --ntasks=1 + --cpus-per-task="${CPUS}" + --time="${TIME}" + --mem="${MEM}" + --partition="${PARTITION}" + --export=ALL,ALG="${ALG}",SIF="${SIF}",DATASETS="${DATASETS}",CPUS="${CPUS}",RDIR="${RDIR}",ANALYZE_ARGS="${ANALYZE_ARGS}" + scripts/run_srbench.slurm + ) + + if [ "$DRYRUN" -eq 1 ]; then + echo "[dryrun] ${SBATCH_CMD[*]}" + else + "${SBATCH_CMD[@]}" + fi +done diff --git a/scripts/run_srbench.slurm b/scripts/run_srbench.slurm new file mode 100755 index 00000000..bf128e20 --- /dev/null +++ b/scripts/run_srbench.slurm @@ -0,0 +1,46 @@ +#!/bin/bash +# SLURM batch script: run SRBench analyze.py for ONE algorithm inside its +# Apptainer/Singularity container. Submitted by scripts/run_srbench.sh, +# which exports: ALG, SIF, DATASETS, CPUS, RDIR, ANALYZE_ARGS. +# Modeled on run_distributed.slurm. + +# Inform user +echo "[srbench] $USER @ $(hostname) | Algorithm: $ALG" +echo "[srbench] $USER @ $(hostname) | Image: $SIF" +echo "[srbench] $USER @ $(hostname) | Datasets: $DATASETS" +echo "[srbench] $USER @ $(hostname) | CWD: $(pwd)" +echo "[srbench] $USER @ $(hostname) | analyze.py args: $ANALYZE_ARGS" + +# --- Runtime ------------------------------------------------------------------ +if command -v apptainer >/dev/null 2>&1; then + CONTAINER=apptainer +else + CONTAINER=singularity +fi +echo "[srbench] Using runtime: $CONTAINER ($($CONTAINER --version 2>/dev/null))" + +# Keep per-job cache/tmp off $HOME (quota) and on local/scratch disk. +export APPTAINER_CACHEDIR=${APPTAINER_CACHEDIR:-/tmp/$USER/apptainer_cache} +export APPTAINER_TMPDIR=${APPTAINER_TMPDIR:-/tmp/$USER/apptainer_tmp} +export SINGULARITY_CACHEDIR=$APPTAINER_CACHEDIR +export SINGULARITY_TMPDIR=$APPTAINER_TMPDIR +mkdir -p "$APPTAINER_CACHEDIR" "$APPTAINER_TMPDIR" + +# --- Bind mounts ---------------------------------------------------------------- +# experiment/ -> /srbench (the image's WORKDIR; results are written here) +# datasets -> /pmlb/datasets (read-only) +# /pgeoprj,/tatu kept for consistency with your other jobs. +BINDS="$PWD/experiment:/srbench,$DATASETS:/pmlb/datasets:ro,/pgeoprj,/tatu" + +# --- Run ------------------------------------------------------------------------ +# `run` (not `exec`) so the image entrypoint activates the conda base env, +# matching `docker compose run ...`. +$CONTAINER run --pwd /srbench -B "$BINDS" "$SIF" \ + python analyze.py /pmlb/datasets \ + --local \ + -ml "$ALG" \ + -n_jobs "$CPUS" \ + -results "$RDIR" \ + $ANALYZE_ARGS + +echo "[srbench] $USER @ $(hostname) | Done ($ALG). Results in experiment/$RDIR" diff --git a/scripts/singularity_pull_all.sh b/scripts/singularity_pull_all.sh new file mode 100755 index 00000000..36f8a343 --- /dev/null +++ b/scripts/singularity_pull_all.sh @@ -0,0 +1,111 @@ +#!/bin/bash +# Pull prebuilt SRBench algorithm images from Docker Hub as Singularity SIFs. +# +# Run this on a node WITH internet access (e.g. the login node); compute +# nodes usually can't reach Docker Hub. +# +# Usage: +# bash scripts/singularity_pull_all.sh # pull all available images +# bash scripts/singularity_pull_all.sh gplearn feat # pull specific ones +# +# Env overrides: +# OUTPUT_DIR - where .sif files are written +# (default: /tatu/container_images/srbench) +# SINGULARITY_CACHEDIR - docker layer cache (default: /tatu/$USER/singularity_cache) +# SINGULARITY_TMPDIR - temp dir during conversion (default: /tatu/$USER/singularity_tmp) +# +# NOTE: images are large (some >10 GB uncompressed). Do NOT let the cache +# default to $HOME or you may fill your home quota. +set -uo pipefail + +# --- Pick a container runtime ------------------------------------------------- +# "ERROR: pull is only supported for shub URIs" means singularity 2.x, which +# cannot pull from docker://. We need singularity >= 3.x or apptainer. +# Prefer apptainer if installed; otherwise use singularity and check its version. +if command -v apptainer >/dev/null 2>&1; then + SINGULARITY=apptainer +else + SINGULARITY=singularity +fi + +VER=$(command $SINGULARITY --version 2>/dev/null | grep -oE '[0-9]+(\.[0-9]+)+' | head -1) +MAJOR=${VER%%.*} +# if [ -z "${MAJOR}" ] || [ "${MAJOR}" -lt 3 ]; then +# echo "ERROR: '$SINGULARITY' is version '${VER:-unknown}', but >= 3.0 (or apptainer)" +# echo " is required to pull from Docker Hub (docker:// URIs)." +# echo +# echo "Look for a newer installation on the cluster, e.g.:" +# echo " module avail singularity" +# echo " module avail apptainer" +# echo " which -a singularity apptainer" +# echo "then 'module load' it and re-run this script." +# exit 1 +# fi +echo "Using runtime: $SINGULARITY ($VER)" + +# Algorithms published on Docker Hub (srbench org), tag :latest. +# Verified 2026-10-16. The remaining repo algorithms are NOT published: +# brush bsr gp-elite keplearn lightgbm nesymres pantara pir sklearn tpsr udsr xgboost +# (those need a local conda install or a SIF built from a def file). +AVAILABLE=( + afp + bingo + e2et + eplex + eql + feat + ffx + geneticengine + gpgomea + gplearn + gpzgd + itea + operon + ps-tree + pysr + qlattice + rils-rols + tir +) + +# OUTPUT_DIR=${OUTPUT_DIR:-/tatu/container_images/srbench} +# export SINGULARITY_CACHEDIR=${SINGULARITY_CACHEDIR:-/tatu/$USER/singularity_cache} +# export SINGULARITY_TMPDIR=${SINGULARITY_TMPDIR:-/tatu/$USER/singularity_tmp} +OUTPUT_DIR=${OUTPUT_DIR:-/pgeoprj/dxt6/maromba/notebooks/container/output_dir} +export SINGULARITY_CACHEDIR=${SINGULARITY_CACHEDIR:-/pgeoprj/dxt6/maromba/notebooks/container/singularity_cache} +export SINGULARITY_TMPDIR=${SINGULARITY_TMPDIR:-/pgeoprj/dxt6/maromba/notebooks/container/singularity_tmp} +mkdir -p "$OUTPUT_DIR" "$SINGULARITY_CACHEDIR" "$SINGULARITY_TMPDIR" + +if [ "$#" -gt 0 ]; then + ALGS=("$@") +else + ALGS=("${AVAILABLE[@]}") +fi + +FAILED=() +for alg in "${ALGS[@]}"; do + sif="$OUTPUT_DIR/srbench-${alg}.sif" + if [ -f "$sif" ]; then + echo "[skip] $sif already exists" + continue + fi + echo "[pull] docker://srbench/${alg}:latest -> $sif" + if $SINGULARITY pull "$sif" "docker://srbench/${alg}:latest"; then + echo "[ok] $alg" + else + echo "[FAIL] $alg" + FAILED+=("$alg") + fi +done + +echo +if [ "${#FAILED[@]}" -gt 0 ]; then + echo "Finished with ${#FAILED[@]} failure(s): ${FAILED[*]}" +else + echo "All images pulled successfully." +fi +echo "SIF files are in: $OUTPUT_DIR" +echo +echo "Smoke-test one image (run from the srbench repo root):" +echo " singularity run --pwd /srbench -B \$PWD/experiment:/srbench \\" +echo " $OUTPUT_DIR/srbench-gplearn.sif bash test.sh" diff --git a/scripts/srbench-alg.def b/scripts/srbench-alg.def new file mode 100755 index 00000000..7f383c30 --- /dev/null +++ b/scripts/srbench-alg.def @@ -0,0 +1,49 @@ +Bootstrap: docker +From: srbench/base:latest +# Build a SIF for one SRBench algorithm, mirroring alg-Dockerfile. +# +# Build with (needs root, --fakeroot, or --remote): +# APPTAINERENV_ALGORITHM=brush apptainer build --fakeroot --no-mount bind-paths srbench-brush.sif scripts/srbench-alg.def +# or, without root: +# APPTAINERENV_ALGORITHM=brush apptainer build --remote srbench-brush.sif scripts/srbench-alg.def +# +# The --no-mount bind-paths flag disables site-configured bind mounts +# (e.g. /exp, /pgeo) that would otherwise fail because the base image +# doesn't have those mountpoints. +# +# %files paths are relative to the build CONTEXT (the current directory), +# so run the build from the srbench repo root. + +%files + base_environment.yml /install/base_environment.yml + scripts/install_algorithm.sh /install/install_algorithm.sh + algorithms /install/algorithms + +%post + set -e + ALGORITHM="${APPTAINERENV_ALGORITHM:-brush}" + echo "Building for algorithm: ${ALGORITHM}" + cd /install + # alg-Dockerfile relies on MAMBA_DOCKERFILE_ACTIVATE=1 to auto-activate + # the base env in RUN steps; here we activate it explicitly. + eval "$(micromamba shell hook --shell bash)" + micromamba activate base + bash install_algorithm.sh "/install/algorithms/${ALGORITHM}" + micromamba clean --all --yes + # remove all other algorithm sources to keep the image small + find /install/algorithms -mindepth 1 -maxdepth 1 -type d ! -name "${ALGORITHM}" -exec rm -rf {} + + mkdir -p /srbench + +%environment + export LD_LIBRARY_PATH=/opt/conda/lib:${LD_LIBRARY_PATH} + +%runscript + # The base image's Docker entrypoint (micromamba activation) is NOT run + # by apptainer, so activate base here to match `docker compose run`. + eval "$(micromamba shell hook --shell bash)" + micromamba activate base + cd /srbench + exec "$@" + +%labels + Project srbench