From 274ef7bcf924a0aa31087947e64ce221a4026576 Mon Sep 17 00:00:00 2001 From: KHUDIYEV ALI <ali.khudiyev@etu.unistra.fr> Date: Mon, 31 Mar 2025 16:53:17 +0200 Subject: [PATCH] stand-alone sh files + example groff man --- README.md | 3 + init.sh | 22 +++++++ loit.sh | 16 ++++++ lore.sh | 19 ++++++ new.sh | 14 +++++ tree.sh | 46 +++++++++++++++ utils.sh | 36 ++++++++++++ xper.1 | 97 +++++++++++++++++++++++++++++++ xper.sh | 168 ++++++++++++++++-------------------------------------- 9 files changed, 302 insertions(+), 119 deletions(-) create mode 100755 init.sh create mode 100755 loit.sh create mode 100755 lore.sh create mode 100755 new.sh create mode 100755 tree.sh create mode 100755 utils.sh create mode 100644 xper.1 diff --git a/README.md b/README.md index f44b2d8..0c212ec 100644 --- a/README.md +++ b/README.md @@ -65,3 +65,6 @@ Git also introduces strong overhead for what the xper tries to do. With **xper** need to switch between different branches to rerun other versions of the experiments or just remind yourself how stupid you are. Everything is directory-based and you can switch between different experiments as easily as switching between different directories. + +## TO DO + diff --git a/init.sh b/init.sh new file mode 100755 index 0000000..f7726bb --- /dev/null +++ b/init.sh @@ -0,0 +1,22 @@ +#!/bin/zsh + +init(){ + export XPER_ROOT=$(pwd) + mkdir 001 2>/dev/null + mkdir .xper 2>/dev/null + + if [ $? -eq 1 ]; then + printf "already initialized ;)\n" + fi + printf "cd into the 001/\n" + + TAG="main" + if [ $# -gt 0 ]; then + TAG=$1 + fi + printf "TAG=$TAG\n" >> .meta +}; + +if [ $# -eq 0 ] || [ $1 != "--import" ]; then + init $@ +fi diff --git a/loit.sh b/loit.sh new file mode 100755 index 0000000..4ea6ec3 --- /dev/null +++ b/loit.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +loit(){ + mkdir 001 2>/dev/null + if [ $? -eq 1 ]; then + printf "extension already exists; cd to 001/\n" + else + mv $(find . -d 1 -not -iname "001") 001/ 2>/dev/null + printf "extension created at 001/\n" + cd 001 && xper.sh exp + fi +}; + +if [ $# -eq 0 ] || [ $1 != "--import" ]; then + new $@ +fi diff --git a/lore.sh b/lore.sh new file mode 100755 index 0000000..46c724a --- /dev/null +++ b/lore.sh @@ -0,0 +1,19 @@ +#!/bin/zsh + +. utils.sh --import + +lore(){ + if [ $(pwd) = $XPER_ROOT ]; then + printf "cannot expand from the root folder\n" + else + maxnum=$(get_next_max_num) + # echo $maxnum + mkdir ../$maxnum + cp -r ./ ../$maxnum/ 2>/dev/null + printf "copied_from=$(pwd)\n" > ../$maxnum/.meta + fi +}; + +if [ $# -eq 0 ] || [ $1 != "--import" ]; then + lore $@ +fi diff --git a/new.sh b/new.sh new file mode 100755 index 0000000..8f9a97d --- /dev/null +++ b/new.sh @@ -0,0 +1,14 @@ +#!/bin/zsh + +. utils.sh --import + +new(){ + cd 001 2>/dev/null + dir=$(get_next_max_num) + cd .. && mkdir $dir && cd $dir + printf "copied_from=-1\n" >> .meta +}; + +if [ $# -eq 0 ] || [ $1 != "--import" ]; then + new $@ +fi diff --git a/tree.sh b/tree.sh new file mode 100755 index 0000000..42a0db2 --- /dev/null +++ b/tree.sh @@ -0,0 +1,46 @@ +#!/bin/zsh + +print_tree_from(){ + local dir=$1 + local depth=$2 + local folders=($(find $dir -type d -d 1)) + folders=($(echo $folders | xargs -n1 | sort | xargs)) + # echo sorted folders are... $folders + if [ ${#folders} -eq 0 ] + then + return 0 + fi + + for folder in $folders + do + local dirname=$(echo $folder | sed -E "s/.*(\/[0-9][0-9][0-9])/\1/g") + # echo dirname=$dirname + if [[ $dirname =~ "[0-9][0-9][0-9]$" ]] + then + local spaces=0 + while [ $spaces -lt $depth ] + do + printf " " + spaces=$((spaces+1)) + done + printf "$dirname\n" + print_tree_from $folder $((depth+1)) + fi + done +}; + +tree(){ + if [ $# -eq 0 ]; then + echo print all tree + cd $XPER_ROOT && ./xper.sh tree . + else + echo print tree from $1 + cd $1 + print_tree_from . 1 + printf "\n" + fi +} + +if [ $# -eq 0 ] || [ $1 != "--import" ]; then + tree $@ +fi diff --git a/utils.sh b/utils.sh new file mode 100755 index 0000000..9b6b61b --- /dev/null +++ b/utils.sh @@ -0,0 +1,36 @@ +#!/bin/zsh + +get_next_max_num(){ + local folders=($(find .. -type d -d 1)) + local maxnum=0 + # echo folders $folders + + for folder in $folders + do + local num=$(expr $(echo $folder | sed -E "s/.*([0-9][0-9][0-9]).*/\1/g")) + # echo curr_$num + case "$num" in + "" | *[!0-9]*) ;; + *) + if [ $num -gt $maxnum ] + then + maxnum=$num + fi + ;; + esac + # if [[ $num =~ "[0-9]+" ]] && [ $num -gt $maxnum ] + # then + # maxnum=$num + # # echo updated to maxnum=$maxnum + # fi + done + + maxnum="$((maxnum+1))" + local numlen=${#maxnum} + while [ $numlen -lt 3 ] + do + maxnum=0$maxnum + numlen=$((numlen+1)) + done + echo "$maxnum" +}; diff --git a/xper.1 b/xper.1 new file mode 100644 index 0000000..b7be8f5 --- /dev/null +++ b/xper.1 @@ -0,0 +1,97 @@ +.\" Man page for xper +.TH XPER 1 "March 30, 2025" "1.0" "xper Manual" +.SH NAME +xper \- experiment version control tool for managing ML experiments +.SH SYNOPSIS +.B xper +.I command +.RI [ options ...] +.SH DESCRIPTION +.B xper +is a tool designed for researchers, particularly in machine learning, to manage experiment directories in a hierarchical tree structure. Unlike traditional version control systems like +.BR git (1), +.B xper +organizes experiments using directories and subdirectories, distinguishing between major changes (siblings) and minor tweaks (children). It provides commands to initialize, create, run, and search experiments, with results stored in a structured way. +.SH COMMANDS +.TP +.B init +Initialize an experiment directory with a default "root" tag. Creates a +.I .xper +subdirectory with metadata. +.TP +.BI "ment -t " tag +Create an empty experiment subdirectory under the directory with the specified +.I tag. +.TP +.BI "lore -t " tag +Create a subdirectory under the directory with +.I tag +and copy the current directory's contents into it. +.TP +.BI "loit -t " tag +Create two child subdirectories in the current directory and store +.I tag +in a hidden file. +.TP +.B tree +Display the experiment tree, showing tagged directories and their hierarchy. +.TP +.BI "run -e " entry +Run an experiment using the specified +.I entry +file, store results in a +.I result +subdirectory, and set the directory to read-only. +.TP +.B origin +Navigate to the first non-empty experiment subdirectory in the current directory. +.TP +.BI "search -t " tag1 " -t " tag2 +Search for experiments or results matching the specified tags. +.SH OPTIONS +.TP +.BI -t " tag" +Specify a tag to identify an experiment directory (used by +.BR ment ", " lore ", " loit ", and " search ). +.TP +.BI -e " entry" +Specify the entry file (e.g., a script) to execute (used by +.BR run ). +.SH EXAMPLES +.TP +Initialize a new experiment directory: +.B xper init +.TP +Create a new empty experiment tagged "model1": +.B xper ment -t model1 +.TP +Run an experiment with a Python script: +.B xper run -e train.py +.TP +View the experiment tree: +.B xper tree +.TP +Search for experiments with tags "model1" and "datasetA": +.B xper search -t model1 -t datasetA +.SH FILES +.TP +.I .xper/ +Directory containing experiment metadata, created by +.BR init . +.TP +.I .xper/root.json +JSON file storing tags and IDs for each experiment directory. +.TP +.I result/ +Subdirectory storing experiment outputs, created by +.BR run . +.SH SEE ALSO +.BR git (1), +.BR mkdir (1), +.BR chmod (1) +.SH AUTHOR +Written by [Your Name]. +.SH BUGS +The +.B origin +command's definition of "non-empty" is not yet standardized. Report bugs to [your contact]. diff --git a/xper.sh b/xper.sh index cd3d1c9..d42dc01 100755 --- a/xper.sh +++ b/xper.sh @@ -1,131 +1,61 @@ #!/bin/zsh -get_next_max_num(){ - local folders=($(find .. -type d -d 1)) - local maxnum=0 - # echo folders $folders +if [ $# -eq 0 ]; then + printf "usage: xper [init|new|lore|loit|origin] +\tinit - to initialize the experiment directory (run first) +\tnew - to create an empty experiment subdirectory +\tlore - to run major modification of the current experiment in a sibling subdirectory +\tloit - to run minor modification of the current experiment in a child subdirectory +\torigin - to go to the leaf subdirectory */001 of the current experiment directory - for folder in $folders - do - local num=$(expr $(echo $folder | sed -E "s/.*([0-9][0-9][0-9]).*/\1/g")) - # echo curr_$num - case "$num" in - "" | *[!0-9]*) ;; - *) - if [ $num -gt $maxnum ] - then - maxnum=$num - fi - ;; - esac - # if [[ $num =~ "[0-9]+" ]] && [ $num -gt $maxnum ] - # then - # maxnum=$num - # # echo updated to maxnum=$maxnum - # fi - done +example 1: expr new -m 'from MLP-AE to CNN-AE' +example 2: expr ext -m '5x more parametrized AE' - maxnum="$((maxnum+1))" - local numlen=${#maxnum} - while [ $numlen -lt 3 ] - do - maxnum=0$maxnum - numlen=$((numlen+1)) - done - echo "$maxnum" -}; +usage: xper tree [|<path>] +usage: xper new --at-depth <d> +\t --at-depth <d> - to run major modification of the current expriment in a sibling subdirectory created at depth absolute <d> or relative depth <-d> or <+d> +\n" + exit +fi -print_tree_from(){ - local dir=$1 - local depth=$2 - local folders=($(find $dir -type d -d 1)) - folders=($(echo $folders | xargs -n1 | sort | xargs)) - # echo sorted folders are... $folders - if [ ${#folders} -eq 0 ] - then - return 0 - fi +. init.sh --import +. new.sh --import +. lore.sh --import +. loit.sh --import +. tree.sh --import - for folder in $folders - do - local dirname=$(echo $folder | sed -E "s/.*(\/[0-9][0-9][0-9])/\1/g") - # echo dirname=$dirname - if [[ $dirname =~ "[0-9][0-9][0-9]$" ]] - then - local spaces=0 - while [ $spaces -lt $depth ] - do - printf " " - spaces=$((spaces+1)) - done - printf "$dirname\n" - print_tree_from $folder $((depth+1)) - fi - done -}; +CMD=$1 +shift -main(){ - if [ $# -eq 0 ] - then - printf "usage: xper [init|new|ext|origin] -\tinit - to initialize the experiment directory in the experiments folder (run first) -\tnew - to run major modification of the current experiment in a sibling subdirectory -\text - to run minor modification of the current experiment in a child subdirectory -\torigin - to go to the leaf subdirectory */001 of the current experiment directory +ARGS=() +TAG="" - example 1: expr new -m 'from MLP-AE to CNN-AE' - example 2: expr ext -m '5x more parametrized AE' +while [[ $# -gt 0 ]]; do + case $1 in + -t|--tag) + TAG="$2" + shift + shift + ;; + *) + ARGS+=("$1") + shift + ;; + esac +done -usage: xper tree [|<path>] -usage: xper new --at-depth <d> -\t --at-depth <d> - to run major modification of the current expriment in a sibling subdirectory created at depth absolute <d> or relative depth <-d> or <+d> -\n" - elif [ $1 = "init" ] - then - export XPER_ROOT=$(pwd) - mkdir 001 2>/dev/null - mkdir .xper 2>/dev/null - if [ $? -eq 1 ] - then - printf "already initialized ;)\n" - fi - printf "cd into the 001/\n" - elif [ $1 = "new" ] || [ $1 = "exp" ] - then - if [ $(pwd) = $XPER_ROOT ] - then - printf "cannot expand from the root folder\n" - else - maxnum=$(get_next_max_num) - # echo $maxnum - mkdir ../$maxnum - cp -r ./ ../$maxnum/ 2>/dev/null - fi - elif [ $1 = "ext" ] - then - mkdir 001 2>/dev/null - if [ $? -eq 1 ] - then - printf "extension already exists; cd to 001/\n" - else - mv $(find . -d 1 -not -iname "001") 001/ 2>/dev/null - printf "extension created at 001/\n" - cd 001 && xper.sh exp - fi - elif [ $1 = "tree" ] - then - if [ $# -eq 1 ] - then - echo print all tree - cd $XPER_ROOT && ./xper.sh $1 . - else - echo print tree from $2 - cd $2 - print_tree_from . 1 - printf "\n" - fi - elif [ $1 = "search" ] - then +main(){ + if [ $CMD = "init" ]; then + init $TAG + elif [ $CMD = "new" ]; then + new ${ARGS:2} + elif [ $CMD = "lore" ]; then + lore ${ARGS:2} + elif [ $CMD = "loit" ]; then + loit ${ARGS:2} + elif [ $CMD = "tree" ]; then + tree ${ARGS:2} + elif [ $CMD = "search" ]; then echo search else echo hi there -- GitLab