Skip to content

toast-uz/ahc_tools

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

49 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AtCoder Heuristic Contenst Tools

AtCoderヒューリスティックコンテストの、ローカルテストをサポートするツールです。

特徴

前作 【競プロ】テスト10倍速!AtCoder AHC向け【Python】自動テスト並列処理ツール の以下の特徴を継承しています。

  • ローカルテストための初期環境整備(steup.pyとして独立)
  • テスト対象プログラムの更新を検知して自動再コンパイル
  • 簡単なコマンドラインオプションによる動作制御(条件によっては、一部、ソース修正が必要)
  • 大量のテストを素早く完了できる並列処理

今作では、さらに、以下の特徴を追加しています。

  • 初期環境整備をsetup.pyとして別プログラム化し、本体をシンプルに
  • 初期環境整備で.gitignorerust-toolchainも作成
  • インタラクティブ型、非インタラクティブ型を自動切り替え
  • コンパイル後にダミー実行するようにして、コンパイル直後の実行が遅いことを防止
  • Pythonの並列処理ライブラリrayを利用することで並列処理を安定化
  • エラーやTLEでテスト対象プログラムが落ちた時、プロセスが暴走することがあったバグを解消
  • テストケースの特徴量によるスコア差がわかりやすくなるように、特徴量をスコアとあわせて表示
  • 相対スコアゲーに効果ある $Σ\log(1+score)$ 評価を追加
  • Optunaに対応、ストレージ、enqueue_trial、枝刈りにも対応
  • https://img.atcoder.jp/ahc_standings/index.html (以下、ahc_standingsと呼びます)のローカル実行に対応

使い方

1. 準備

適切なPython3.11Rust1.70.0の実行環境を、準備しておきます。

ディレクトリ構造は、cargo_cometeで作成されるものを前提としており、以下に例示します。この通りでなくても、ソースを修正することで対応可能です。

コンテストルートに、setup.pyeval.pyをコピーするとともに、ローカルテストツールをダウンロード、解凍して配置してください。以降はすべてコンテストルートで作業します。

ParentDir
├── ahc028
├── ahc029 (コンテストルート(例))
│   ├── setup.py (コピーする)
│   ├── eval.py  (コピーする)
│   ├── src
│   │   └── bin
│   │       └── a.rs (Rustの提出ソース)
│   └── tools (ローカルテストツール)
│       ├── src
│       │   └── テストツールのソース群
│       └── in
│           └── テストケース入力ファイル群
└── target
    ├── debug
    └── release
        └── Rustの実行ファイル群 

2. setup.pyの実行

python setup.py を実行します。

  • 以下のファイル・ディレクトリを作成してくれます。
./.gitignore
./rust-toolchain
tools/out
  • 自動でahc_standingsから index.htmlをダウンロードしてtools/outに配置してくれます。

  • 運営から提供されたローカルテストツールを、自動でコンパイルしてくれます。

3. 提出プログラムの作成

提出プログラムを作成します。(これは解説できませんww)

4. eval.pyの修正

eval.pyのソース冒頭にある以下を、条件にあわせて変更します。なお、FEATURESは、変更しなくても一部表示が正しくなくなるだけで、動作はします。しかしながら、ahc_standingsを使う上でも必要ですので、ちゃんと変更しましょう。

# 条件にあわせて以下のみ変更する
LANGUAGE = 'Rust'  # 'Python' or 'Rust'
FEATURES = ['N', 'M', 'K']  # 特徴量

5. eval.pyを利用したAHCの最適化例

代表的な最適化例を記載します。オプションパラメータの詳細は--helpオプションで出るヘルプを参照ください。

最適化例A. 提出プログラムの動作確認

以下にて、単一のテストを実行してデバッグ出力を確認することができます。

python eval.py -s 0 -v

提出プログラムがバグ無く実行完了して、予定した動作ができているか、実行時間は適切か、など細かく確認したい場合に使います。予め、提出プログラムに、デバッグ出力を入れておきます。

なお、以下で複数テストケースを実行し、得点や実行時間の変化を見ることで、さまざまなテストケースにおいて、期待通りの動作をしているかを、確認することが可能です。

python eval.py -s 0 49

動作が変なテストケースにしぼって、単一テストでデバッグ付きの実行をすることで、詳細確認します。

最適化例B. 提出プログラムの処理変更時の効果確認

処理前と処理後で、以下にて、テスト0-49を実行します(テストケースは場合によって増やします)。

python eval.py -s 0 49

スコア合計や、logスコア合計について、プログラム処理変更前後の差異を確認し、プログラムの改善度合いを確認します。

さらに、ahc_standingsによって、ダッシュボードを使って、試行全体の分析をすることが可能です。以下のコマンドでahc_standingsをローカルサーバとして起動できます。

python -m http.server --directory tools/out 8000

起動後に、以下のURLで確認が可能です。contes=0_49は実行したケースにあわせて修正します。

http://127.0.0.1:8000/index.html?contest=0_49

最適化例C. ハイパーパラメータの最適化

以下にて、テスト0-999の実行を1試行として、Optunaで1000回試行してハイパーパラメータを最適化することができます。

事前に、ソースの以下の箇所を修正して、Optunaで最適化するハイパーパラメータをセットします。ハイパーパラメータは環境変数として実行プログラムに流し込まれますので、予め、実行プログラム側で環境変数を読み取って、ハイパーパラメータをセットするように作り込んでおく必要があります。

# 環境変数で流し込むパラメータ
# int: suugest_intの係数、float: suggest_floatの係数
# enque: enque_trialの値(複数あれば複数回実行)
PARAMS = {
    'AHC_PARAMS_SAMPLE1': {'int': [0, 1000], 'enque': [500]},
    'AHC_PARAMS_SAMPLE2': {'float': [0.0, 1.0], 'enque': [0.5]},
}
  • 'int'キーの後ろの値は、sugggest_intのパラメータになります。最初の2つが最小値と最大値です。
  • 'float'キーの後ろの値は、sugggest_floatのパラメータになります。最初の2つが最小値と最大値です。
  • 'enque'キーの後ろの値は、enque_trialのパラメータになります。複数ある場合は、enque_trialが複数回実行されます。

enque_trialとは、Optunaの試行において、固定値をセットするメソッドです。過去の最適値をセットすることで、新たな試行において最適値の周辺の探索機会が増え、さらなる最適値を見つけやすくなります。

実行は以下にようにして、-oオプションにOptunaの試行回数を設定します。

python eval.py -s 0 999 -o 1000

実行にあたっては、自動的に枝刈り(prune)を行い、無駄な探索はしないように設定しています。

pruneとは、大量のテストケースセットで試行する場合に、試行途中で非常に悪い成績のものは途中で試行を中止することで、より短時間に効率的な試行を行う機能です。

試行全体の結果は、Optunaストレージに保存されていますので、optuna-dashboardによって分析することが可能です。以下のコマンドでoptuna-dashboardが起動します。

optuna-dashboard sqlite:///tools/out/optuna.db

おまけ. ハイパーパラメータを受け取る実装例(Rust)

// 環境変数が無かった時のデフォルト値(=最終提出するベストな値を入れる)
const P1: usize = 5;
const P2: f64 = 0.5;

fn main() {
    // 環境変数の取得
    let p1: usize = os_env::get::<usize>("p1").unwrap_or(P1);
    let p2: f64 = os_env::get::<f64>("p2").unwrap_or(P2);
    println!("p1: {p1}, p2: {p2}");
}

// 他の環境変数とのバッティングを避けるため、プレフィックスをつけ、
// AHC_PARAMS_P1, AHC_PARAMS_P2が環境変数となる
pub mod os_env {
    const PREFIX: &str = "AHC_PARAMS_";

    pub fn get<T: std::str::FromStr>(name: &str) -> Option<T> {
        let name = format!("{}{}", PREFIX, name.to_uppercase());
        std::env::var(name).ok()?.parse().ok()
    }    
}

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages