最適化セミナーのご案内

5.2.2 二次計画問題の例(UNIX)

 サンプルディレクトリの中には次のような簡単な二次計画問題を記述したモデルファイルQP.smpがあります.

  1. モデルの記述(UNIX
    //
    // 簡単な二次計画問題
    //
        // 集合
        Set S;
        Element i(set=S);
        Set T;
        Element j(set=T), k(set=T);
        // パラメータ
        Parameter c(name="c", index=j);
        Parameter Q(name="Q", index=(j,k));
        Parameter cu(name="cu", index=i);
        Parameter cl(name="cl", index=i);
        Parameter A(name="A", index=(i,j));
        Parameter bu(name="bu", index=j);
        Parameter bl(name="bl", index=j);
        // 変数
        Variable x(index=j);
        // 最小化
        Objective f(type=minimize);
        f = sum(c[j] * x[j], j) + 1.0/2.0 *sum(Q[j,k]*x[j]*x[k], (j,k));
        // 条件
        Expression constr(index=i);
        constr[i] = sum(A[i,j] * x[j], j);
        cu[i] >= constr[i] >= cl[i];
        bu[j] >= x[j] >= bl[j];
  2. genClassの起動(UNIX
    さて,次はこのモデル記述QP.smpからクラス定義と実装を記述したファイル(C++のコード)を生成します.そのためにはプロンプトから

    prompt% genClass QP.smp

    とします.genClassはNumerical Optimizerをインストールした環境でのみ実行可能なユーティリティーです.そうすると,この数理計画モデルに対応するクラス定義と実装がgenClassを起動したディレクトリに作成されます.サンプルのmakefileではこの操作を生成規則に記述しています.

    QP.h QP.smpに対応するクラスの定義
    QP.cc 同クラスの実装
    QPControl.cc コントロール手続きサンプル

     

    クラスの定義(QP.h)は,この数理計画問題に対して操作を行うコントロール手続き(C++のコード)の中で操作を行う前に必ずインクルードする必要があります.クラスの実装(QP.cc)の内容は通常ユーザが意識する必要はありませんが,実行形式を作成する際にユーザのプログラムにコンパイル・リンクする必要があります.
  3. モデルからの問題の生成,求解,出力(UNIX
    次にこの数理計画モデルに対する操作の例です.以下はサンプルプログラムuseClass.ccからこの二次計画問題に関連する操作を行っている部分を抜き出したものです.
    #include "QP.h" //クラスの定義のインクルード(必須)
    
        …中略…
    
    void simpleControl()
    {
      int len,i,*ind;
    
      //
      //  QPの求解
      //
      System_QP qp; // QP.smpに対するクラスオブジェクトqpの宣言と最適化実行
    
      qp.f.val.print(); // QP.smp中のオブジェクトの表示
      qp.x.val.print();
      qp.constr.val.print();
    
      double* x;
      qp.x.val.dump(len,ind,x); // QPのxをC++の配列にダンプ.
    
      printf("x(QP):\n");
      for ( i = 0 ; i < len ; ++i ) {
          printf("[%3d] %10.3e ",ind[i],x[i]);
          if ( (i+1) % 4 == 0 ) {
          printf("\n");
          }
      }
      printf("\n");
    
         …後略…
    useClass.ccではsimpleControlという手続きの中で二次計画問題に対する操作が記述されています.手続きの名前は任意です.
    手続きの最初でSystem_QPというクラスのオブジェクトqpを宣言していますが,これがQP.smpというモデル記述に対応するクラスのオブジェクトの宣言です.
    一般に

    NAME.smpなるモデルにはSystem_NAMEというクラスが対応

    します.クラスの定義ファイルNAME.hにはSystem_NAMEの定義が書かれています.そのため,System_NAMEを使用する際には必ずNAME.hのインクルードが必要になります.
    さて,宣言して作成されたqpがその数理計画モデル(問題)そのものに対応します.以下ではこれを説明のため「システムオブジェクト」と呼びます.システムオブジェクトに対して

    qp.show();

    とすると,問題の中身が表示されます(showSystem()と同じ).次にシステムオブジェクトと数理計画モデルの構成要素についてですが,一般的な原則として以下があります.

    System_NAMEのオブジェクトsについてs.xNAME.smp中のオブジェクトxに対応する.


    さらに,s.xとして参照されたオブジェクトに対する

    代入 =
    表示 print()coutdump()
    配列の設定 readD()

     

    などの操作は通常のSIMPLEオブジェクトに対するのと全く同様に可能です.
    例えば

    qp.f.print();
    qp.x.print();
    qp.constr.print();

    とすると,それぞれQP.smpのモデル中のfxconstrの値が表示されます.

    デフォルト動作では宣言を行ったのみで自動的に最適化の実行が行われます.オプションの設定(options.noDefaultSolve = 1)によって,自動的に最適化の実行が行われるのを抑制することが可能ですが,その場合には

    qp.solve();

    として陽に求解を指示します.

    サンプルルーチンuseClass.ccでは,QP.smpのデータ(Parameter)は与えていませんが,その場合には,SIMPLEのデータファイルの内容が自動的に設定されます.ただし,そうするにはメイン関数によってデータファイルを読みこんでバッファに蓄えておく必要があります.その方法については後述します.

    求解のあと,

    qp.x.val.dump(len,ind,x); // QPのxをC++の配列にダンプ.

    と行うことによって,解をC++の配列に解を書き出しています.SIMPLEでは一般に

    (SIMPLEオブジェクト).val.dump

    によってオブジェクトを配列に書き出すことができます.

  4. モデルへのデータ入力(UNIX
    モデルにデータを設定する方法は
    A) SIMPLEのデータファイルを用いる.
    B) C++の配列を直接設定する.
    という二つがあります.
  5. データファイルの利用(UNIX
    useClass.ccでは,QP.smpのデータ(Parameter)の設定は0の方法で行うことを前提としています.この場合には,このこのデータからシステムオブジェクトを作成する前にメイン関数によってデータファイルを読みこんでバッファに蓄えておく必要があります.
    データを読み込むには

    readData(FILE* fp, char* filename);

    なる手続きをコールします.fpはCのstdio.hで宣言されているファイル構造体で,読み込むデータファイルに対応します.データが読み込まれると,データの内容が共用バッファにプールされます.データ読み込みは繰り返すことができます.UseClass.ccmain()手続きの中の次のコードは,呼び出し引数として与えられたファイルをすべて読み込んで共用バッファに蓄えます.

    //
    // SIMPLE形式で記述されたデータを読む
    //
    int i;
    for (i = 1 ; i  < argc; i++) {
      char *file = argv[i];
      FILE* fp;
      if (strcmp(file, "stdin") == 0) fp = stdin;
      else fp = fopen(file, "r");
      if (!fp) {
        fprintf(stderr, "can not open %s\n", file);
        exit(1);
      }
      readData(fp, file);
    }
    useClass.ccQP.smpに対応する例えば次のようなデータ:

    c = [1] -3 [2] 1;
    Q =
    [1,1] 11 [1,2] 0
    [2,1] 0  [2,2] 22
    ;
    cu = [1] 10 [2] 10 [3] 10;
    cl = [1] -1 [2] -2 [3] 2;
    A =
    [1,1] -1   [1,2] 0.1
    [2,1] -0.2 [2,2] -1
    [3,1] 2    [3,2] 1
    ;
    bu = [1] 1 [2] 2;
    bl = [1] 0 [2] 0;

    を読み込みますが,この時点で名前と値の対応がバッファに蓄えられます.続いて,

    System_QP qp;

    が実行された段階で,QP.smpの中のParameterであるaQcuclAbublにこの値が設定されます.バッファにプールされたデータとパラメータの対応付けは名前によって行われます.名前はコンストラクト時にname =で与えますが,name =によって名前を与えられていないオブジェクトには,そのオブジェクトの名前と同じ名前が自動的に与えられます.

    例えば,通常

    Parameter a(name="パラメータ");

    に対しては

    パラメータ = 2;

    というデータファイルの記述が対応しますが,

    Parameter a;

    と書くと,これは

    Parameter a(name="a");

    としたことに相当して,データファイル中の

    a = 2;

    という記述に対応します.

    データファイルから読み込んだデータは大域的に有効で,特定のモデルに結びついたものではないことにご注意ください.
    例えば

    Parameter a;

    という記述を含む二つのモデルM1.smpM2.smpがあり,それから生成されたクラスの両方を利用するコード,

    System_M1 m1;
    System_M2 m2;

    があるとします.
    最初に

    a = 2;

    というデータファイルを読み込んだ状態でこのコードを実行すると,m1m2両方にa = 2が設定されます.


 

 

上に戻る