Integer Set Library（ISL）は、整数集合操作のための高速で効率的なライブラリであり、多くのアプリケーションで使用されています。ISLは、データフロー解析、スケジューリング、グラフの彩色など、様々な分野で利用され、その高速性と柔軟性により、多くのプロジェクトで重要な役割を果たしています。
本チュートリアルは、ISLの基本的な操作から高度な機能までを学ぶためのガイドです。初心者の方でも理解できるよう、わかりやすく説明します。また、応用例を含め、ISLの具体的な使用方法を理解することができます。
本チュートリアルを通じて、ISLの利点と欠点を理解し、プロジェクトに応じた適切な使用方法を選択できるようになることを目指しています。ISLを学び、その強力な機能を活用し、プログラミングの可能性を広げましょう。
ISLは、整数集合の操作を行うためのライブラリであり、データフロー解析、スケジューリング、グラフの彩色など、様々なアプリケーションで利用されています。ISLは、整数集合の演算や制約を簡単に表現できることが特徴であり、高速で効率的な計算を実現します。
ISLは、Linux、Mac OS X、Windowsなどの多くのプラットフォームで利用できます。インストール方法は、以下の手順に従います。
ISLのソースコードをダウンロードします。
ソースコードを展開して、必要なライブラリをインストールします。
ISLをビルドして、実行可能ファイルを作成します。
ISLを動作させるための必要なライブラリをインストールします。
ISLを使用するためには、以下の手順が必要です。
ヘッダーファイルをインクルードします。
ISLの構造体を初期化します。
ISLの関数を使用して、整数集合を生成、演算、変換します。
ISLは、整数集合の操作を簡単に実現できるように設計されています。各関数の使用方法は、チュートリアルの後半で説明します。
ISLでは、整数集合を生成するための関数が用意されています。例えば、以下の関数を使用して、1から10までの整数を含む整数集合を生成できます。
isl_set *set = isl_set_range(isl_set_universe(isl_ctx_alloc()), 1, 10);
以下は、整数集合の生成と表示を行うC言語のコード例です。
isl_intset.c:
int main()
{
    isl_ctx* ctx = isl_ctx_alloc();
    // create an integer set { x : x >= 0 and x < 10 }
    isl_set* s1 = isl_set_read_from_str(ctx, "{ x : x >= 0 and x < 10 }");
    
    // create an integer set { x, y : x >= 0 and x < 10 and y >= 0 and y < 5 }
    isl_set* s2 = isl_set_read_from_str(ctx, "{ x, y : x >= 0 and x < 10 and y >= 0 and y < 5 }");
    // print the sets
    printf("s1 = ");
    isl_set_print(s1);
    printf("s2 = ");
    isl_set_print(s2);
    isl_set_free(s1);
    isl_set_free(s2);
    isl_ctx_free(ctx);
    
    return 0;
}
このコードでは、整数集合の生成と表示を行います。まず、isl_set_read_from_str()関数を使って、文字列から整数集合を生成します。生成された整数集合は、isl_set型の変数に格納されます。
次に、isl_set_print()関数を使って、整数集合を表示します。
コードをビルドするには、ISLのライブラリをリンクする必要があります。次のように、ccコマンドを使ってコードをビルドできます。
% cc -I/usr/local/include -L/usr/local/lib -lisl isl_intset.c -o isl_intset
ここで、-lislオプションは、ISLのライブラリをリンクするために使用されます。ビルドが成功したら、isl_intsetを実行して、整数集合が表示されることを確認できます。
ISLでは、整数集合の和、差、共通部分、積集合などの演算が可能です。以下は、2つの整数集合の和を計算する例です。
isl_set *set1 = isl_set_range(isl_set_universe(isl_ctx_alloc()), 1, 5);
isl_set *set2 = isl_set_range(isl_set_universe(isl_ctx_alloc()), 6, 10);
isl_set *set3 = isl_set_union(set1, set2);
ISLでは、整数集合を他の形式に変換することができます。例えば、整数集合をポリトープ、多面体、線形不等式などに変換することが可能です。以下は、整数集合を多面体に変換する例です。
isl_set *set = isl_set_range(isl_set_universe(isl_ctx_alloc()), 1, 10);
isl_basic_set *bset = isl_set_basic_set(set);
isl_convex_hull *hull = isl_basic_set_convex_hull(bset);
ISLでは、整数集合を単純化することができます。単純化により、整数集合の表現を簡略化し、計算時間を短縮できます。以下は、整数集合を単純化する例です。
isl_set *set = isl_set_range(isl_set_universe(isl_ctx_alloc()), 1, 10);
isl_set *simple_set = isl_set_simplify(set);
ISLには、これらの操作以外にも多くの関数が用意されています。詳細な情報は、ISLの公式ドキュメントを参照してください。
ISLでは、多項式を生成するための関数が用意されています。例えば、以下の関数を使用して、2つの変数xとyを持つ2次多項式を生成できます。
isl_space *space = isl_space_set_alloc(isl_ctx_alloc(), 0, 2);
isl_local_space *ls = isl_local_space_from_space(space);
isl_pw_qpolynomial *poly = isl_pw_qpolynomial_zero_on_domain(isl_local_space_domain(ls));
isl_pw_qpolynomial *x_term = isl_pw_qpolynomial_from_qpolynomial(isl_qpolynomial_var_on_domain(ls, isl_dim_set, 0));
isl_pw_qpolynomial *y_term = isl_pw_qpolynomial_from_qpolynomial(isl_qpolynomial_var_on_domain(ls, isl_dim_set, 1));
isl_pw_qpolynomial *xy_term = isl_pw_qpolynomial_mul(x_term, y_term);
poly = isl_pw_qpolynomial_add(poly, xy_term);
ISLでは、多項式の和、差、積、除算などの演算が可能です。以下は、2つの多項式の積を計算する例です。
isl_space *space = isl_space_set_alloc(isl_ctx_alloc(), 0, 2);
isl_local_space *ls = isl_local_space_from_space(space);
isl_pw_qpolynomial *poly1 = isl_pw_qpolynomial_zero_on_domain(isl_local_space_domain(ls));
isl_pw_qpolynomial *poly2 = isl_pw_qpolynomial_zero_on_domain(isl_local_space_domain(ls));
isl_pw_qpolynomial *x_term = isl_pw_qpolynomial_from_qpolynomial(isl_qpolynomial_var_on_domain(ls, isl_dim_set, 0));
isl_pw_qpolynomial *y_term = isl_pw_qpolynomial_from_qpolynomial(isl_qpolynomial_var_on_domain(ls, isl_dim_set, 1));
isl_pw_qpolynomial *xy_term1 = isl_pw_qpolynomial_mul(x_term, y_term);
isl_pw_qpolynomial *xy_term2 = isl_pw_qpolynomial_mul(y_term, x_term);
poly1 = isl_pw_qpolynomial_add(poly1, xy_term1);
poly2 = isl_pw_qpolynomial_add(poly2, xy_term2);
isl_pw_qpolynomial *result = isl_pw_qpolynomial_mul(poly1, poly2);
ISLでは、多項式制約を線形変換することができます。以下は、多項式制約を2次元空間上で回転する例です。
isl_space *space = isl_space_set_alloc(isl_ctx_alloc(), 0, 2);
isl_aff *aff1 = isl_aff_alloc(isl_local_space_from_space(space));
isl_aff *aff2 = isl_aff_alloc(isl_local_space_from_space(space));
isl_aff_set_coefficient_si(aff1, isl_dim_in, 0, 1);
isl_aff_set_coefficient_si(aff2, isl_dim_in, 1, 1);
isl_multi_aff *ma = isl_multi_aff_from_aff_list(isl_aff_list_from_affs(isl_aff
ISLでは、制約を整数集合に変換することができます。例えば、以下のような制約を整数集合に変換することができます。
 
isl_space *space = isl_space_set_alloc(isl_ctx_alloc(), 0, 2);
isl_local_space *ls = isl_local_space_from_space(space);
isl_constraint *c = isl_constraint_alloc_equality(isl_local_space_copy(ls));
isl_constraint_set_coefficient_si(c, isl_dim_set, 0, 1);
isl_constraint_set_coefficient_si(c, isl_dim_set, 1, -1);
isl_constraint_set_constant_si(c, 1);
isl_set *set = isl_set_universe(isl_space_copy(space));
set = isl_set_add_constraint(set, c);
この例では、x - y = 1の制約を整数集合に変換しています。ここで、isl_constraint_alloc_equalityは等式制約を生成するための関数であり、isl_constraint_set_coefficient_siとisl_constraint_set_constant_siは、制約の係数と定数を設定するための関数です。
ISLでは、制約を線形不等式の形式に変換することができます。例えば、以下のような制約を線形不等式の形式に変換することができます。
 
isl_space *space = isl_space_set_alloc(isl_ctx_alloc(), 0, 2);
isl_local_space *ls = isl_local_space_from_space(space);
isl_constraint *c = isl_constraint_alloc_equality(isl_local_space_copy(ls));
isl_constraint_set_coefficient_si(c, isl_dim_set, 0, 1);
isl_constraint_set_coefficient_si(c, isl_dim_set, 1, -1);
isl_constraint_set_constant_si(c, 1);
isl_basic_set *bset = isl_basic_set_universe(isl_local_space_copy(ls));
bset = isl_basic_set_add_constraint(bset, c);
isl_constraint *ineq = isl_inequality_from_basic_set(bset);
この例では、x - y = 1の制約を線形不等式の形式に変換しています。ここで、isl_basic_set_universeは空でない基本集合を生成するための関数であり、isl_basic_set_add_constraintは、制約を基本集合に追加するための関数です。最後に、isl_inequality_from_basic_setは、基本集合から線形不等式を生成するための関数です。
ISLを使ったデータフロー解析の例として、以下のようなCコードを考えます。
 
for (int i = 0; i < N; i++) {
  a[i] = b[i] + c[i];
  d[i] = a[i] * e[i];
}
このコードは、配列bとcの要素を加算して、結果を配列aに格納し、配列aの要素と配列eの要素を乗算して、結果を配列dに格納するという処理を行います。
このコードに対して、データフロー解析を行い、依存関係を抽出することができます。依存関係は、配列の要素の参照によって定義されます。例えば、以下のような依存関係があります。
 
a[i] -> d[i]
b[i] -> a[i]
c[i] -> a[i]
a[i] -> d[i]
e[i] -> d[i]
ISLを使って、このような依存関係を抽出することができます。
 
isl_union_map *reads = isl_union_map_empty(isl_space_set_alloc(ctx, 0, 1));
isl_union_map *writes = isl_union_map_empty(isl_space_set_alloc(ctx, 0, 1));
for (int i = 0; i < N; i++) {
  isl_map *read_map = isl_map_from_domain_and_range(isl_set_read_from_str(ctx, "{ S[i] }"), isl_set_read_from_str(ctx, "{ a[i] }"));
  isl_map *write_map = isl_map_from_domain_and_range(isl_set_read_from_str(ctx, "{ S[i] }"), isl_set_read_from_str(ctx, "{ d[i] }"));
  reads = isl_union_map_add_map(reads, read_map);
  writes = isl_union_map_add_map(writes, write_map);
}
この例では、依存関係を抽出するために、配列の参照に対応するマップを生成し、それらをisl_union_mapオブジェクトに追加しています。
以下は、データフロー解析にISLを使用するための単純なCプログラムです。この例では、ループのイテレーションスペースと配列のアクセスパターンが与えられます。そして、このプログラムは、ループのイテレーションスペースと配列のアクセスパターンから、ループのイテレーションスペース内でアクセスされる配列要素の集合を計算します。
 
int main() {
  isl_ctx* ctx = isl_ctx_alloc();
  // Input: loop iteration space and array access pattern
  isl_set* loop_iter_space = isl_set_read_from_str(ctx, "{ [i] : 0 <= i < N }");
  isl_map* access_pattern = isl_map_read_from_str(ctx, "{ [i,j] -> A[i+j] : 0 <= i < N and 0 <= j < M }");
  // Compute accessed array elements
  isl_set* accessed_elems = isl_map_apply_range(isl_map_copy(access_pattern), isl_set_copy(loop_iter_space));
  isl_set_dump(accessed_elems);
  isl_set_free(accessed_elems);
  isl_map_free(access_pattern);
  isl_set_free(loop_iter_space);
  isl_ctx_free(ctx);
  return 0;
}
このプログラムは、ループイテレーションスペース { [i] : 0 <= i < N } と配列アクセスパターン { [i,j] -> A[i+j] : 0 <= i < N and 0 <= j < M } を受け取ります。次に、ISLライブラリの関数を使用して、アクセスされる配列要素の集合を計算します。これは、 isl_map_apply_range() 関数を使用して、 access_pattern に loop_iter_space を適用することで実現されます。最後に、計算されたアクセスされる要素の集合を isl_set_dump() 関数を使用して表示し、ISLオブジェクトを解放します。
「グラフの彩色」は、グラフ理論において重要な問題の1つです。与えられたグラフに対して、各頂点に異なる色を割り当てることで、隣接する頂点が同じ色にならないようにする問題です。
ISLを使ってこの問題を解くことができます。以下は、Pythonで実装された「グラフの彩色」のコード例です。
import islpy as isl
def graph_coloring(num_vertices, edges, num_colors):
    ctx = isl.Context()
    # generate variables x_i_j (i=0,1,...,num_vertices-1, j=1,2,...,num_colors)
    variables = [
        isl.Variable(ctx, isl.dim_type.set, i, 1, num_colors)
        for i in range(num_vertices)
    ]
    # add constraints for each edge (i,j)
    constraints = []
    for i, j in edges:
        for k in range(num_colors):
            constraints.append(variables[i][k] + variables[j][k] <= 1)
    # create an empty linear program
    lp = isl.BasicSet.universe(isl.Space.alloc(ctx, 0, num_vertices * num_colors))
    # add constraints to the linear program
    for constraint in constraints:
        lp = lp.add_constraint(constraint)
    # set objective function to minimize the number of colors used
    obj = isl.Aff.zero_on_domain(lp.get_space())
    for i in range(num_vertices):
        obj += variables[i][0]
    for k in range(1, num_colors):
        obj -= variables[num_vertices - 1][k]
    # create a linear program with the objective function
    lp = lp.set_objective(obj)
    # solve the linear program
    lp = lp.simplex()
    # get the optimal solution
    solution = lp.sample_point()
    if solution.is_void():
        return None
    # extract the solution as a list of colors
    colors = [solution.get_val(variables[i][0]).to_python() for i in range(num_vertices)]
    return colors
この関数の実装は、まずISLのContextを生成します。次に、グラフの頂点数と色の数に応じた変数を生成します。変数x_i_jは、頂点iが色jを持つ場合に1となります。
その後、各辺の制約条件を変数に対して設定します。例えば、辺(i,j)が存在する場合、x_i_k + x_j_k <= 1の制約条件が追加されます。この制約条件は、頂点iと頂点jが同じ色kを持つことができないことを示しています。
次に、空の線形計画問題を作成し、制約条件を線形計画問題に追加します。最適化する目的関数は、使用する色の数を最小化するものとなります。具体的には、各頂点iに対して、変数x_i_0を加算し、最後の色kについては減算することで、使用する色の数をカウントします。
最後に、線形計画問題を解いて、最適解を取得します。最適解は、各頂点に割り当てられる色のリストとして返されます。
この関数は、与えられたグラフに対して最小限の色数で彩色することができるため、実用的な問題解決に役立ちます。