LoginSignup
1
1

More than 1 year has passed since last update.

Makefileで全探索

Posted at

背景

Makefileをタスクランナーとして使用する記事が色々あります。

そうすると、パラメータチューニングなどで、全探索をしたくなります。

本記事では、Makefileの並列処理の能力を生かせるようにターゲットを構成してみます。

方法

Makefileのワイルドカードや関数を用いて、テストケースの通し番号に紐づくパラメータの組み合わせを求めます。

LIST_OF_VAR_A = $(shell seq -f "%03.1f" 0 0.1 0.2)
LIST_OF_VAR_B = $(shell seq -f "%01.0f" 2)
LIST_OF_VAR_C = $(shell seq -f "%02.0f" 10 12)

LEN_A = $(words $(LIST_OF_VAR_A))
LEN_B = $(words $(LIST_OF_VAR_B))
LEN_C = $(words $(LIST_OF_VAR_C))

LAST_INDEX := 1
LAST_INDEX := $(shell echo "$(LEN_A) * $(LAST_INDEX)" | bc)
LAST_INDEX := $(shell echo "$(LEN_B) * $(LAST_INDEX)" | bc)
LAST_INDEX := $(shell echo "$(LEN_C) * $(LAST_INDEX)" | bc)
LAST_INDEX := $(shell echo "$(LAST_INDEX) - 1" | bc)

OUTPUTS = $(foreach INDEX, $(shell seq -f "%02.0f" 0 $(LAST_INDEX)), test_$(INDEX))

all: $(OUTPUTS)

test_%:
    $(eval INDEX:=$(subst test_,,$@))
    $(eval VAR_A = $(word $(shell echo "$(INDEX) % $(LEN_A) / 1 + 1" | bc), $(LIST_OF_VAR_A)))
    $(eval VAR_B = $(word $(shell echo "$(INDEX) % ($(LEN_A)*$(LEN_B)) / $(LEN_A) + 1" | bc), $(LIST_OF_VAR_B)))
    $(eval VAR_C = $(word $(shell echo "$(INDEX) % ($(LEN_A)*$(LEN_B)*$(LEN_C)) / ($(LEN_A)*$(LEN_B)) +1 " | bc), $(LIST_OF_VAR_C)))
    @echo Check $@ with VAR_A: $(VAR_A), VAR_B: $(VAR_B), VAR_C: $(VAR_C)

結果

$ make all
Check test_00 with VAR_A: 0.0, VAR_B: 1, VAR_C: 10
Check test_01 with VAR_A: 0.1, VAR_B: 1, VAR_C: 10
Check test_02 with VAR_A: 0.2, VAR_B: 1, VAR_C: 10
Check test_03 with VAR_A: 0.0, VAR_B: 2, VAR_C: 10
Check test_04 with VAR_A: 0.1, VAR_B: 2, VAR_C: 10
Check test_05 with VAR_A: 0.2, VAR_B: 2, VAR_C: 10
Check test_06 with VAR_A: 0.0, VAR_B: 1, VAR_C: 11
Check test_07 with VAR_A: 0.1, VAR_B: 1, VAR_C: 11
Check test_08 with VAR_A: 0.2, VAR_B: 1, VAR_C: 11
Check test_09 with VAR_A: 0.0, VAR_B: 2, VAR_C: 11
Check test_10 with VAR_A: 0.1, VAR_B: 2, VAR_C: 11
Check test_11 with VAR_A: 0.2, VAR_B: 2, VAR_C: 11
Check test_12 with VAR_A: 0.0, VAR_B: 1, VAR_C: 12
Check test_13 with VAR_A: 0.1, VAR_B: 1, VAR_C: 12
Check test_14 with VAR_A: 0.2, VAR_B: 1, VAR_C: 12
Check test_15 with VAR_A: 0.0, VAR_B: 2, VAR_C: 12
Check test_16 with VAR_A: 0.1, VAR_B: 2, VAR_C: 12
Check test_17 with VAR_A: 0.2, VAR_B: 2, VAR_C: 12

解説

3通りの値を取るVAR_A、2通りの値を取るVAR_B、3通りの値を取るVAR_Cの全探索なので、3x2x3=18(通り)あります。
そこで、00から18までのインデックスを各場合に割り当て、生成すべきターゲット名をtest_00などのように名付けます。

ターゲット名から、インデックス$I$を取得するのが以下の行です。

$(eval INDEX:=$(subst test_,,$@))

インデックス$I$に基づいて、パラメータを決定します。
VAR_A, VAR_B, VAR_Cのインデックス$I_A$, $I_B$, $I_C$は以下で決定します。

$$ I_A = (I \% 3) // 1$$
$$ I_B = (I \% (3*2)) //3 $$
$$ I_C = (I \% (3*2*3)) //(3*2) $$

ここで、//は商を、%は「あまり」を求める演算子です。この計算で以下の数列を得ます。
ただし、Makefileの関数であるwordは、最初の要素のインデックスがゼロではなく1であるので、上記の計算で求めたI_A, I_B, I_Cに1を足して、パラメータを取得しています。

I I_A I_B I_C
0 0 0 0
1 1 0 0
2 2 0 0
3 0 1 0
4 1 1 0
5 2 1 0
6 0 0 1
7 1 0 1
8 2 0 1
9 0 1 1
10 1 1 1
11 2 1 1
12 0 0 2
13 1 0 2
14 2 0 2
15 0 1 2
16 1 1 2
17 2 1 2
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1