p-doom/crowd-code-dataset-0.2 huggingface dataset:
[tab] slurm/jobs/mihir/horeka/coinrun/train_dyn_single_gpu.sh (shellscript)
#!/usr/bin/env bash

#SBATCH --nodes=1
#SBATCH --ntasks-per-node=4
#SBATCH --time=00:20:00
#SBATCH --partition=accelerated
#SBATCH --cpus-per-task=5
#SBATCH --gres=gpu:1
#SBATCH --output=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/logs/logs_mihir/coinrun/dyn/%x_%j.log
#SBATCH --error=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/logs/logs_mihir/coinrun/dyn/%x_%j.log
#SBATCH --job-name=train_dyn_1e-4
#SBATCH --requeue
#SBATCH --signal=b:usr1@300 # 5 min before timeout

# Log the sbatch script
cat $0

module unload mpi/openmpi/5.0
module unload devel/cuda/12.4
source .venv/bin/activate

array_records_dir_train=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/data_coinrun/coinrun_episodes_10m_gt_actions_split/train
array_records_dir_val=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/data_coinrun/coinrun_episodes_10m_gt_actions_split/val

job_name=$SLURM_JOB_NAME
slurm_job_id=$SLURM_JOB_ID

CHECKPOINT_DIR=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/checkpoints/big-runs/dyn/$job_name/$slurm_job_id
mkdir -p $CHECKPOINT_DIR

lam_checkpoint=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/checkpoints/big-runs/lam/interactive/3468835
tokenizer_checkpoint=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/checkpoints/big-runs/tokenizer/interactive/3468835

env | grep SLURM

srun python train_dynamics.py \
    --save_ckpt \
    --image_height=64 \
    --image_width=64 \
    --ckpt_dir $CHECKPOINT_DIR \
    --batch_size=12 \
    --init_lr=0 \
    --max_lr=1e-4 \
    --log_image_interval=10 \
    --log_checkpoint_interval=50 \
    --log \
    --name=coinrun-dyn-dev-$slurm_job_id \
    --tags dyn coinrun dev \
    --entity instant-uv \
    --project jafar \
    --warmup_steps 0 \
    --wsd_decay_steps 0 \
    --num_steps 100 \
    --restore_ckpt \
    --val_data_dir $array_records_dir_val \
    --val_interval 10 \
    --val_steps 10 \
    --wandb_id=$slurm_job_id \
    --data_dir $array_records_dir_train \
    --tokenizer_checkpoint $tokenizer_checkpoint
[tab] slurm/jobs/mihir/horeka/lam/coinrun/coinrun_lam_base.sbatch (shellscript)
#!/usr/bin/env bash

#SBATCH --nodes=1
#SBATCH --ntasks-per-node=1
#SBATCH --time=2-00:00:00
#SBATCH --partition=accelerated
#SBATCH --cpus-per-task=5
#SBATCH --gres=gpu:1
#SBATCH --output=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/logs/logs_mihir/coinrun/lam/%x_%j.log
#SBATCH --error=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/logs/logs_mihir/coinrun/lam/%x_%j.log
#SBATCH --job-name=train_lam_coinrun_37M_actionspace_6
#SBATCH --requeue
#SBATCH --signal=b:usr1@300 # 5 min before timeout

# --- signal trap to requeue job before timeout ---
requeue_job() {
    echo "[$(date)] caught sigusr1 (timeout warning), requeueing slurm job $SLURM_JOB_ID..."
    # optional: trigger checkpoint saving here
    # e.g., touch $checkpoint_dir/requeue_trigger
    scontrol requeue $SLURM_JOB_ID
    exit 0
}

trap requeue_job sigusr1

# set checkpoint flag based on restart count
restart_count=$(scontrol show job $SLURM_JOB_ID | grep -o 'Restarts=[0-9]*' | cut -d'=' -f2)

if [ $restart_count -eq 0 ]; then
    restore_ckpt_flag="--no-restore-ckpt"
else
    restore_ckpt_flag="--restore-ckpt"
fi

# Log the sbatch script
cat $0

module unload mpi/openmpi/5.0
module unload devel/cuda/12.4
source .venv/bin/activate

job_name=$SLURM_JOB_NAME
slurm_job_id=$SLURM_JOB_ID

CHECKPOINT_DIR=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/checkpoints/mihir/coinrun/lam/$job_name/$slurm_job_id
mkdir -p $CHECKPOINT_DIR

array_records_dir=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/data_coinrun/coinrun_episodes_10m_gt_actions_split/train
array_records_dir_val=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/data_coinrun/coinrun_episodes_10m_gt_actions_split/val

env | grep SLURM

srun python train_lam.py \
    --save_ckpt \
    $restore_ckpt_flag \
    --wandb_id $SLURM_JOB_ID \
    --ckpt_dir $CHECKPOINT_DIR \
    --batch_size=24 \
    --image_height=64 \
    --image_width=64 \
    --max_lr=3e-6 \
    --log_image_interval=1000 \
    --log_checkpoint_interval=1000 \
    --log \
    --name="${job_name}_${slurm_job_id}" \
    --tags "coinrun lam_ablation modelsize actionspace" \
    --entity instant-uv \
    --project jafar \
    --lr_schedule wsd \
    --val_data_dir $array_records_dir_val \
    --data_dir $array_records_dir &

child_pid=$!

wait $child_pid
[tab] slurm/jobs/mihir/horeka/lam/coinrun/coinrun_lam_as_16.sbatch (shellscript)
#!/usr/bin/env bash

#SBATCH --nodes=1
#SBATCH --ntasks-per-node=1
#SBATCH --time=2-00:00:00
#SBATCH --partition=accelerated
#SBATCH --cpus-per-task=5
#SBATCH --gres=gpu:1
#SBATCH --output=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/logs/logs_mihir/coinrun/lam/%x_%j.log
#SBATCH --error=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/logs/logs_mihir/coinrun/lam/%x_%j.log
#SBATCH --job-name=train_lam_coinrun_37M_actionspace_16
#SBATCH --requeue
#SBATCH --signal=b:usr1@300 # 5 min before timeout

# --- signal trap to requeue job before timeout ---
requeue_job() {
    echo "[$(date)] caught sigusr1 (timeout warning), requeueing slurm job $SLURM_JOB_ID..."
    # optional: trigger checkpoint saving here
    # e.g., touch $checkpoint_dir/requeue_trigger
    scontrol requeue $SLURM_JOB_ID
    exit 0
}

trap requeue_job sigusr1

# set checkpoint flag based on restart count
restart_count=$(scontrol show job $SLURM_JOB_ID | grep -o 'Restarts=[0-9]*' | cut -d'=' -f2)

if [ $restart_count -eq 0 ]; then
    restore_ckpt_flag="--no-restore-ckpt"
else
    restore_ckpt_flag="--restore-ckpt"
fi

# Log the sbatch script
cat $0

module unload mpi/openmpi/5.0
module unload devel/cuda/12.4
source .venv/bin/activate

job_name=$SLURM_JOB_NAME
slurm_job_id=$SLURM_JOB_ID

CHECKPOINT_DIR=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/checkpoints/mihir/coinrun/lam/$job_name/$slurm_job_id
mkdir -p $CHECKPOINT_DIR

array_records_dir=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/data_coinrun/coinrun_episodes_10m_gt_actions_split/train
array_records_dir_val=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/data_coinrun/coinrun_episodes_10m_gt_actions_split/val

env | grep SLURM

srun python train_lam.py \
    --save_ckpt \
    $restore_ckpt_flag \
    --wandb_id $SLURM_JOB_ID \
    --ckpt_dir $CHECKPOINT_DIR \
    --batch_size=24 \
    --image_height=64 \
    --image_width=64 \
    --max_lr=3e-6 \
    --log_image_interval=1000 \
    --log_checkpoint_interval=1000 \
    --log \
    --num_latents=16 \
    --name="${job_name}_${slurm_job_id}" \
    --tags "coinrun lam_ablation modelsize actionspace" \
    --entity instant-uv \
    --project jafar \
    --lr_schedule wsd \
    --val_data_dir $array_records_dir_val \
    --data_dir $array_records_dir &

child_pid=$!

wait $child_pid
[tab] slurm/jobs/mihir/horeka/lam/coinrun/coinrun_lam_as_128.sbatch (shellscript)
#!/usr/bin/env bash

#SBATCH --nodes=1
#SBATCH --ntasks-per-node=1
#SBATCH --time=2-00:00:00
#SBATCH --partition=accelerated
#SBATCH --cpus-per-task=5
#SBATCH --gres=gpu:1
#SBATCH --output=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/logs/logs_mihir/coinrun/lam/%x_%j.log
#SBATCH --error=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/logs/logs_mihir/coinrun/lam/%x_%j.log
#SBATCH --job-name=train_lam_coinrun_37M_actionspace_128
#SBATCH --requeue
#SBATCH --signal=b:usr1@300 # 5 min before timeout

# --- signal trap to requeue job before timeout ---
requeue_job() {
    echo "[$(date)] caught sigusr1 (timeout warning), requeueing slurm job $SLURM_JOB_ID..."
    # optional: trigger checkpoint saving here
    # e.g., touch $checkpoint_dir/requeue_trigger
    scontrol requeue $SLURM_JOB_ID
    exit 0
}

trap requeue_job sigusr1

# set checkpoint flag based on restart count
restart_count=$(scontrol show job $SLURM_JOB_ID | grep -o 'Restarts=[0-9]*' | cut -d'=' -f2)

if [ $restart_count -eq 0 ]; then
    restore_ckpt_flag="--no-restore-ckpt"
else
    restore_ckpt_flag="--restore-ckpt"
fi

# Log the sbatch script
cat $0

module unload mpi/openmpi/5.0
module unload devel/cuda/12.4
source .venv/bin/activate

job_name=$SLURM_JOB_NAME
slurm_job_id=$SLURM_JOB_ID

CHECKPOINT_DIR=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/checkpoints/mihir/coinrun/lam/$job_name/$slurm_job_id
mkdir -p $CHECKPOINT_DIR

array_records_dir=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/data_coinrun/coinrun_episodes_10m_gt_actions_split/train
array_records_dir_val=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/data_coinrun/coinrun_episodes_10m_gt_actions_split/val

env | grep SLURM

srun python train_lam.py \
    --save_ckpt \
    $restore_ckpt_flag \
    --wandb_id $SLURM_JOB_ID \
    --ckpt_dir $CHECKPOINT_DIR \
    --batch_size=24 \
    --image_height=64 \
    --image_width=64 \
    --max_lr=3e-6 \
    --log_image_interval=1000 \
    --log_checkpoint_interval=1000 \
    --log \
    --num_latents=128 \
    --name="${job_name}_${slurm_job_id}" \
    --tags "coinrun lam_ablation modelsize actionspace" \
    --entity instant-uv \
    --project jafar \
    --lr_schedule wsd \
    --val_data_dir $array_records_dir_val \
    --data_dir $array_records_dir &

child_pid=$!

wait $child_pid
[tab] generate_dataset.py (python)
"""
Generates a dataset of random-action CoinRun episodes.
Episodes are saved individually as memory-mapped files for efficient loading.
"""

from dataclasses import dataclass
from pathlib import Path

from gym3 import types_np
import numpy as np
from procgen import ProcgenGym3Env
import tyro
import pickle
import json
from array_record.python.array_record_module import ArrayRecordWriter 



@dataclass
class Args:
    num_episodes: int = 10000
    output_dir: str = "data/coinrun_episodes"
    min_episode_length: int = 50


args = tyro.cli(Args)
output_dir = Path(args.output_dir)
output_dir.mkdir(parents=True, exist_ok=True)

# --- Generate episodes ---
i = 0
episode_metadata = []
while i < args.num_episodes:
    seed = np.random.randint(0, 10000)
    env = ProcgenGym3Env(num=1, env_name="coinrun", start_level=seed)
    observations_seq = []

    # --- Run episode ---
    for j in range(1000):
        env.act(types_np.sample(env.ac_space, bshape=(env.num,)))
        rew, obs, first = env.observe()
        observations_seq.append(obs["rgb"])
        if first:
            break

    # --- Save episode ---
    if len(observations_seq) >= args.min_episode_length:
        observations_data = np.concatenate(observations_seq, axis=0).astype(np.uint8)
        episode_path = output_dir / f"episode_{i}.array_record"  

        # --- Save as ArrayRecord ---
        writer = ArrayRecordWriter(str(episode_path), "group_size:1")
        record = {"raw_video": observations_data.tobytes(), "sequence_length": len(observations_seq)}
        writer.write(pickle.dumps(record))
        writer.close()

        episode_metadata.append({"path": str(episode_path), "length": len(observations_seq)})
        print(f"Episode {i} completed, length: {len(observations_seq)}")
        i += 1
    else:
        print(f"Episode too short ({len(observations_seq)}), resampling...")

# --- Save metadata ---
metadata = {
    "env": "coinrun",
    "num_episodes": args.num_episodes,
    "avg_episode_len": np.mean([ep["length"] for ep in episode_metadata]),
    "episode_metadata": episode_metadata,
}
with open(output_dir / "metadata.json", "w") as f:
    json.dump(metadata, f)

print(f"Dataset generated with {len(episode_metadata)} valid episodes")
[content] input_pipeline/preprocess/video_to_array_records.py (python)
def _reprocess_single_arrayrecord(
    arrayrecord_file: Path,
    output_folder: str,
    chunk_size: int,
    videos_per_file: int,
    image_h: int,
    image_w: int,
    image_c: int,
    file_index: int,
) -> list[str]:
    """
    Reprocess a single ArrayRecord file by splitting videos into chunks.

    Args:
        arrayrecord_file: Path to the ArrayRecord file
        output_folder: Output folder for the chunked files
        chunk_size: Number of frames per video chunk
        videos_per_file: Number of video chunks per output file
        image_h: Image height in pixels
        image_w: Image width in pixels
        image_c: Number of image channels
        file_index: Index for naming output files

    Returns:
        List of paths to created ArrayRecord files
    """
    print(f"Processing {arrayrecord_file.name}...")
    reader = ArrayRecordReader(str(arrayrecord_file))

    all_record_bytes = reader.read_all()
    file_chunks = []

    for record_bytes in all_record_bytes:
        record = pickle.loads(record_bytes)
        video_data = record["raw_video"]
        sequence_length = record["sequence_length"]

        video_tensor = np.frombuffer(video_data, dtype=np.uint8)

        assert (
            video_tensor.shape[0] == sequence_length * image_h * image_w * image_c
        ), f"Video tensor shape {video_tensor.shape} does not match expected shape {sequence_length * image_h * image_w * image_c}"
        video_tensor = video_tensor.reshape(sequence_length, image_h, image_w, image_c)

        current_episode_len = video_tensor.shape[0]
        if current_episode_len < chunk_size:
            print(
                f"Warning: Video has {current_episode_len} frames, skipping (need {chunk_size})"
            )
            continue

        for start_idx in range(0, current_episode_len - chunk_size + 1, chunk_size):
            chunk = video_tensor[start_idx : start_idx + chunk_size]

            # FIXME: currently no way of correlating the chunk with the original video
            chunk_record = {
                "raw_video": chunk.tobytes(),
                "sequence_length": chunk_size,
            }

            file_chunks.append(chunk_record)

    reader.close()

    # Write chunks to output files
    output_files = []
    for i in range(0, len(file_chunks), videos_per_file):
        batch_chunks = file_chunks[i : i + videos_per_file]
        output_filename = (
            f"chunked_videos_{file_index:04d}_{i//videos_per_file:04d}.array_record"
        )
        output_file = os.path.join(output_folder, output_filename)

        writer = ArrayRecordWriter(output_file, "group_size:1")
        for chunk in batch_chunks:
            writer.write(pickle.dumps(chunk))
        writer.close()

        output_files.append(output_file)
        print(f"Created {output_filename} with {len(batch_chunks)} video chunks")

    print(
        f"Processed {arrayrecord_file.name}: {len(file_chunks)} chunks -> {len(output_files)} files"
    )
    return output_files
[terminal_command] TERMINAL ()
python input_pipeline/preprocess/video_to_array_records.py --input_path /hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/data_new/tmp/vid \
--output_path /hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/data_new/tmp/array_record --env_name minecraft
[terminal_command] TERMINAL ()
python input_pipeline/preprocess/video_to_array_records.py --input_path /hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/data_new/tmp/vid --output_path /hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/data_new/tmp/array_record --env_name minecraft
[tab] slurm/jobs/mihir/horeka/preprocessing/preprocess_atari.sbatch (shellscript)
#!/usr/bin/env bash

#SBATCH --nodes=1
#SBATCH --ntasks-per-node=1
#SBATCH --time=48:00:00
#SBATCH --partition=cpuonly
#SBATCH --cpus-per-task=16
#SBATCH --output=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/logs/logs_mihir/preprocess/atari/%x_%j.log
#SBATCH --error=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/logs/logs_mihir/preprocess/atari/%x_%j.log
#SBATCH --job-name=preprocess_atari

source .venv/bin/activate

python input_pipeline/preprocess/pngs_to_array_records.py \
    --input_path=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/data_atari/per-game/atari_v1/screens \
    --output_path=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/data_atari/array_records
[content] slurm/jobs/mihir/horeka/preprocessing/preprocess_minecraft_single_chunk.sbatch (plaintext)
#!/usr/bin/env bash

#SBATCH --nodes=1
#SBATCH --ntasks-per-node=1
#SBATCH --time=48:00:00
#SBATCH --partition=cpuonly
#SBATCH --cpus-per-task=16
#SBATCH --output=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/logs/logs_mihir/preprocess/atari/%x_%j.log
#SBATCH --error=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/logs/logs_mihir/preprocess/atari/%x_%j.log
#SBATCH --job-name=preprocess_atari
[tab] slurm/jobs/mihir/horeka/preprocessing/preprocess_minecraft_single_chunk copy.sbatch (shellscript)
#!/usr/bin/env bash

#SBATCH --nodes=1
#SBATCH --ntasks-per-node=1
#SBATCH --time=48:00:00
#SBATCH --partition=cpuonly
#SBATCH --cpus-per-task=16
#SBATCH --output=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/logs/logs_mihir/preprocess/minecraft/%x_%j.log
#SBATCH --error=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/logs/logs_mihir/preprocess/minecraft/%x_%j.log
#SBATCH --job-name=preprocess_minecraft_single_chunk

source .venv/bin/activate

python input_pipeline/preprocess/video_to_array_records.py \
        --input_path /hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/data_new/open_ai_minecraft
        --output_path /hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/data_new/tmp/single_chunk
        --env_name minecraft
        --chunk_size 1600
        --chunks_per_file 1
[tab] slurm/jobs/mihir/horeka/preprocessing/preprocess_minecraft_multi_chunk.sbatch (shellscript)
#!/usr/bin/env bash

#SBATCH --nodes=1
#SBATCH --ntasks-per-node=1
#SBATCH --time=48:00:00
#SBATCH --partition=cpuonly
#SBATCH --cpus-per-task=16
#SBATCH --output=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/logs/logs_mihir/preprocess/minecraft/%x_%j.log
#SBATCH --error=/hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/logs/logs_mihir/preprocess/minecraft/%x_%j.log
#SBATCH --job-name=preprocess_minecraft_single_chunk

source .venv/bin/activate

python input_pipeline/preprocess/video_to_array_records.py \
        --input_path /hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/data_new/open_ai_minecraft
        --output_path /hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/data_new/tmp/single_chunk
        --env_name minecraft
        --chunk_size 1600
        --chunks_per_file 1
[tab] generate_dataset copy.py (python)
"""
Generates a dataset of random-action CoinRun episodes.
Episodes are saved individually as memory-mapped files for efficient loading.
"""

from dataclasses import dataclass
from pathlib import Path

from gym3 import types_np
import numpy as np
from procgen import ProcgenGym3Env
import tyro
import pickle
import json
from array_record.python.array_record_module import ArrayRecordWriter 



@dataclass
class Args:
    num_episodes: int = 10000
    output_dir: str = "data/coinrun_episodes"
    min_episode_length: int = 50
    max_episode_length: int = 1000


args = tyro.cli(Args)
output_dir = Path(args.output_dir)
output_dir.mkdir(parents=True, exist_ok=True)

# --- Generate episodes ---
i = 0
episode_metadata = []
while i < args.num_episodes:
    seed = np.random.randint(0, 10000)
    env = ProcgenGym3Env(num=1, env_name="coinrun", start_level=seed)
    observations_seq = []

    # --- Run episode ---
    for j in range(args.max_episode_length):
        env.act(types_np.sample(env.ac_space, bshape=(env.num,)))
        rew, obs, first = env.observe()
        observations_seq.append(obs["rgb"])
        if first:
            break

    # --- Save episode ---
    if len(observations_seq) >= args.min_episode_length:
        observations_data = np.concatenate(observations_seq, axis=0).astype(np.uint8)
        episode_path = output_dir / f"episode_{i}.array_record"  

        # --- Save as ArrayRecord ---
        writer = ArrayRecordWriter(str(episode_path), "group_size:1")
        record = {"raw_video": observations_data.tobytes(), "sequence_length": len(observations_seq)}
        writer.write(pickle.dumps(record))
        writer.close()

        episode_metadata.append({"path": str(episode_path), "length": len(observations_seq)})
        print(f"Episode {i} completed, length: {len(observations_seq)}")
        i += 1
    else:
        print(f"Episode too short ({len(observations_seq)}), resampling...")

# --- Save metadata ---
metadata = {
    "env": "coinrun",
    "num_episodes": args.num_episodes,
    "avg_episode_len": np.mean([ep["length"] for ep in episode_metadata]),
    "episode_metadata": episode_metadata,
}
with open(output_dir / "metadata.json", "w") as f:
    json.dump(metadata, f)

print(f"Dataset generated with {len(episode_metadata)} valid episodes")
[tab] generate_dataset_chunk.py (python)
"""
Generates a dataset of random-action CoinRun episodes.
Episodes are saved individually as memory-mapped files for efficient loading.
"""

from dataclasses import dataclass
from pathlib import Path

from gym3 import types_np
import numpy as np
from procgen import ProcgenGym3Env
import tyro
import pickle
import json
from array_record.python.array_record_module import ArrayRecordWriter 



@dataclass
class Args:
    num_episodes: int = 10000
    output_dir: str = "data/coinrun_episodes"
    min_episode_length: int = 50
    max_episode_length: int = 1000


args = tyro.cli(Args)
output_dir = Path(args.output_dir)
output_dir.mkdir(parents=True, exist_ok=True)

# --- Generate episodes ---
i = 0
episode_metadata = []
while i < args.num_episodes:
    seed = np.random.randint(0, 10000)
    env = ProcgenGym3Env(num=1, env_name="coinrun", start_level=seed)
    observations_seq = []

    # --- Run episode ---
    for j in range(args.max_episode_length):
        env.act(types_np.sample(env.ac_space, bshape=(env.num,)))
        rew, obs, first = env.observe()
        observations_seq.append(obs["rgb"])
        if first:
            break

    # --- Save episode ---
    if len(observations_seq) >= args.min_episode_length:
        observations_data = np.concatenate(observations_seq, axis=0).astype(np.uint8)
        episode_path = output_dir / f"episode_{i}.array_record"  

        # --- Save as ArrayRecord ---
        writer = ArrayRecordWriter(str(episode_path), "group_size:1")
        record = {"raw_video": observations_data.tobytes(), "sequence_length": len(observations_seq)}
        writer.write(pickle.dumps(record))
        writer.close()

        episode_metadata.append({"path": str(episode_path), "length": len(observations_seq)})
        print(f"Episode {i} completed, length: {len(observations_seq)}")
        i += 1
    else:
        print(f"Episode too short ({len(observations_seq)}), resampling...")

# --- Save metadata ---
metadata = {
    "env": "coinrun",
    "num_episodes": args.num_episodes,
    "avg_episode_len": np.mean([ep["length"] for ep in episode_metadata]),
    "episode_metadata": episode_metadata,
}
with open(output_dir / "metadata.json", "w") as f:
    json.dump(metadata, f)

print(f"Dataset generated with {len(episode_metadata)} valid episodes")
[content] generate_dataset_chunk.py (python)
    for start_idx in range(0, current_episode_len - chunk_size + 1, chunk_size):
        chunk = video_tensor[start_idx : start_idx + chunk_size]

        chunk_record = {
            "raw_video": chunk.tobytes(),
            "sequence_length": chunk_size,
            "video_file_name": video_file_name,
        }

        file_chunks.append(chunk_record)
[content] generate_dataset_chunk.py (python)
   output_files = []
    for i in range(0, len(file_chunks), chunks_per_file):
        batch_chunks = file_chunks[i : i + chunks_per_file]
        output_filename = (
            f"chunked_videos_{file_index:04d}_{i//chunks_per_file:04d}.array_record"
        )
        output_file = os.path.join(output_folder, output_filename)

        writer = ArrayRecordWriter(output_file, "group_size:1")
        for chunk in batch_chunks:
            writer.write(pickle.dumps(chunk))
        writer.close()

        output_files.append({"path": output_file, "length": chunk_size, "video_file_name": video_file_name})
        print(f"Created {output_filename} with {len(batch_chunks)} video chunks")
[tab] /hkfs/work/workspace/scratch/tum_ind3695-jafa_ws_shared/logs/logs_mihir/preprocess/minecraft/preprocess_minecraft_single_chunk_3475976.log (log)
╭─ Required options ──────────────────────────────────────────────╮
│ The following arguments are required: --output-path, --env-name │
│ ─────────────────────────────────────────────────────────────── │
│ Argument helptext:                                              │
│     --output-path STR                                           │
│         (required)                                              │
│     --env-name STR                                              │
│         (required)                                              │
│ ─────────────────────────────────────────────────────────────── │
│ For full helptext, run video_to_array_records.py --help         │
╰─────────────────────────────────────────────────────────────────╯
/var/spool/slurmd/job3475976/slurm_script: line 16: --output_path: command not found
/var/spool/slurmd/job3475976/slurm_script: line 17: --env_name: command not found
/var/spool/slurmd/job3475976/slurm_script: line 18: --chunk_size: command not found
/var/spool/slurmd/job3475976/slurm_script: line 19: --chunks_per_file: command not found

============================= JOB FEEDBACK =============================

Job ID: 3475976
Cluster: hk
User/Group: tum_cte0515/hk-project-p0023960
Account: hk-project-p0023960
State: FAILED (exit code 127)
Partition: cpuonly
Nodes: 1
Cores per node: 152
Nodelist: hkn1164
CPU Utilized: 00:00:01
CPU Efficiency: 0.02% of 01:23:36 core-walltime
Job Wall-clock time: 00:00:33
Starttime: Mon Sep  8 12:02:23 2025
Endtime: Mon Sep  8 12:02:56 2025
Memory Utilized: 10.37 MB
Memory Efficiency: 0.00% of 233.79 GB
Energy Consumed: 571 Joule / 0.158611111111111 Watthours
Average node power draw: 17.3030303030303 Watt