Comments
Description
Transcript
Xpress-MP 概説書
© Copyright Dash Associates 1984 – 2002 Second Edition, February 2002. Xpress-MP 概説書 Xpress-MPの機能と活用について一から説明します。 2007年 Dashoptimization株式会社 注: 本マニュアルに含まれるすべての商標はダッシュ社(Dash Associates)のものではありません。 本書で使用されている会社名、製品名、データは、すべて架空のものであって、単にXpress-MPの使 用方法を例示するためのものです。したがって、仮に類似の名称、データが実際に存在していても、 それは偶然であることを承知ください。 弊社への連絡方法 Xpress-MPの使用に関して、質問、コメント、等がありましたら、下記のダッシュ社技術 サポート部にご連絡ください。: Xpress-MPソフトウェアの購入注文に関して、質問等がありましたら、下記のダッシュ社 各地域販売部にご連絡ください。 Xpress-MPソフトウェアと関連資料の更新に関する最新のニュースについては、下記のウ ェブサイトをご覧ください。 http://www.msi-jp.com/doj/ 2 目 次 1 始める前に 1 はじめに ·············································································7 Xpress-MP ライブラリーと構成要素 ·····················································8 Xpress-Mosel Xpress-Optimizer ························································8 Xpress-IVE ···········································································8 コンソール Xpres-MP ··································································8 Xpress-MP ライブラリー ·······························································9 どのインターフェイスを使うか? ························································9 この本の読み方 ······································································ 9 この本の構成 ········································································ 9 表記法 ·············································································· 10 2 Xpress-IVE 始める前に ·········································································· 11 Xpress-IVEを始めよう ································································ 11 簡単なモデル入門 ···································································· 11 プロジェクトの関連作業 ······························································ 12 モデルエディター ···································································· 12 コンパイルと読み込みと実行 ·························································· 13 Xpress-IVEをもっと知るために ························································ 14 Xpress-IVEについて ·································································· 15 制御オプションの設定 ································································ 15 多重モデルの実行 ···································································· 15 行列ファイルの書き方 ································································ 16 行列ファイルによる問題の入力 ························································ 17 関数のグラフ化 ······································································ 17 環境の変更 ·········································································· 18 ヘルプの利用 ··································································· 18 3 Console Xpress 始める前に ·········································································· 19 Console Xpress の構成要素 ··························································· 19 Xpress-Moselのテスト方法 ······························································· 20 Xpress-Optimizer の検査方法 ························································· 20 Xpress-Moselを用いて簡単な問題を解く ················································ 21 Mosel ファイル ······································································ 21 Moselの3つの柱 ······································································ 21 Moselからさらに情報を得る ··························································· 22 Moselをさらに用いる ································································· 23 パッチモードで実行する ······························································ 23 複数個のModelを実行する ····························································· 23 行列ファイルの書き方 ································································ 25 Xpress-Optimizerを動かす ···························································· 25 問題の解き方 ········································································ 25 解の見方 ············································································ 26 3 WRITEPRTSOLの出力 ··································································· 27 Optimizerをさらに利用するために ····················································· 28 最適化アルゴリズム ·································································· 28 整数計画法 ·········································································· 28 Optimizer の制御 ···································································· 30 ヘルプの利用 ········································································ 31 4 Xpress-MP ライブラリー 始める前に ·········································································· 32 Xpress-MPライブラリーの構成要素 ····················································· 32 Mosel ライブラリーを動かす ························································· 33 簡単なモデル入門 ···································································· 33 Mosel ライブラリーの構成要素 ························································ 33 Moselの3つの段階 ··································································· 33 解の情報の取得 ······································································ 35 Mosel ライブラリーをさらに利用するために ············································ 36 いくつかのモデルをさらに利用するために ·············································· 36 実行時間の管理 ······································································ 38 行列ファイルの書き方 ································································ 39 Xpress-BCLをさらに利用するために ···················································· 40 モデルの最初の定式化 ································································ 40 問題をBCLを用いて解く方法 ··························································· 42 行列変数を用いた問題の定式化 ························································ 44 BCLをさらに利用するために ··························································· 45 整数計画法 ·········································································· 45 行列ファイルの書き方 ································································ 46 Xpress-Optimizer ライブラリーをさらに利用するために ································· 47 Optimizer ライブラリーを用いて問題を解く方法 ········································ 48 XPRSwriteprtsolを用いて解を得る方法 ················································· 49 Optimizer ライブラリーをさらに利用するために ········································ 50 最適化アルゴリズムを変更する ························································ 50 整数計画法 ·········································································· 50 事前解法と後処理 ···································································· 51 制御と問題の特性 ···································································· 52 最適化処理の対話方式 ································································ 53 メモリーからのモデルの入力方法 ······················································ 55 Optimizerライブラリーによる問題の入力方法 ··········································· 55 問題の解を得る方法 ·································································· 57 問題の行列の変更 ···································································· 58 問題の全域要素の追加 ································································ 59 Xpress-MPライブラリーから最良解を得ること ··········································· 60 エラーの検出方法 ···································································· 60 BCLとOptimizerライブラリーの結合 ···················································· 61 Visual BasicとXpress-MPライブラリーの利用 ··········································· 63 内部ファイル ········································································ 63 主要構成 ············································································ 63 Visual Basicのコールバックスの利用 ·················································· 66 4 JavaによるXpress-MPライブラリーの使用方法 ··········································· 67 Javaを用いたBuilder Component Library (BCL) ········································· 67 Javaを用いた Optimizer ライブラリー ················································· 68 主要構成 ············································································ 70 Javaのコールバックスの利用 ·························································· 72 ヘルプの利用 ········································································ 73 5 Xpress-MPによるモデルの構築 はじめに ············································································ 74 最初のモデルの構築 ·································································· 74 盗人問題 ············································································ 74 問題の特定 ·········································································· 75 モデルのXpress-Mosel への導入 ······················································· 75 Optimizerのライブラリーモジュール ··················································· 77 行列によるモデルの構築 ······························································ 78 行列変数と添字の導入 ································································ 78 ループ化と和計算 ···································································· 79 コメント ············································································ 79 ストリング添字の使用方法 ···························································· 80 モデリングにおける汎用性 ···························································· 81 総称的モデルと例示的モデル ·························································· 81 実数の宣言 ·········································································· 81 テキストファイルからのデータの入力 ·················································· 82 強盗問題の解 ········································································ 82 Mosel によるパラメタの利用 ·························································· 85 ハイカー問題 ········································································ 85 ハイカー問題の解法 ·································································· 85 ヘルプの利用 ········································································ 86 6 Moselに関する話題 はじめに ············································································ 88 集合について ········································································ 88 動態的、固定的、最終集合 ···························································· 89 集合演算 ············································································ 89 行列の利用について ·································································· 90 動的、固定的行列 ···································································· 91 疎行列 ·············································································· 92 データの入出力 ······································································ 93 モデルデータの要素 ·································································· 93 initializations ブロックを用いたデータの転送 ········································ 93 ODBCを用いたデータの転送 ···························································· 94 readln と writeln によるデータの転送 ················································ 97 条件付き変数と制約 ·································································· 98 条件付き上下限 ······································································ 98 条件付き変数 ········································································ 99 基本的計画問題の構造 ································································ 99 if コマンド ········································································· 99 case コマンド ····································································· 101 5 forall ループ ····································································· 101 while ループ ······································································ 102 repeat ループ ····································································· 102 プロシージャと関数 ································································ 103 プロシージャ ······································································ 104 関数 ·············································································· 105 再帰性 ············································································ 106 前進型宣言 ········································································ 106 7 用語のまとめ····································································· 109 索引 ·············································································· 112 6 Xpress-MP Essentials Introduction 1 Getting Started 1 第1章 始める前に 概要 この章では、以下の項目について説明します。 • • • Xpress-Mosel と Xpress-Optimizerの概要; Xpress-Mosel と Xpress-Optimizerの相互関連; 本書の構成と読み方. はじめに Xpress-MP は数理モデル構築と最適化のためのソフトウェアで、線形ならびに2次の整数計画問題 に対して、モデルの定式化、最適解、分析のための各種手法を提供します。 さらにXpress-Mosel と Xpress-Optimizerという強力な構成要素に基づいて、各種インターフェイ スを通じて、問題解決とXpress-MP技術のソフトウェア製品への導入をはかることによって、多様な 利用者のニーズに応えるものである。 本書は、Xpress-MPへの入門書であって、初めての利用者がすぐにXpress-MPになじみ、実行できる ように書かれている。したがって、読者が数理計画法の知識をほとんど有していないことを前提と して、読み進めることができる構成となっている。一方、数理計画法による問題解決の経験と知識 のある読者に対しては、このソフトウェアに関する新たな知識が有効的に与えられるよう、考慮さ れている。 Xpress-MPの多くのマニュアルでは、本書に説明されている内容に関する知識が前提とされているた め、読者は、前もってこれらの内容に精通していることが望ましい。 Xpress-MPの構成要素とインターフェイスについて説明しよう。構成要素のうちのいずれを用いるか について、すでに決めている読者は、それにしたがって本書の関連する章を参照することができる。 未だ決めていない読者に対しては、本書の各章の概要がわかるように工夫がなされている。 本 書は、Xpress-MP製品のインストールやセットアップ方法について詳細を述べることを目的としてい るのではなく、そのような情報については、付属のCD-ROMにある‘readme’ 資料(readme.win, readme.unx) を参照されたい。すべての読者が、本CD-ROMに含まれるソフトウェアをインストール し、使用する前にオペレーティングシステム関連資料を読むことを強くすすめたい。 7 Xpress-MP の構成要素とインターフェイス Xpress-Mosel Xpress-MPの基本的な構成要素はXpress-MoselとXpress-Optimizerである。Moselは、Mosel モデル 言語を用いて線形計画法あるいは混合型整数計画法 (MIP)によって表されるモデルを構築し、これ らの問題に対する解法を与える環境である。モデルが小さい場合には、機能をいろいろと増やすラ イブラリーモジュールを含めることによってモデルの拡張が可能である。またMoselの基本的前提は、 モデリング記述と問題を解く手続きとが分離されていないということである。このために、複雑な モデルを構築し、Moselの中で問題を解き、解を利用者が指定したように出力するライブラリーモジ ュールとしてのOptimizerが含まれている。 ライブラリー利用者に対しては、もう一つのXpress-BCLがあるので、詳細については、4章を参照されたい。 Getting ODBC ライブラリーモジュールを用いると、Moselによってデータを検索し、回答をスプレッドシー トやデータベースの形で広く転送することが可能となる。ODBCは主要なデータベースとのデータ交 換を行う標準ソフトであって、Mosel mmodbcライブラリーモジュールには、ODBCをExcel, Oracle あ るいはAccessなどのODBC関連ソフトと連結する機能がある。 利用者は、Moselをテキストファイルからデータを取り出したり、問題を表現するためのデータベー スを作成したり、Optimizerライブラリーモジュールを呼び出して最適解を求めたり、数値結果を他 のODBC関連のソフトに転送してレポートやプレゼンテーション用資料を作成したりすることができ る。入力データはいくつかの異なるデータソースから集められ、各種の最適解は異なるファイルに それぞれの要求に最も良く合うように転送される。 Xpress-Optimizer Xpress-MPの中核部分にあるXpress-Optimizerには、線形計画法(LP), 2次計画法 (QP)、そして混 合型整数計画法 (MIP)などが含まれている。連続型の計画問題に関しては、Optimizerを用いて広範 な問題の解決が可能となった。Optimizerはあらゆる問題を解くための最良の手段として広く認識を されてきているが、各種の制御手法を用いることによって利用者がOptimizerの機能をそれぞれの問 題を各自が望むように利用することが可能となっている。このことは、特に、近似的な最適解を得 ることすら困難とされる、多くの大規模混合型整数計画問題を解く場合に有用である。最も難しい とされるMIP問題に対して、Xpress-MPは、並列型MIP Optimizerを用いて、多様なPCネットワークや ワークステーション、あるいはメモリー共用型マルチプロセッサーのもとで稼働し、すべてのコン ピュータの性能を十分に発揮するように作られている。並列型Optimizerは大規模最適化手法の最先 端にある手法である。 Getting Xpress-IVE Xpressの対話型ビジュアル環境としてのXpress-IVEは、マイクロソフトウィンドウズの下で動くモ デリングと最適化を進める環境である。Moselが、容易に利用可能なテキストエディターを有するグ ラフィカルユーザーインターフェイス(GUI)として表わされるとすると、IVEは、モデルを多重的に 開発し、管理し、さらに実行する手段となる。これらは容易に使用可能であることから、Xpress-MP 利用者が精通している強力なモデリング、最適化手法を結合することによって、IVEは標準型モデル を開発し、計算する理想型ソフトである。 Console Xpress Console Xpress はmoselとoptimizerの2つの独自に実行可能な部分からなる。これらはMoselと Optimizerという構成要素に対する強力なテキストベースの ‘console’ インターフェイスである。 8 これらは広範な機能とアルゴリズム上の汎用性を有し、Windows, Linux あるいは UNIXなど種々の OS上で本質的には同様の形で利用可能となっている。Console Xpress の製品は、多くの業務用利用 者にとって各種の要求を満たすべく、作られていることが判明している。Xpress-IVEを用いてモデ ルを構築すると、Console Xpressの要素をバッチファイルあるいはシェルスクリプトに組み込み、 大規模なモデルデータを解くように最適化アルゴリズムを利用することによって、生産システムの モデルを容易に構築することが可能となる。 Xpress-MP ライブラリーズ C/C++, Javaあるいは Visual Basic を用いてXpress-MP ライブラリーをMosel とOptimizerの2つ を組み込むと、より専門的な応用を実現することが可能となります。 Xpress-MP ライブラリーの 主な長所は、Xpress-MPによって提供される各種の機能を利用者の応用例に適合させ、それぞれの応 用例の中で利用者のニーズを満たすような柔軟性を持たせていることである。Windowsの下では、 Xpress-MP ライブラリーは、Xpress-MPの構成要素をWindowsの下で統合するための標準型数理計画 問題のインターフェイスを提供するDLLとして利用可能である。 Getting UNIX 利用者にとっては、Xpress-MP ライブラリーを共用のオブジェクトライブラリーとして利用す ることが可能である。またXpress-MP ライブラリーのもう一つの利用方法として、すべての Xpress-MP製品の中で基本的なモデルの構築手法と最適化機能を利用した単独の応用例を作成する ことも可能である。データを入力し、問題を作成し、最適解を求め、解を出力するという応用例に ついては、膨大なマニュアルに頼ることなく、容易に処理することが可能である。特殊な応用例を 実行し、特別なヒューリスティック手法を用いたい利用者にとっては、Xpress-MP ライブラリーは より高度の機能を提供することが可能である。すなわち、行列を入力し、操作し、出力し、問題を 解き、解を検索し、多重的に問題を処理し、コントロールすることが可能である。さらに、最適化 アルゴリズムと分枝カット法(‘Branch and Cut’)の管理機能を駆使することによって、より高度の最 適化手法の応用が可能となる。 どのようなインターフェイスを使うか? 初心者が新たなモデルを構築する場合には、Xpress-IVEによってXpress-MPソフトウェアに対する最 も簡単なインターフェイスを利用することができる。統合的な開発環境を用いると、モデル構築が 容易となり、他のインターフェイスの可能性を処理するすべての解が得られる。モデルの構築と最 適化手法を利用する生産システムを作動させるには、Console Xpressが用いられ、バッチ処理の応 用例が構築される。これは最大の問題例にふさわしい最適化手法を提供する柔軟性のある製品であ る。仮に利用者がウィンドウズのようなソフトウェアを開発している場合には、Xpress-MPライブラ リーが最良のインターフェイスを提供する。また問題が難しく、良い解を得るために特別なヒュー リスティック手法を開発したいような場合には、Xpress-MPライブラリーが非常に有用となることも ある。 Getting Started 1 この本の読み方 この本の構成 本書は、読者がXpress-MPソフトウェアをすでにインストールしていることを前提としているので、 インストールの詳細については記述されていない。Xpress-MPソフトウェアのインストールと主要な 9 各種コンパイラー環境のライブラリー構成要素のセットアップに関する情報については、インスト ールのためのCD-ROMに付属している‘readme’ ファイル (readme.win, readme.unx および lib\readme.txt) を参照されたい。 以下の3つの章 (2 – 4)は、初めての利用者にとって興味あるものであって、Xpress-IVE, Console Xpressあるいは the Xpress-MP ライブラリーなどの各種Xpress-MPインターフェイスにつ いて論じている。本書を利用する読者の大部分は、本書を利用する特定の目的のみが関心事である ため、各種Xpress-MPインターフェイスの中でも唯一の事項だけが関連することになるであろう。以 下の各章においては、まず一つの簡単な例題を取り上げ、読者自身の問題の最適化をおこなう場合 に重要となる実際的な多くの事項を紹介する。以下の2つの章では、Moselとそのモデル言語につい て述べている。まず5章は“Xpress-MPによるモデリング”であって、モデリング言語についての主要 な特性を紹介し、さらに簡単なモデルを構築し、いろいろなモデリングの機能の特典についても述 べている。6章では、”Mosel についてのさらなる話題”として、いくつかの概念を拡張することによ って得られる関連の話題を紹介する。しかしながら、ここでの扱い方は概略のみであるので、詳細 については、Xpress-Moselレファレンスマニュアルを参照されたい。本書の最後は、すべてのマニ ュアルで用いられる共通の用語集となっている。 表記法 本書の大部分を理解するには、いくつかの形式、規則について共通理解をしておく必要がある。 一般に、問題を表現するには標準的な印刷スタイルが用いられ、方程式や変数はイタリック形式の 固定幅フォントを用いた出力として得られる。問題の出力が混乱を招くような場合には、利用者の 入力をボールドタイプ(bold type face)で表し、いくつかの問題の間の相異を表すのにも、この方 式が用いられる。さらに追加的に、本書では以下のような表示形式とアイコンが用いられている。 Getting 注: 読者に知っておいて欲しいと思われる一般的事項を表す。関連のテキストの中で用いられてい る場合に必要とされる事項である。 注意: 読者自身が問題あるいは解を正しく把握するために必要とされることが多い注意点を表す。 概念: 経験することは重要である。概念とは、われわれが過去において成功したようなアプローチ、 あるいはわれわれ利用者にとって価値があったとされる特定の知識を表す。 デバッギング: 特に本書の前半部分に関連する事項で、利用者がソフトウェアをセットアップする のが困難な場合に必要とされる共通のエラーの扱い方、ヘルプの用い方などを表す。 演習問題: 本書を通して、いくつかの短い演習問題を掲載した。これらは本製品についての演習と いうだけでなく、他の章にある概念についても理解を深めるために有用なものである。 標記: 本書の利用に際しては、いろいろな読み方が可能であって、各自の興味と知識に合わせて、 ある部分を読み飛ばすことも可能である。この標記は、論理的な章とその内容とが終了することを 意味し、次の章の内容を表している。 補注: 本項目は、本文中に入れられないような細かな事項を欄外に注あるいは引用の形で記載し、読者の 注意を引くことを目的としている。 Getting まとめ この章では、Xpress-MPソフトウェア、Mosel、Optimizerという3つの構成要素、そしてまた Xpress-IVE, Console Xpress、Xpress-MP ライブラリーという主要な3つの構成要素に対するイン ターフェイスについて学んだ。 Xpress-IVE 2 10 第2章 Xpress-IVE 概要 本章では、以下の項目について述べる。 • • • • プロジェクトの実施とXpress-IVEインターフェイスの中のプログラムファイルのモデル化; 単純なモデルの入力とその解法; 出力と問題の行列ファイルとしての入力; Xpress-IVE環境の概観をカストマイズする方法を学ぶ はじめる前に Xpress-IVEは、Xpress-Moselに対するグラフィックユーザーインターフェイスであって、またマイ クロソフトウィンドウズの利用者にとってはXpress-Optimizerであるため、Xpress-MPの最適化ツー ルを利用し、学ぶ際の簡単な開発環境を提供するものである。本章の読者は、すでにXpress-IVEを インストールしているものとする。本章を読むと、対話型のビジュアル環境が説明され、解と出力 がいろいろな形式で表現されているので、簡単なモデルをどのようにして入力し、解くかが分かる であろう。Xpress-IVEのインターフェイスは、利用方法を容易にするために、MoselとOptimizerと いう2つのXpress-MPの主要な構成要素を一つのグラフィック環境に組み込んでいる。強力なMosel モデルプログラミング言語を実行するために、複雑なモデルが開発され、最小の手間で解かれるた め、Xpress-IVEはXpress-MPソフトウェアの新たな利用者にとって理想的な出発点となっている。 2 Xpress-IVE Xpress-IVEを始めよう インストールをすると、スタートメニューにアイコンフォルダーが追加され、Startボタンを押し、 Programs, Xpress-MP とXpress-IVEを選択するとIVEが開始される。そうでない場合には、 xpressmp\binディレクトリーにあるive.exeアイコンをクリックすることによってWindows Explorerからスタートすることになる。 演習問題: IVEを開始し、インストールが正しく行われたことを確認せよ。 すべてのXpress-MP製品には、ソフトウェアにとって必須の安全システムが付けられており、インス トールによって安全対策としてのすべての特性が実施可能となる。インストールが正しく行われな い場合には、あるいはまたライセンスなしで実行するような場合には、Xpress-MPは‘試行モード (trial mode)’で開始され、‘試行バージョン(Trial Version) — 業務利用禁止(commercial use prohibited)’というメッセージがウィンドウ上枠に提示される。ソフトウェアはこのモードで実行 可能ではあるが、問題の大きさや複雑さに関しては制限が加えられている。Xpress-MPのソフトウェ アライセンスがあるにもかかわらず、このような状況が生じた場合には、‘readme’ファイルに安全シ ステムのセットアップの詳細が記されているので、参照されたい。 簡単なモデル入門 11 プロジェクトの関連作業 IVEを用いることによってモデルが作成され、projectとして構築される。IVEプロジェクトには一 つのモデルに対してファイルを何個でも含むことができるので、計算すべき対象を次々と設定する ことができる。画面の左端にあるProject Files枠には 利用をさらに容易にするために、デスクトップにIVEへのショートカットを追加することができる。You may wish to add a という表示があり、開発中のプロジェクトと関連する ファイルが示される。新しいモデルに入る場合に最初になすべきことは、そのモデルを含むプロジ ェクトを作ることである。 shortcut to IVE to your desktop for easier access.” 新しいXpress-IVE 2 演習問題 本章で用いる、‘essentials’ というプロジェクトを作成せよ。プロジェクトにはどのような種類の ファイルを含む必要があるか? Project Files枠は新しいプロジェクトを示すために更新される。プロジェクトは作成された入力フ ァイルとモデルをコンパイル、実行して得られた出力ファイルの両方を含むことができる。モデル プログラムファイルとモデルに関連した外部データを含む任意の個数のファイルがプロジェクトに 追加されるが、出力ファイルにはバイナリーモデルと行列ファイルを含むことも可能である。ここ では単にプロジェクトにモデルプログラムファイルを追加してみる。 演習問題 ‘simple.mos’という名前の新しいファイルMosel fileを作成し、現在のプロジェクトである ‘essentials’と結合させよ。 Project Files枠は、新しいファイルの‘simple.mos’がプロジェクトの一部であって、新しいウィン ドウでブランクファイルが開き、モデルに入ることを示している。 新しいプロジェクトの作成 IVEメニューバーからProjectとNew Projectを選ぶ。‘Browse’ボタンを用いてプロジェクトの親ディ レクトリーを選び、プロジェクトディレクトリーに名前を入れる。このオプションが選ばれている 場合には、プロジェクトに対するすべてのファイルを含むような新しいディレクトリーが作成され る。‘OK’をクリックして進む。 新しいモデルプログラムファイルの追加 IVEメニューバーからFile, New と Blank Fileを選ぶ。ダイアログボックスが現れ、ファイル名と 型を特定した後、ファイルを現在のプロジェクトに加える。 中央のエディターウィンドウが現れない場合は、Output/Input 枠の端をドラッグする。 モデルエディター IVEはモデルファイルを作成し、変更するためのエディター機能を備えている。ASCIIファイルを見 たり、編集したり、保存したりすることができるが、モデルの構造を認識し、いろいろな構成要素 を区別したりすることが可能であるという点が有用である。この特性によってデバッグに要する時 間をかなり節約することができ、さらにほとんどのタイプミス的なエラーを発見し、修正すること ができる。 演習問題 リスト 2.1にあるモデルを1行づつモデルエディターに入力せよ。 リスト 2.1の簡単なモデルには2個の決定変数(decision variables)と2本の制約条件 (constraints)がある。問題は利益を表す目的関数(objective function)を最大化することである。 ここではIVEを利用することに主眼を置いているので、当面、モデルの意味や構成要素について論じ ることは控えよう。5章において、Mosel言語について述べることにする。 12 リスト 2.1 簡単なモデル model simple uses "mmxprs" declarations a: mpvar b: mpvar end-declarations first:= 3*a + 2*b <= 400 second:= a + 3*b <= 200 profit:= a + 2*b maximize(profit) writeln("Profit is ", getobjval) end-model Xpress-MP Essentials Entering a Simple Model 13 Xpress-IVE 2 モデルが入力されると、model, uses, declarationsなどのキーワードが認識され、青色に変わる。 キーワードはモデルプログラムのいろいろな構成要素に対する指標となるので、正確に入力しなけ ればならない。キーワードは、Ctrlキーとスペースキーを同時に押すと、エディターによって選択 され、メニューを用いてモデルの中に直接入力されるので、非常に便利である。 演習問題 モデルの中のdeclarationsブロックを削除し、カーソルを元の位置に置き、Ctrl-Space キーを押して、リストから‘declarations@@end-declarations’を選べ。モデルがリスト2.1のように なることを確認せよ。 IVEモデルはテキスト探索機能を備えているが、これは長いファイルを編集する際に特に有用である。 双眼鏡のアイコンの隣のテキストボックスにある単語やフレーズを入力してリターンキーを押すと、 その単語やフレーズの最初の例が表示される。リターンキーを再度押すと、ファイルに例がなくな るまで次へ進むことができ、最終的には最初の提示に戻る。 演習問題 IVEモデルエディターのテキスト探索機能を用いて、ファイル‘simple.mos’の中にある単 語‘profit’のすべての例を示せ。 コンパイルと読み込みと実行 ここで作成したファイルは、model programとして表示される。これはモデル様式についての記述を 含むだけでなく、実行のための指令についての記述も含んでいる。IVEはコンパイルによってこのよ うなファイルを処理し、コンパイルファイルを読み込み、最終的にそれを実行する。モデルプログ ラムファイルが変更されるたびに新たなモデルを解くためのプロセスを処理しなければならない。 これらのステップについて順次考えてみよう。 モデルプログラムファイルをコンパイルする IVE メニューバーからBuild とCompile を選ぶ。現在モデルエディターで動いているファイルがコ ンパイルされる。 "Keywords…" "Text searches…" 2 Xpress-IVE 13 モデルプログラムファイルがコンパイルされると、コンパイルのプロセスについての情報が作業ス ペースの最下部にあるBuild枠の中に示される。この枠は、コンパイルが開始されると自動的に表示 される。モデルの中に文法エラーが見つかると、エラーが見つかった行と文字の位置についての詳 細と、可能な場合には問題についての記述もここに表示される。 警告のメッセージも表示され、 Eばかりでなく、Wの文字が頭に付けられている。エラーが存在しない場合には、Build枠の中に ‘Compilation successful’が示され、コンパイル済みのバイナリーファイル、すなわちBIMファイル が作成され、プロジェクトに付加される。 演習問題 リスト 2.1にあるモデルプログラムをコンパイルせよ。エラーが見つかった場合には、 リストを注意深くチェックし、問題を修正せよ。バイナリーモデルファイルがプロジェクトに付加 されるのをチェックせよ。 問題の解を求めるために、コンパイル済みのBIMファイルが読み込まれ、実行される。 演習問題 ‘simple.bim’ファイルを読み込み、問題を解け。このモデルで得られる利益の最大値を求 めよ。 モデルプログラムが実行されると、作業スペースウィンドウの右側のOutput/Input枠が選ばれ、プ ログラムの出力が示される。モデルの出力はすべてこのウィンドウに送られ目的関数値が示される。 BIM ファイルを読み込んで実行する メニューバーからBuildとRunを選ぶ。現在のモデルに対するBIMファイルが自動的に読み込まれ、実 行される。 Xpress-IVE 2 Xpress-IVEをもっと知るために モデルの中で得られる最大利益を知るということは確かに有用であるが、それを達成するためには、 決定変数はどのような値をとるべきであろうか。IVEは左側ウィンドウにあるEntities枠を通して解 に関するすべての情報を得ることができる。この枠にある決定変数のリストを拡げ、マウスポイン ターを操作すると、解とリデューストコストが提示される。制約条件に対する双対値とスラック値 も得られる。さらにEntitiesにある変数あるいは制約条件をクリックすると、モデルの中のあらゆ る状況を説明するすべての行がマーカーで示され、作業スペースの最下部にあるLocations枠の中に 記述されている。マーカーはIVEツールバー上の‘Clear bookmarks’アイコンを用いて次々と除去する ことができる。 演習問題 タブをクリックしてEntities枠を提示せよ。最大利益を達成するために決定変数(aとb) はどのような値をとらなければならないか。変数aをクリックして、最終的にマーカーを除去する前 にこの変数を含むモデルのすべての行を示せ。 IVEは、解がどのようにして得られるかをグラフ的に表示するが、これは問題が最適化されるたびに 標準的に行われる。右側ウィンドウには、解くべき問題の型と使用される特定のアルゴリズムによ って、これらについて説明するいくつかの枠が含まれている。考察対象の問題については、 Sim:Obj(iter)枠の中に単体法が用いられる場合の反復途中段階のいくつかの目的関数値が示され る。 演習問題 Sim:Obj(iter)枠を示し、その上にあるグラフの全貌と情報を見るために、必要な場合に ははウィンドウの大きさを変更せよ。マウスポインターをグラフの別の領域に移動させ、各反復に おける反復回数と目的関数値がグラフの上に提示されていることを確認せよ。 この場合には、グラフの上に初期点と最終反復値のみが提示され、それ以外の情報は存在しない。 14 現在、これを制御するパラメタを変更することによって、反復途中の状態を見ることができる。 解の詳細は、単に"Solution graphs…"というモデルファイルの要素を操作することによっても見ることができ る。 2 Xpress-IVE Xpress-IVEをさらに利用するために 制御オプションの設定 Mosel モデルプログラミング言語を用いると、Xpress-Optimizerのコントロールパラメタの値を設 定することによって、解のプロセスに影響を与えるようないくつかの可能性を探ることができる。 このような可能性はプログラミング言語によって直接提供されるものであるが、オプションがIVE によって得られる出力に影響を与えることを除けば、IVEは他との重複機能を有していない。Options の後にBuildを選択すると、これらの可能性の全リストが見られる。おそらく最も興味があるのは、 最適化の途中で解の反復についてどれだけ詳細な情報が必要かということについての指令を与える LPLOGコントロールであろう。特に、Optimizerでは初期値、最終値、そしてあらゆるLPLOG反復につ いての情報が出力される。標準的にLPLOG反復回数を100に設定すると、Moselの設定は最初のオプシ ョンに対する単体法によって書き換えられる。 Sim:Obj(iter)枠の中に作られたグラフ上に反復回 数をプロットするのはこのオプションである。 演習問題 ‘Ignore Mosel settings’の箱をチェックし、LPLOG反復回数を1に設定し、‘Apply’をクリ ックし、問題‘simple’を再最適化せよ。グラフから、アルゴリズムの最初の反復に対する目的関数値 を求めよ。 ここで紹介するような小規模の問題について考慮することは興味あることであるが、大規模な問題 に対して、出力を各反復回数示す場合、Optimizerの性能はかなり落ちる。このような場合は、LPLOG を高い値に設定しておくのが望ましい。解のグラフ表示に関心がない場合には、グラフオプション を不可にして、無駄な手順を費やすことなく最適化操作を実行することが望ましい。これを実現す るには、同様のコントロールオプションメニューを利用すればよい。 多重モデルの実行 IVE には一度に一つのプロジェクトしか入力できないが、プロジェクトはいくつかのモデルファイ ルを有しており、これらのファイルはメモリーの中に保存され、同時に実行される。エディターウ ィンドウの中に示されているモデルは現問題active problemと呼ばれるが、開いているモデルの中 からいずれかを選ぶことによっていずれかがコンパイルされ、必要に応じて実行される。これを提 示するためには、リスト2.2に示すような他のモデルが必要となる。 演習問題 プロジェクト‘essentials’に2番目のモデルファイル‘altered.mos’を追加し、リスト2.2 のモデルに入力せよ。 モデルがIVE の中で編集されると、現問題となる。Buildメニューからコンパイル(Compile) あるい は 実行(Run)のオプションが呼ばれると、変換される現問題となる。 リスト2.2 変換モデル model altered uses "mmxprs" declarations a: mpvar b: mpvar end-declarations 15 first:= 3*a + 2*b <= 400 second:= a + 3*b <= 200 third:= 6*a + 5*b <= 800 profit:= a + 2*b maximize(profit) writeln("Profit is ", getobjval) writeln(" a = ", getsol(a), "; b = ", getsol(b)) end-model これとリスト2.1のモデル上の唯一の変化は、新しい制約を追加した点である。この制約条件が等号で成立する場合には、目 的関数値が減少することが期待される。 2 Xpress-IVE 演習問題 新しいモデル ‘altered’をコンパイルせよ。現在得られる最大利益はどれだけか。これを プロジェクト‘simple’の利益と比較せよ。 左側ウィンドウにあるProject Files枠が現在のプロジェクトのすべてのファイルのリストを示し ていることはすでに見た。すべてのオープンファイルのリストはこのウィンドウのすぐ上にボタン の形で示されており、ファイル名のラベルが付けられている。これらのボタンをクリックすると、 関連のファイルがエディターウィンドウに示され、問題が操作可能となる。 問題の変更はこのようにして可能となるが、Entities枠にある情報はモデルのコンパイルが実行さ れたときに更新されるということに注意されたい。このため、Entities枠にある解の情報が必ずし も現在の問題の状況を表しているとは限らないので、いくつかのモデルを同時に動かす場合には、 このことに注意されたい。 演習問題 エディターウィンドウにあるモデル‘simple’を再提示するためにボタンをクリックせよ。 Entities枠にあるすべての情報は問題‘altered’に関連があることを確認せよ。 プロジェクトの中の他のファイルを開くためには、Project Files枠内のファイルを右マウスボタン をクリックし、下に示されたメニューからOpen/Show fileを選べばよい。開いたファイルを閉じる には、同じメニューからClose fileを選べばよい。ファイルをプロジェクトから削除するには、 Remove from projectを用いればよい。 演習問題 エディターウィンドウにあるファイル‘altered.mos’を閉じ、現在のプロジェクトからこ のファイルと‘altered.bim’を削除せよ。Entities枠にある情報を更新するために、‘simple’を再実行 せよ。 Xpress-IVE 2 行列ファイルの書き方 最適化プロセスにおいて、IVEによって提供されている以上に制御可能なモデルを構築したくなるこ とがあるかもしれない。あるいはまたモデルを他人に利用して欲しい場合もあるかもしれない。こ のような場合には、出力は最終段階で利用されるソルバープログラムにおいて解読可能なように共 通の形式で記録されることが必要である。IVEによって、2種類の主要な業界標準形式である LPフ ァイルとMPSファイルという行列ファイルを作ることが可能となる。 演習問題 モデルプログラム‘simple.mos’からMPS行列ファイル‘simple.mat’を作成し、モデルファイ ル‘simple.mos’を閉じよ。 MPS形式と異なって、LPファイルにはモデルが最大化問題あるいは最小化問題のいずれを扱う場合に 対しても追加的な情報が含まれている。ファイルをこの形式で出力する場合には、最適化の意味を 特定化することが重要である。 16 演習問題 File, Openを用いて、IVEモデルエディターによる新たな行列ファイルを観察せよ。指示 がある場合には、これを自身のプロジェクトに追加せよ。 問題行列の出力方法 メニューバーからExport matrixにしたがってBuildを選択し、MPS行列ファイルか最大化LPあるいは 最小化LPのいずれかを選べ。行列ファイルを作成する前に、どの制約が目的関数を表すか設定せよ。 この操作の前に、モデルがコンパイルされ、実行されていることを確かめよ。 2 Xpress-IVE 行列ファイルによる問題の入力 行列ファイルとして利用可能な問題を入力し、解くにはXpress-IVEを利用することができる。 Xpress-IVEはIVEの中からXpress-Optimizerに対する直接のインターフェイスを与える。IVEはMosel を用いてモデルを構築する場合のグラフ的なインターフェイスとして有力なものであるが、たとえ ば利用者が解のグラフ表示能力に関心を持っている場合のように、IVEをこのように利用することが 望ましいこともある。 演習問題 上記のようにして作成した行列ファイル‘simple.mat’を入力し、整数制御パラメタLPLOG の値を1に設定し、LP問題を最大化せよ。 問題に対する解が求まると、Output/Input枠に問題の統計量が示される。これらの統計量は見過ご されることもあるが、実際に入力された行列がわれわれが解こうとしている問題に対応しているか 否かをチェックする場合に有用である。この場合、問題の行列は3つの行(2本の制約条件と目的 関数に対応する)と2つの(構造的)列、換言すると決定変数、を有する。また行列は、a と bが すべての行に非零係数を有しているので、6つの非零要素を持つ。これは(混合型)整数計画問題 ではないので、問題の中にはグローバル要素も集合も集合要素も存在しない。このようにして初期、 最終段階、そして各反復回数LPLOGに対する目的関数値が解の状況とともに示される。 問題の統計量が問題‘simple’に対応することを確認せよ。このことから、あるいは Sim:Obj(iter)枠から最適な目的関数値が得られていることを確認せよ。 演習問題 行列問題による問題の入力 IVEメニューバーからOptimize matrix fileにしたがってBuildを用いて、行列ファイルが利用でき るようにせよ。エディターウィンドウがすでに開いていれば、ファイルは自動的に選択される。ア ルゴリズムと内容と問題の型を設定し、解き始めるために‘Start’をクリックせよ。LPLOGのような最 適化制御変数も問題の統計量の中で設定される。 Xpress-IVE 2 関数のグラフ化 最適化過程におけるすべての反復回数に対して、LPLOGの値による制御によって、IVEが目的関数値 をグラフ的に表示できることをすでに示した。しかしながらIVEにおいてもMoselプログラムの中で コマンドIVEinitgraph と IVEaddtographを用いることによって利用者自身の関数をグラフ表示す ることが可能となっている。この場合に出力は右側ウィンドウのUser Graph枠から見ることができ る。リスト2.3に例を示す。 演習問題 ‘decay.mos’と呼ばれる新しいファイルを作成し、リスト2.3を入れよ。User Graph枠を選 17 択してモデルを実行し、関数を観察せよ。 IVEinitgraph IVEのグラフ化機能を初期化する。2つの引数はx y軸のそれぞれに対する名称である。 IVEaddtograph 利用者のグラフにプロットする。2つの引数はカルテシアン座標軸に対応する点の値である。 リスト 2.3 指数的崩壊状況のグラフ化 model decay uses "mmive"; IVEinitgraph("Decaying oscillator","x","y") forall(i in 1..400) do IVEaddtograph(i,exp(-0.01*i)*cos(i*0.1)) end-do end-model 2 Xpress-IVE 環境の変更 IVE環境の多くの側面はカスタム化可能で、利用者が必要としない場合には容易に変更することがで きる。作業領域の中にあるウィンドウの配置や視度ばかりでなく、フォントや色彩や大きさといっ た特性もすべて特定のプロジェクトに対して変更可能なように設定される。標準的な設定は新たな プロジェクトが作られ、呼び出されるたびに適用される。 IVE作業領域の中にウィンドウを配置するのは、ウィンドウの境界を新しい位置にドラッグするのと 同じくらいに容易である。視度はViewメニューからProject Bar, Info Bar or Run Barのいずれか を選択したり解除したりすることによって変更可能である。 演習問題 Viewメニューにあるいろいろなオプションに対してoff とonのを繰り返してそれぞれ実 験を試みよ。作業が容易になるようにウィンドウの大きさを変えよ。 プロジェクトに対する他の設定はModel Editorウィンドウにある右マウスボタンをクリックし、 Propertiesを選ぶことによってアクセス可能となる。提示される対話ボックスには、Color/Font, Language/Tabs, Keyboard、あるいはその他のいろいろな設定を変更するためのタブが装備されてい る。変更を指示し、‘Apply’ あるいは ‘OK’ ボタンをクリックすると、ウィンドウあるいはフォント の情報が更新される。そしてプロジェクトあるいはアプリケーションを閉じると、これらは保存さ れる。 演習問題 現在の設定を見た上で、エディターで用いられる色を変更してキーワードを示せ。‘Apply’ ボタンをクリックして変更を有効にせよ。 IVEモデルのエディターは、Language/Tabs枠にある言語を適切に設定することによって他の型のフ ァイルを編集するのに利用可能となる。C/C++, Basic, Javaあるいは多くの他の型のファイルを編 集するにはオプションも存在する。長いモデルを編集するには、十進表示の行番号がMisc枠からフ ァイルに対して適用される。 Xpress-IVE 2 ヘルプの利用 これまではXpress-IVEにあるプロジェクトをどのようにして作成し、管理するか、そしてModel 18 Editorを用いて単純なモデルをどのようにして入力し、変更し、保存するか、さらには解を得るた めにこれらのモデルをどのようにしてコンパイルし、読み込み、実行するかを学んだ。またいくつ かのモデルを同時に動かし、問題を行列ファイルとして転送し、最適化するにはどうすればよいか、 あるいはIVE作業領域をどのようにしてカストマイズするかについても学んだ。これまでは単にIVE 環境をどのように利用するかについてのみ集中的に述べてきた。しかしながら5章においては、 Mosel計画言語の基本について説明し、利用者の問題をどのようにしてモデル化し、解くかについて 述べる。次の項目に注目したいかもしれない。そこで以下の2章は、Console XpressとXpress-MP ライブラリーを用いる場合の情報を説明する。IVEだけを利用したい場合には、以下の2章は飛ばし てもよい。Mosel か Optimizerのいずれかを利用する場合のヘルプが必要な場合は、それぞれMosel かOptimizer Reference Manualsのいずれかから入手可能である。 まとめ 本章では、IVEにあるプロジェクトをどのようにして作成し、管理するか、そして内蔵のModel Editor を用いてモデルをどのようにして作成するか、あるいはモデルプログラムをどのようにしてコンパ イルし、読み込み、実行するか、あるいは問題を行列ファイルとしてどのようにして転送したり、 入力したり、解いたりするか、あるいはまたIVE環境をプロジェクトとしてどのようにカストマイズ するかについても学んだ。 2 Xpress-IVE Console 第3章 Console Xpress 概要 この章では、以下の項目について説明する。 • • • • Xpress-Moselを用いてモデルプログラムファイルをコンパイルし、読み込み、実行する; 線形あるいは整数計画問題を解き、解を得る; Xpress-Optimizerを用いて問題を解く; 最適化操作をするために、制御を設定し解法を選択する. 始める前に Console XpressはXpress-MPソフトウェアに対して強力なテキストベースのインターフェイスを提 供するため、すべてのプラットフォームの利用者はモデルを対話型式あるいはバッチモードで利用 できる。読者がこの章を読む場合には、すでにConsole Xpressがインストールされていることが前 提となっている。この章を読んでいくと、単純なモデルの入力方法と解法が分かるであろうし、ま たConsoleインターフェイスの主要な特性についても説明がなされている。Xpress-MP ライブラリー の利用者はこれらの情報が有用であることに気づくであろう。 3 Console Xpress Console Xpress−の構成要素 Console XpressインターフェイスはXpress-Mosel と Xpress-Optimizerという2つの主要な構成要 素からなる。最初のXpress-Mosel は、Moselモデルプログラミング言語で書かれたモデル様式プロ 19 グラムがコンパイルされ、実行されるように高度に柔軟性のある環境を構成している。行列出力フ ァイルはいろいろな業界標準形式で作成され、解を求めて出力するためにOptimizerの中に次々と読 み込まれることになる。他方、通常はOptimizerの機能はMoselの中にライブラリーモジュールとし て組み込まれ、利用者がMosel自体の中で問題を解くことが可能となっている。これらの要素はいず れも自動的に問題を解くようバッチモードで利用できるのが非常に有用である。 これらの要素の一つを実行するのはコマンドとして moselあるいは optimizerと入力するのと同じ くらい簡単である。まず最初にこれらの要素が正しくインストールされているかをチェックするた めにテストをしてみよう。 Xpress-Moselの検査方法 Moselが正しくインストールされているか否かをチェックするために、シェルコマンドでmoselと入 力してリターンキーを押す。プログラムは短い標示を出し、続いてMosel記号>が示され、利用者の 入力を待つことになる。 演習問題 上に示したMoselを開始せよ。Mosel記号でquitと入力すると、シェルコマンド記号に戻 るはずである。 リスト3.1は上記の手順を示しており、利用者からの入力がボールド文字で点滅する。上記の手順を 実行してもヘッダー情報が現れない場合は、mosel とoptimizerの2つの要素は現在のパスに存在し ていないことになる。2つの要素を含むディレクトリーに変更して、そこから再度実行せよ。うま くいった場合には、それに従ってパスを更新せよ。 Console すべてのXpress-MP製品には、ソフトウェアの重要部分として安全保護システムが装備されている。 これはインストール時にすべての機能が立ち上がるように設定されなければならない。安全保護シ ステムが正しく設定されないと、Xpress-MPの構成要素は‘trial mode’で開始されるため、取り扱う ことのできる問題の大きさや複雑さについて制約を受けることになる。この場合、利用者がConsole Xpressのライセンスを与えられているならば、安全保護システムを正しく設定するための詳細につ いては‘readme’ファイルを参照されたい。 Xpress-Optimizerの検査方法 Optimizerの操作がうまくいっているか否かは、Moselと同様にコマンド記号optimizerを実行するこ とによってチェックできる。プログラムは、インストールされているOptimizerのバージョンを与え るヘッダーに対応して問題名problem nameを入力する。問題名が空白でリターンキーを押すと、 Optimizerでは作成するファイル名として問題名‘$$$$$$$$’が現れる。最後に、Optimizerのコマンド 記号>が現れ、利用者の入力待ちとなる。 演習問題 インストールがうまくいっているか否かをチェックするためにOptimizerを実行せよ。問 題名を聞かれたら、単にReturnを入力せよ。Optimizerにおいてプログラムを終了するには、QUIT を入力せよ。 リスト3.2はOptimizerに対するリスト3.1と同一であって、利用者の入力部分はボールドタイプで示 されている。 リスト 3.1 Console Xpress-Moselをテストする C:\> mosel ** Xpress-Mosel ** (c) Copyright Dash Associates 1998-zzzz > quit 20 Exiting. C:\> 問題名problem nameはOptimizer あるいは Moselによって作成された任意のファイルの基本となる。 3 Console Xpress Xpress-Moselを用いて簡単な問題を解く Mosel ファイル Mosel と Optimizerが正しく動いているならば、モデルを入力して解くのためにConsole Xpressを 用いることができる。この章の大半はリスト3.3に示した簡単なモデルについて述べている。モデル は2つの決定変数decision variables (aとb)、そして2本の制約条件constraints (first と second)を有し、目的は目的関数objective functionである profitを最大化することである。 当面の間、われわれはMosel と Optimizerを 用いる ことに焦点を絞っているので、モデルやそ の構成要素については論じない。5章では、Mosel モデルプログラミング言語に焦点を絞ることに する。 このような問題は、リスト3.3に示したような形式のモデル入力ファイルとしてMoselに提示される。 これらはASCIIテキストファイルであって、Moselが実行しようとしている処理情報に加えてモデル の構文についても説明している。標準的には、Moselファイルは拡張子.mosを有しているので、ここ ではこれを採用する。 リスト 3.2 Console Xpress-Optimizerをテストする C:\> optimizer XPRESS-MP Integer Barrier Optimizer Release xx.yy (c) Copyright Dash Associates 1984-zzzz Enter problem name > Default problem name of $$$$$$$$ assumed > QUIT C:\> Console Xpress 演習問題 存せよ。 テキストエディターを用いて、リスト3.3のモデルプログラムをファイルsimple.mosに保 Moselの3つの柱 Mosel で問題を解く場合、以下に示す3つの段階がある。まず第一にモデルプログラムがコンパイ ルされ、次にコンパイルされたプログラムはMoselに読み込まれ、最後に実行されるということであ る。Moselがこの手続きを実行するのに必要なコマンドは、以下の通りである。 リスト 3.3 簡単なモデル model simple uses "mmxprs" declarations a: mpvar b: mpvar end-declarations first:= 3*a + 2*b <= 400 21 second:= a + 3*b <= 200 profit:= a + 2*b maximize(profit) writeln("Profit is ", getobjval) end-model compile この手続きによってモデルプログラムがコンパイルされるが、必要なのはファイル名だけである。 拡張子がない場合は.mosが付けられる。 モデルを入力するのにワードプロセッサーを用いる場合は、ファイルを"text only"形式で保存せよ。-g フラッグを用いる と、エラーがあった場合に追加情報が得られる。 Xpress 演習問題 Moselを立ち上げ、モデルプログラムsimple.mosをコンパイルし、読み込み、実行せよ。 Moselを止めないと、どうなるかを確かめよ。 上述の手順を説明する部分はリスト 3.4に与えられている。利用者の入力部分はボールドタイプで 示されている。この手順の第一段階において、ファイルsimple.mosに保存されているモデルプログ ラムはバイナリーモデルのBIMファイルとしてコンパイルされる。モデルの中に文法エラーが見つか った場合には、エラー情報がコンソールに示され、エラーが見つかった行と文字の位置についての 詳細が提示される。警告メッセージもこのようにして示されるが、前にEでなくWが付けられる。コ ンパイル中に-g フラッグを用いると、範囲エラーのような発見の難しい問題を見つけるのに役立つ、 追加的な情報が得られる。エラーが見つからない場合は、コンパイルされたモデルのファイルは次々 とMoselに読み込まれる。 load コンパイルされたモデルのファイルはコマンドloadを用いてMoselに読み込まれる。 run これによって現在のプログラムが実行される。引数はすべてパラメタの初期値となる。 リスト 3.4 モデルプログラムのコンパイルと読み込みと実行 C:\Mosel Files>mosel ** Xpress-Mosel ** (c) Copyright Dash Associates 1998-zzzz >compile simple Compiling `simple'... >load simple >run Profit is 171.429 Returned value: 0 > このようにしてパラメタを設定する例は5章に示される。 "Compilation errors…" Console コンパイルと読み込みという手順は非常に頻繁に行われるので、Moselではこれらを一緒にした短縮 コマンドが用いられる。最終段階では、コンパイルされたプログラムは実行され、目的関数は最大 化される。この最適値はコンソールに出力される。 Moselからさらに情報を得る 最大利益の値をモデルから得ることは確かに有用であるが、それはどのようにして達成されるので あろうか?あるいは決定変数がどのような値をとれば、最大利益の値が得られるのだろうか?この 22 ような情報は、モデルをa と bの値を画面に表示するように変更することによって得られるが、問 題の規模が大きく、解くのに時間がかかる場合には、問題を再度コンパイルして解くということは 賢明ではない。幸いなことに、Moselではdisplayコマンドを利用することによって、変数の値や制 約条件を知ることが可能である。 Xpress 演習問題 displayコマンドを利用して、決定変数の最適値を求めよ。これらがa = 114.286; b = 28.571となることを確かめよ。 cload (cl) これはモデルプログラムをコンパイルし、コンパイルされたバイナリーモデルのファイルを読み込 む。短縮すると、clと書くことができるが、唯一の引数はモデルプログラムのファイル名である。 display これによってMoselは引数の値を示す。 Moselのコマンドは、他のコマンドと重ならない限り、通常2文字に短縮される。詳細については、Mosel Referenceのマニ ュアルを参照されたい。 3 Console Xpress Moselをさらに用いる バッチモードで実行する Console Xpressの主要な利用方法の一つは、バッチ生産システムにおいて多くの問題を日常的に解 くことにある。これまではMoselを対話型で利用することについてのみ詳細を記述したが、その利用 方法は-sフラッグを用いることに限定されることなく、Moselはコマンドラインから非対話型で実行 されることである。この例はリスト3.5に与えられているが、そこでは前のモデルファイルがコンパ イルされ、読み込まれ、実行される。 Moselでは、-cフラッグに続いて、引用符に囲まれ、セミコロン(;)で区切られたコマンドが記述さ れる。-sフラッグを追加すると、Moselは静かに実行され、モデルファイルで要求された情報だけを 出力する。 演習問題 Moselをバッチモードで-sフラッグを用いたり、用いなかったりして実行せよ。これらの 各場合に対する出力の違いを確かめよ。 リスト 3.5 Moselをバッチモードで実行する C:\Mosel Files>mosel -c "cload simple; run" ** Xpress-Mosel ** (c) Copyright Dash Associates 1998-zzzz >cload simple Compiling `simple'... > run Profit is 171.429 Returned value: 0 > Exiting. C:\Mosel Files> Moselフラッグの全リストは、mosel –hとコマンド入力することによって得られる。 多重 Modelの実行 Moselのもう一つの特徴は、いくつかの異なるモデルをメモリーに入れ、同時に実行することである。 23 これらの一つが現問題active problemとして指定され、それぞれが必要に応じて実行されることに なる。これを示すために、リスト3.6に示したような別のモデルを紹介する。 Xpress 演習問題 テンプレートとしてsimple.mosを用いて、リスト3.6のテキストを含む新たなファイル altered.mosを作成せよ。 モデルがMoselに読み込まれると、現問題となる。runコマンドが呼ばれると、それが実行対象とな る現問題となり、displayコマンドが呼ばれると、現問題に対する情報が出力される。 リスト 3.6 モデルの変更 model altered uses "mmxprs" declarations a: mpvar b: mpvar end-declarations first:= 3*a + 2*b <= 400 second:= a + 3*b <= 200 third:= 6*a + 5*b <= 800 profit:= a + 2*b maximize(profit) writeln("Profit is ", getobjval) writeln(" a = ", getsol(a), "; b = ", getsol(b)) end-model このモデルとリスト3.3の間の唯一の変更点は、新たな制約条件を追加したことである。この制約条件が有効になると、目的 関数値は減少する。 演習問題 Moselを立ち上げ、まずsimple.bimを読み込み(すでにコンパイルされてなければならな い)、コンパイルし、新しい問題‘altered’として読み込め。runを用いて、変更された問題に対する 解を求めよ。 読み込まれた問題の全リストは、Moselにおけるコマンドlistを入力することによって得られる。こ こでは2つの問題‘simple’ と ‘altered’についての詳細が示される。この場合、現問題‘altered’のフ ァイル名の左側に星印が示される。現問題はコマンドselectを入力することによって変更されるが、 これはリスト3.7に示される。 演習問題 Moselに現在読み込まれている問題のリストをとり、現問題を‘simple’とせよ。runを入力 すると、解かれるのはこの問題である。 load あるいは cloadを用いると、Moselにさらにモデルを読み込むことができる。モデルをMosel から削除するには、deleteを用いる。 リスト 3.7 Moselにある現問題を観察し、設定する >list * name: altered number: 2 size: 3832 Sys. com.: `altered.mos' User com.: - name: simple number: 1 size: 3176 Sys. com.: `simple.mos' User com.: >select simple 24 Model 1 selected. >list - name: altered number: 2 size: 3832 Sys. com.: `altered.mos' User com.: * name: simple number: 1 size: 3176 Sys. com.: `simple.mos' User com.: > Console deleteコマンドはモデルをMosel から除くだけで、モデルファイルを削除するものではない。 演習問題 delete alteredコマンドを用いて新しい問題をMoselから削除せよ。listコマンドを用い て、‘simple’のみが残っているのを確かめよ。 行列ファイルの書き方 Moselでは、モデルを解いたり、行列を次々に変更したり、利用者との対話が必要になったりするこ とがよくある。他方、同じ問題を他人が利用することもあるであろう。このような場合、問題が別 のプログラムに対する入力となるように、モデルの行列ファイルがMoselから要求される。Moselで はexportprobコマンドを用いてMPS とLPの両方の形式で行列ファイルを書くことをサポートしてい る。 list 現在Moselに読み込まれている問題のリストを与える。現問題には星印が付けられている。 select 新しい問題が現問題として選択される。引数は現問題として設定するための名前と個数である。 delete 問題をMoselから削除する。 exportprob 単独で用いられ、行列をLP形式でコンソールにプリントする。-mフラッグを用いると、MPS形式の出 力となる。ファイル名を引数にすると、行列がそのファイルに転送される。 exportprob はモデル内部からも利用できる。例として、リスト3.11を参照されたい。 演習問題 exportprob -m simple.matを用いて、MPS形式の問題‘simple’ の行列ファイルを作成せよ。 ファイルをテキストエディターを用いて観察せよ。 MPS形式と異なって、LP形式もまたモデルが最大化問題か最小化問題かについての情報を含んでいる。 標準的には、特に断らなければ、目的関数は最小化される。この問題をLP形式で転送するには、-p フラッグを用いて目的関数が最大化であることを表示しなければならない。 演習問題 問題‘simple’ に対する行列ファイルをLP形式でコンソールに出力せよ。行列が正しく作 成され、最大化問題となっていることを確かめよ。 Xpress-Optimizer Xpress-MP 問題の解き方 上の演習問題に対しては、モデルプログラムをMoselに入れ、問題を解くためにOptimizerをライブ 25 ラリーモジュールと呼んだ。Optimizerもまた単独のアプリケーションとして利用されるが、このよ うな利用方法について本章の残りの部分で説明する。 Optimizerは問題の行列ファイルを主要な業界標準形式であるMPS形式あるいはLP形式で受け入れる。 この章の演習問題を試みるためには、上に示したMPSファイルであるsimple.matのような行列ファイ ルが必要となる。このファイルを用いると、問題を解くために必要なことは、完成した行列を Optimizerに読み込み、目的関数を最大化することである。 Console Optimizerを用いた例はリスト3.8に与えられているが、ここでも利用者の入力はボールド表示され ている。問題名を入力すると、Mosel で作られたMPSファイルで読むためには、コマンドREADPROB が用いられ、その後MAXIMが呼び出され、リスト3.3に与えられている表示profitを有する目的関数 が最大化される。 演習問題 リスト3.8にしたがって、ファイルsimple.matをOptimizerに読み込み、目的関数を最大 化せよ。停止する前にコマンドWRITEPRTSOLを入力せよ。 解の見方 Optimizerの解の情報は、以下に述べるように、いくつかの異なる出力で表される。リスト3.8にあ るコマンドMAXIMを用いて出力を画面に出すと、コマンドWRITEPRTSOLの上の行に、問題の最適解が 見つかり、目的関数値が171.428571となることが示される。これは、行列が読み込まれた場合の出 力の中で、問題に関する統計量と共に得られる最初の出力情報となる。 問題に関する統計量は解を解釈する場合に有用なチェック要素となる。読み込まれた行列がわれわ れが解きたい問題に正しく対応しているか否かをチェックするのにこれらの統計量が用いられる。 現在の例では、リスト3.8から、行列が2本の制約条件と1本の目的関数に対応する3つの行を有す るのが分かる。さらには、2個の決定変数に対応する2つの列を有することも分かる。2つの決定 変数はすべての行に現れているので、行列の非零要素の個数が分かる。 MPS ファイルをOptimizerに読み込む optimizerを開始し、問題名problem_nameを入れる。次にOptimizerコマンドREADPROBを入れる。リ ターンを押すと、Optimizerはファイルproblem_name.matを読み込む。 目的関数を最大化する MPS ファイルが読み込まれると、ただちにOptimizerコマンドMAXIMを入力する。目的関数は最大化 される。 最小化問題の場合には、MINIM と入力する。 Xpress 最終的には、グローバル統計量から、モデルがMIP問題であることが分かるので、要素(entities) も(特別の順序付き)集合(ordered sets)も集合要素(set members)もないことが分かる。 Optimizerでは、利用者が利用可能なより詳細な出力を出している。リスト3.8では、コマンド WRITEPRTSOLを用いると、ファイルsimple.prtに出力される。同じ出力は、コマンドPRINTSOLを用い ると、画面にも送られる。 リスト 3.8 Optimizerを用いた第一講 C:\Optimizer Files> optimizer XPRESS-MP Integer Barrier Optimizer Release xx.yy (c) Copyright Dash Associates 1984-zzzz Enter problem name >simple >READPROB Reading Problem simple 26 Problem Statistics 3 ( 0 spare) rows 2 ( 0 spare) structural columns 6 ( 0 spare) non-zero elements Global Statistics 0 entities 0 sets 0 set members >MAXIM Presolved problem has: 2 rows 2 cols 4 non-zeros Crash basis containing 0 structural columns created Its Obj Value S Ninf Nneg Sum Inf Time 0 .000000 p 0 0 .000000 1 2 171.428571 P 0 0 .000000 1 Uncrunching 2 171.428571 P 0 0 .000000 1 Optimal solution found >WRITEPRTSOL >QUIT C:\Optimizer Files> Optimizerは大小文字にはよらないが、コマンドには小文字を用いる。ここではreferenceマニュアルの記述に従うため、大 文字を用いる。 ‘Global entities’ は、非連続型変数と特別の順序付き集合を記述するためにXpress-Mpでは傘付きで表示される。 Console WRITEPRTSOLの出力 WRITEPRTSOLからの出力は、プリンターへの転送が容易なように ASCII テキストファイルとなって いる。このファイルは、リスト3.9に示すように、構造を理解しやすくするために3つの部分に分け られている。 すべての解の出力 目的関数を最適化した後は、コマンドPRINTSOL を用いて出力を画面に送るか、あるいは後で解を見 るためにWRITEPRTSOLを用いて出力をASCII 解のプリントファイルproblem_name.prtに送るかのい ずれかである。 リスト 3.9コマンド WRITEPRTSOLからの出力 Problem Statistics Matrix simple (1部) Objective *OBJ* RHS *RHS* Problem has 3 rows and 2 structural columns Solution Statistics Maximization performed Optimal solution found after 2 iterations Objective function value is 171.428571 Rows Section (2部) Number Row At Value Slack Value Dual Value RHS N 1 *OBJ* BS 171.428571 -171.428571 .000000 .000000 L 2 second UL 200.000000 .000000 .571429 200.000000 L 3 first UL 400.000000 .000000 .142857 400.000000 Columns Section (3部) 27 Number Column At Value Input Cost Reduced Cost C 4 a BS 114.285714 1.000000 .000000 C 5 b BS 28.571429 2.000000 .000000 Xpress 部構成を考える場合、1部,3部,2部の順番が最も有用であろう。1部では、解法に関するまと めの統計量が与えられる。そこでは、用いられた行列(問題)名(simple)と目的関数と右辺名が与 えられる。続いて行(目的関数を含む制約条件)と列(変数)の個数、最大化問題かどうか、解く のに2回の反復が必要であったこと、最良値が171.428571であることが示されている。 決定変数の最適値は3部のColumns Sectionに与えられている。列の値Value は各変数の最適値を与 える。各変数に対する入力コストやリデューストコストなどの解に関する追加的な情報もここに与 えられる。さらに詳細については、7章の“用語のまとめ”を参照されたい。 最終節は2部の行に関するRows Sectionである。ここでは問題の各種制約条件や目的関数のリスト が"左辺値"と共に与えられる。特に目的関数値はここに再度与えられる。制約条件に対するスラッ ク値や双対値もここに示される。さらに詳細については、7章の“用語のまとめ”を参照されたい。 演習問題 上で作成した出力ファイルsimple.prtを観察し、行、列等のいろいろな部分を確認せよ。 リスト中で決定変数と目的関数に関する結果をチェックせよ。 Optimizerをさらに利用するために 最適化アルゴリズム Optimizerは、標準的には双対単体法であるが、LP問題を解くためのいろいろなアルゴリズムをサポ ートしている。しかしながら、ある種の問題に対しては、アルゴリズムを変更したために問題を解 くのに要する時間が大きく変化することもある。また主単体法かニュートンバリア法かのいずれか が短時間で解を見つけるかも知れない。ある種の状態下で最良のアルゴリズムを選択することは、 それ自体何か技術に近いようなものであるため、最良の選択は自身の問題に対していろいろなアル ゴリズムを用いて実験することによって得られるものである。 "Section 1…" "Section 3…" "Section 2…" Console 問題に対して用いられるアルゴリズムを変更するための最も簡単な方法は、MAXIMに通じるフラッグ を立てることである。これらの方法は上に述べた3つのアルゴリズムのそれぞれに対応する。リス ト3.10は、問題を解くためにニュートンバリア法を用いるためにリスト3.8からの変更を示している。 利用者の入力を含む行がここに示されている。 演習問題 3つのアルゴリズムのそれぞれを用いて問題を解き、それぞれの出力の違いを確認せよ。 整数計画法 上に考慮した問題に対して、目的関数の最大値は決定変数が小数1位まででa = 114.3、b = 28.6 のときに得られる。しかしながら、問題によっては、このような小数解は受け入れがたいものかも 知れないし、またある変数は整数値のみをとるように限定されているかも知れない。このような整 数型あるいは混合型整数計画問題に対しては -b ニュートンバリア法を用いる; -d 双対単体法を用いる; -p 主単体法を用いる. 28 リスト 3.10 ニュートンバリア法を用いる C:\Optimizer Files> optimizer Enter problem name >simple >READPROB >MAXIM -b >WRITEPRTSOL >QUIT C:\Optimizer Files> "Choosing an algorithm…" Console Xpressを用いて、このような問題を解いてみよう。 リスト3.11では、aとbが整数値をとらねばならないことを指示するために、2行が追加されている。 他の変更は、Moselではプログラムが実行されるときにMPS 行列ファイルを ‘自動的に’転送するとい うことである。 演習問題 リスト3.11の内容を含むモデルファイルintegral.mosを作成した後、実行し、MoselでMPS 行列ファイルを作成せよ。Optimizerを用いて、LP問題を解け。目的関数の最適値を求めよ。 上で解いた問題ではaとbが整数値をとらねばならないとした。さらに、目的関数はこれらの変数の 整数倍のみを含む線形表現である。しかしながら、ここで得られた目的関数の最適値が整数値でな いことに注意されたい。 Optimizerを用いて整数解を見つけるには、2段階操作が必要である。第一段階として、最適解を求 めるためには、LP緩和問題が解かれねばならない。 リスト3.11 問題に整数制約を追加する model integral declarations a: mpvar b: mpvar end-declarations first:= 3*a + 2*b <= 400 second:= a + 3*b <= 200 profit:= a + 2*b a is_integer b is_integer exportprob(EP_MPS,"integral",profit) end-model LP緩和とは、変数に関する整数条件を無視したものである。 Console 第2に、最良整数解が存在する場合に、それを見つけるためにはグローバル探索が実行されなけれ ばならない。グローバル探索は、g フラッグを MAXIMに入れるLP緩和問題の解に自動的にしたがう ことから呼ばれているが、例がリスト3.12に示されている。グローバル探索が完了すると、得られ た整数解は、PRINTSOLあるいは WRITEPRTSOLを前もって用いることによって見ることができる。 演習問題 リスト3.11の整数計画問題を解け。最適解に対するaとbの値を求めよ。(これらは整数値 でされなければならない。) 29 Optimizer の制御 Optimizerにはいくつかのパラメタが含まれているが、それらの値は利用者によりいろいろな制御設 定によって変更される。パラメタの値は、特に難しいとされる問題に対するOptimizerの性能に合わ せて、考慮の上で細かな調整がなされるため、解法に要する時間の大幅な節約になることもある。 はじめての利用者がこれらの大部分を標準値から変更することは勧めないが、実験が有用となるよ うな場合もかなりある。このような例について述べよう。すべての完全なリストは、Optimizerの Referenceマニュアルに見ることができる。 リスト 3.12 整数計画問題を解くためのコマンド READPROB MAXIM -g WRITEPRTSOL QUIT 整数解を求めるグローバル探索を開始する optimizerでコマンドMAXIM –gを入力する。問題に対する整数解を求めるために分枝限定法が用いら れる。 MAXIM -g はコマンドGLOBALに続いてただちにMAXIMを呼ぶことと同じである。 Xpress 最適化に用いられるアルゴリズムがMAXIMに対するフラッグを用いる、という標準形から変更する方 法については、前に見た。 標準的アルゴリズムは制御DEFAULTALGによって設定されるが、DEFAULTALGはどのアルゴリズムが適 用されるべきかを表す整数値である。標準的アルゴリズムはDEFAULTALGを用いて変更されるため、 p, d と b のフラッグを MAXIM (MINIM)に入れる必要はない。 演習問題 リスト3.13にあるように現在値を求めるためにoptimizerコマンドで制御DEFAULTALGを 入力せよ。 演習問題 ニュートンバリア法を用いるために、制御DEFAULTALGの値を4に設定せよ。これは変数の 値によって影響を受けるMAXIMを呼ぶ前に実行されなければならない。再度プログラムを実行し、出 力を観察せよ。 制御値を得る Optimizerに対する制御値は、optimizerにおいて名前を入力することによって得られる。 ¾ CONTROL 制御値を変更する Optimizerに対する制御は、標準的なプログラミング変数として扱われ、変更は値の割当によってな される。 ¾ CONTROL = value 標準的最適化アルゴリズムの変更 制御変数DEFAULTALG の変更: 1 アルゴリズムは自動的に決定される; 2 双対単体法を用いる; 30 3 主単体法を用いる; 4 ニュートンバリア法を用いる. "Changing the algorithm used by default…" Console ヘルプの利用 本章におけるここまでの説明から、Moselを用いてモデルプログラムをコンパイルし、読み込み、実 行する方法、ライブラリーモジュールとしてのOptimizerを用いて問題に対する解を求める方法、そ して問題を行列ファイルに転送する方法について学んだ。さらには、これらのファイルをOptimizer に読み込む方法、それらを最適化する方法、解を見る方法、最適化手法に影響を与える制御を変更 する方法、等についても学んだ。これまでのところでは、特定の既存のモデルを入力し、最適化す る方法に集中してきた。しかしながら5章では、Moselモデルプログラミング言語の基本について説 明し、利用者がモデルを作って問題を解けるようにする。次の章では、Xpress-MP ライブラリーを 利用する場合の情報を与えるので、Xpress-IVE あるいはConsole Xpressだけを使いたい読者は読み 飛ばしてもよい。Mosel かまたは Optimizerを利用する場合に追加的にヘルプを必要とするならば、 それぞれMosel かまたはOptimizer Reference Manualsを参照されたい。 リスト 3.13 制御値を得る C:\Optimizer Files> optimizer XPRESS-MP Integer Barrier Optimizer Release xx.yy (c) Copyright Dash Associates 1984-zzzz Enter problem name >simple >READPROB >DEFAULTALG 1 > Xpress まとめ この章では、Moselを用いてモデルをコンパイルし、読み込み、実行する方法、問題の行列ファイル を転送する方法、線形問題の解き方、解へのアクセスの仕方、整数計画問題の解法、解法アルゴリ ズムの変更方法、Optimizer制御値のアクセスと変更、等について学んだ。 The Xpress-MP 31 第4章 Xpress-MP ライブラリー 概要 この章では以下について述べる。 • • いろいろなXpress-MP ライブラリーを示す; • • • Xpress-BCLを用いてモデルを構築し、解く方法を学ぶ; Mosel ライブラリーを用いてMosel 言語で書かれた問題をコンパイルし、読み込み、解く方法を 学ぶ; Xpress-Optimizer ライブラリーを用いて問題を読み込み、解く方法を学ぶ; いろいろなプログラミング言語を用いてライブラリーを利用する方法を学ぶ 始める前に Xpress-MP ライブラリーは、Xpress-Mosel とXpress-Optimizer エンジンに対するインターフェイ スを与えるが、利用者は自身のプログラムからXpress-MPを呼ぶことができる。この章では利用者が Xpress-MP ライブラリーを自身のシステムにインストールしていることを前提としている。本章の 構成は、Xpress-IVE と Console Xpressの章で示したように、いろいろな機能を使いつつサブルー チンライブラリーを用いて簡単な問題を入力して解くことを基本としている。前提とはしていない が、本章を読む前にConsole Xpressの章を調べておくのが有用であろう。 Libraries Xpress-MP ライブラリーの構成要素 Xpress-MPには3つの主要な構成要素としてMosel, BCL, Optimizerがある。Xpress-MP ライブラリ ーはこれらに対するインターフェイスを与え、いろいろな方法でこれらにアクセスが可能となる。 利用者にとって利用可能なこのようなライブラリーはいくつか存在するが、本章ではこれらのうち の3つについてのみ詳細を述べることにする。 • Mosel ライブラリーによってモデルファイルをコンパイルし、読み込み、実行し、さらにいくつ かの問題を解くモジュールとしてOptimizerにアクセスすることが可能となる。さらには、自身のア プリケーションの中でMoselモデルプログラミング言語を柔軟に利用することが可能となる。 • Xpress-MP Builder Component Library (Xpress-BCL) は少し異なっており、Moselに対する別の モデリング環境を与え、モデルが利用者のプログラムの中に構築され、出力ファイルを作成するか、 行列を直接Optimizerに送る、といったことが可能となる。 • Optimizer ライブラリーによって行列ファイルの入力、操作、最適化が可能となり、そしていろ いろな方法で解に対するアクセスが可能となる。以下の各節では、ライブラリー利用者にとって一 般的に興味ある情報を与える前に、これらのライブラリーのそれぞれについて説明する。 本章における大部分の例は、Xpress-MPライブラリーを呼ぶ場合に広く用いられ、Reference Manuals の基本となっているC プログラミング言語で書かれている。しかしながらサポートされている他の 言語としては、C++, Visual Basic, Javaなどがあり、本章の最後には、Visual BasicとJavaによる ライブラリーを利用する場合の概要が示される。 The Xpress-MP 32 Mosel ライブラリーの利用 簡単なモデル入門 まず最初に、以前に用いたのと同じ簡単なモデルを入力する。解くのにMosel ライブラリーがどの ようにして用いられるかが分かるであろう。リスト4.1に与えられるモデルはMoselモデルプログラ ミング言語で書かれており、わずか2つの決定変数(a and b)と2本の制約条件 (first and second) を有している。目的は目的関数profitを最大化することである。このような問題はモデル入力ファ イルとしてMosel ライブラリーに送られる。これらはMoselが実行しようとしている情報を処理する だけでなく、モデルを表現するアスキーテキストファイルでもある。標準的には、Moselファイルは 拡張子.mosを有することが前提であって、本章でもこれを採用する。 リスト 4.1 簡単なモデル model simple uses "mmxprs" declarations a: mpvar b: mpvar end-declarations first:= 3*a + 2*b <= 400 second:= a + 3*b <= 200 profit:= a + 2*b maximize(profit) writeln("Profit is ", getobjval) end-model Libraries 演習問題 リスト4.1にあるモデルをファイルsimple.mosにテキストエディターを用いて入力せよ。 これは前にConsole Xpressの章にある演習問題ですでに試みたものである。 Mosel ライブラリーの構成要素 Mosel ライブラリーはランタイムライブラリーxprm_rt、コンパイラーライブラリーxprm_mcからな る。これらのうちでランタイムライブラリーは主要なもので、利用の前後でMoselの開始と終了に対 するルーティンを含むものである。それに対してコンパイラーライブラリーは、Moselモデルプログ ラムファイルのコンパイルに用いられる単一のルーティンを含むものである。一般にはランタイム ライブラリーは単独で用いられることが多い。コンパイラーライブラリーが用いられる場合は、両 方のヘッダーファイルが利用者のコードに含まれなければならず、コンパイラールーティンを用い る場合は、前もってランタイムライブラリーから開始ルーティンが呼ばれていなければならない。 この節では、以下のMoselライブラリープログラムを用いて、これらのライブラリーの両者の利用方 法を示す。 Moselの3つの柱 Moselライブラリーを用いて問題を解くには、以下のページで明らかになる3つの段階の操作が必要 である。 リスト 4.2 Moselライブラリープログラムの構造 #include "xprm_rt.h" #include "xprm_mc.h" 33 int main(void) { XPRMinit(); program statements XPRMfree(); return 0; } The Xpress-MP モデルプログラムは一度書かれるとまずコンパイルされ、コンパイルされたプログラムはMoselに読 み込まれ、最終的に実行される。これらの目的のために、Mosel ライブラリーでは3つのルーティ ンが提供される。 リスト4.3は以前に保存したモデルをコンパイルし、読み込み、実行する方法を示している。 演習問題 モデルsimple.mosをコンパイルし、読み込み、実行するためのプログラムを書き、実行 せよ。モデルから得られる最大利益を求めよ。 XPRMcompmod これによってMoselを呼び出してモデルファイルをコンパイルし、BInaryModel (or BIM)ファイルに 入れることができる。以下の4つの引数がある。 • コンパイル操作のためのオプション; • モデルソースファイルの名前; • 目的地ファイルの名前、NULLでよい。; • 出力ファイルの最初に保存されるコメントテキスト これはモデルのコンパイラーライブラリーにおける唯一のコマンドである。 XPRMloadmod Moselがバイナリーファイルを読み込むのを助けるもので、2つの引数を持つ。 • ファイル名; • モデルの内部名、NULLでもよい。 XPRMloadmod では問題ポインター、型XPRMmodelの変数を返す。 XPRMrunmod Mosel内でモデルを実行する。3つの引数がある。 • 問題ポインター; • 結果の値が戻される領域へのポインター; • 一連のパラメタ初期値 NULL. Using a ‘g’ flag とXPRMcompmodを用いると、エラーがある場合にはデバッグに関する追加情報が得られる。これは発見困難な領域エ ラーなどの場合に有用である。このようにしてパラメタを設定する例は5章に示される。 Libraries この演習問題が難しい場合は、Mosel ライブラリーは必要とされる重要な安全保護システムファイ ルを見つけることができないし、また他のライブラリー / DLLsを配置することができない。 Xpress-MP安全保護システムをどのようにして立ち上げるかについての詳細は、CD-ROMにある ‘readme’ファイルを参照されたい。そこには、最も共通に利用可能なコンパイラーを有するライブラ リーを立ち上げる場合の詳細が示されている。 34 解の情報の取得 リスト4.1にあるモデルでは、Moselによって目的関数の最大値を出力することを試みた。同様にし て、モデルでは決定変数の最適値や他の情報を出力するように書くことも可能であった。このよう な例をあとで示そう。しかしながら、Moselの実行時間ライブラリーでは解の詳細な情報を得るため にいくつかの関数を提供し、解の情報を他のアプリケーションで直接使えるようにしている。この ような2つの関数を以下に示す。 リスト 4.3 モデル‘simple’をコンパイルし、読み込み、実行する #include <stdio.h> #include "xprm_rt.h" #include "xprm_mc.h" int main(void) { XPRMmodel simple; int nReturn; XPRMinit(); XPRMcompmod("","simple.mos",NULL,"Simple Example"); simple = XPRMloadmod("simple.bim",NULL); XPRMrunmod(simple,&nReturn,NULL); XPRMfree(); return 0; } The Xpress-MP 目的関数値を返すのはこれらの2つの中でも容易な方である。決定変数の値を得ることはもう少し 煩雑であるが、特定の変数名に対応する辞書要素は、ライブラリー関数XPRMfindidentを用いて最初 にMoselによって返されなければならない。ここでは値を返すべく最終的にXPRMgetvsol として用い られる前に、XPRMmpvar変数として指定されなければならないような一般的な型を返している。リス ト4.4ではこれらの関数とその利用方法を示し、さらにリスト4.3との違いをボールドタイプの文字 で示している。 XPRMgetobjval 目的関数値を倍精度で返す。一つの引数は問題ポインターである。 XPRMgetvsol 決定変数の値を倍精度で返す。2つの引数がある。: • 問題ポインター; • 決定変数の指定. リスト 4.4 解の情報を得る #include <stdio.h> #include "xprm_rt.h" #include "xprm_mc.h" int main(void) { XPRMmodel simple; XPRMalltypes atvar; 35 XPRMmpvar a, b; int nReturn; XPRMinit(); XPRMcompmod("","simple.mos",NULL,"Simple Example"); simple = XPRMloadmod("simple.bim",NULL); XPRMrunmod(simple,&nReturn,NULL); Libraries 演習問題 決定変数a とbの最適値を得るように前の問題を変更せよ。 Moselの実行時間ライブラリーには本質的にはXPRMgetvsolと同様の多くの関数が含まれている。た とえばリデューストコスト(XPRMgetrcost), スラック値 (XPRMgetslack) 、双対値(XPRMgetdual) などであるが、詳細についてはMosel Reference Manualを参照されたい。 Mosel ライブラリーをさらに利用するために いくつかのモデルをさらに利用するために Xpress-MPの利用者にとっては、いくつかの異なるモデルを並行して構築したり、アプリケーション の中で交互に利用したりすることがよくある。Mosel ライブラリーでは、システム資源あるいはラ イセンスの詳細に関する制約の下で、任意の数のモデルをメモリーに入れて同時に動かすことが可 能である。 すべてのXpress-MP製品の場合と同様に、Mosel ライブラリーのモデル管理は、現在読み込まれてい るモデルを識別する問題ポインターの概念に基づいており、いかなる状況においてもどのモデルが 稼働中かを分かるようにしている。 XPRMfindident(simple,"a",&atvar); a = atvar.mpvar; XPRMfindident(simple,"b",&atvar); b = atvar.mpvar; printf("The maximal profit is %g\n", XPRMgetobjval(simple)); printf("a = %g\nb = %g\n", XPRMgetvsol(simple,a), XPRMgetvsol(simple,b)); XPRMfree(); return 0; } リスト 4.4 解の情報を得る 問題ポインターあるいは表示は、前の演習問題あるいはリストのところですでに見たように、 XPRMloadmodルーティンによって戻される。これはモデルのほとんどのルーティンの中で最初の引数 として用いられているので、Console Moselと違ってこの場合は現モデルactive modelという概念は 存在しない。 演習問題 リスト4.5のモデルをファイルaltered.mosに入れて、前のプログラムが‘simple’と ‘altered’ の両者を連続してコンパイルし、読み込み、実行するようにせよ。 使用している間、Moselは現在メモリーにあるモデルのリストを保管し、新しいモデルを読み込んだ リストの最初に置く。モデルは次々にこのリストから関数XPRMgetnextmodを用いて選択され、非常 に多くのモデルを交互に利用するのに便利なようになっている。与えられたモデルについての情報 は、XPRMgetmodinfoを用いて検索される。 リスト 4.5 モデルの変更 36 model altered uses "mmxprs" declarations a, b: mpvar end-declarations first:= 3*a + 2*b <= 400 second:= a + 3*b <= 200 third:= 6*a + 5*b <= 800 profit:= a + 2*b maximize(profit) writeln("Profit is ", getobjval) end-model これとリスト4.1との間の唯一の変化は、新しい制約条件が課されている点である。制約条件が有効な場合は、目的関数値は 減少する。 Libraries モデルが不要になると、XPRMunloadmodを用いて読み出される。これらの関数は通常はわずか2つの モデルだけでは用いられないので、リスト4.6に多くのモデルに対して容易に拡張されるような使用 例を示す。 XPRMgetmodinfo 与えられた問題ポインターによって指定されたモデルについての情報を返す。問題ポインターに続 いて以下の5つの引数がある。 • モデル名; • モデル番号; • システムコメント; • 利用者コメント; • 使用メモリーの量. これらの5つは、不要ならば NULL でよい。 XPRMgetnextmod 引数として与えられたモデルの次のモデルに対するポインターを返す。引数がNULLの場合は、リス ト中の最初のモデルが返される。引数がリスト中の最後のモデルの場合は、XPRMgetnextmodはNULL を返す。 XPRMunloadmod Instructs Moselに指示してモデルを読み出し、それに付随するすべてのメモリーやリソースを解除する。唯一 の引数は問題ポインターである。 The Xpress-MP 演習問題 リスト4.6のプログラムを入力し、実行せよ。前の演習問題で‘simple’と ‘altered’ の両 者をすでにコンパイルしているので、ここではコンパイラーxprm_mcは不要であって、XPRMcompmod を用いた2行を省略できる。 リスト 4.6 複数モデルを動かす #include <stdio.h> #include "xprm_rt.h" 37 #include "xprm_mc.h" /* if needed */ int main(void) { XPRMmodel mod; int nReturn; const char *name; XPRMinit(); /* uncomment the following two lines if needed */ /* XPRMcompmod("","simple.mos",NULL,""); XPRMcompmod("","altered.mos",NULL,""); */ XPRMloadmod("simple.bim",NULL); XPRMloadmod("altered.bim",NULL); /*first in list*/ /* return a pointer to the first item in list */ mod = XPRMgetnextmod(NULL); while(mod != NULL) { XPRMgetmodinfo(mod,&name,NULL,NULL,NULL,NULL); printf("\nCurrent problem is ‘%s’\n", name); XPRMrunmod(mod,&nReturn,NULL); mod = XPRMgetnextmod(mod); } /* unload first item in list, i.e. ’altered’ */ XPRMunloadmod(XPRMgetnextmod(NULL)); XPRMfree(); return 0; } Libraries 実行時間の管理 モデルが読み込まれると、XPRMrunmodルーティンを用いて実行される。利用者は与えられたモデル がXPRMisrunmodを用いて任意の時刻に実行中であるか、XPRMstoprunmodを用いて終了させられたか を見ることができる。これらの2つのルーティンは、アプリケーションの中で、解くのに長い時間 を要すると思われる大きなモデリング問題と共に用いられることが多い。リスト4.7にはこのような 例が与えられているが、その中では‘Ctrl-C’キーを組み合わせることによって実行中にモデルを中断 することができる。 XPRMisrunmod これによってモデルが実行中であるか否かをチェックできる。唯一の引数は問題ポインターであっ て、モデルが実行中である場合に1,そうでないときに0となる。 XPRMstoprunmod これによってモデルを中断することができる。唯一の引数は問題ポインターである。 リスト 4.7 モデルの実行を正しく終了する #include <stdio.h> #include <signal.h> #include "xprm_rt.h" 38 XPRMmodel mod; void end_run(int sig) { if(XPRMisrunmod(mod)) XPRMstoprunmod(mod); } 行列ファイルの書き方 モデルは解かねばならないし、行列は次々に変更しなければならないし、またMoselによって与えら れている以上に利用者との対話が必要になるということがよくある。他方、同じ問題を他人が利用 する必要がある場合もある。このような場合、問題が他のプログラムに対する入力となるように、 行列ファイルがMoselによって要求される場合がある。 Mosel ライブラリーはMPS形式とLP形式の両方の行列ファイルをXPRMexportprobルーティンを用い て書くのをサポートしている。 int main(void) { int nReturn; XPRMinit(); mod = XPRMloadmod("bigmodel.bim",NULL); signal(SIGINT,end_run); /* Redirect Ctrl-C */ XPRMrunmod(mod,&nReturn,NULL); XPRMfree(); return 0; } XPRMexportprob これはMosel に指示してMPS形式とLP形式の両方の行列ファイルを書かせるものである。4つの引数 がある。 • 問題ポインター; • 出力形式、MPS形式に対しては"m"、LP形式(minimization)に対しては""、 LP形式(maximization) に対しては"p"、混合型名称に対しては"s"; • 出力に対するファイル名 — NULLの場合、出力はコンソールにプリントされる。 • 利用目的、NULLでもよい リスト 4.7 モデルの実行を正しく終了する Libraries 演習問題 モデルを問題に対するMPS行列ファイルsimple.matを書くように変更せよ。適当なLP形式 の行列ファイルsimple.lpを作成せよ。 MPSファイルを作成するためにXPRMexportprobを利用する例はリスト4.8に示されている。これを前 の例と比較せよ。これまでの説明から、Mosel ライブラリーを用いてモデルプログラムをコンパイ ルし、読み込み、実行すること、アプリケーションから解の値にアクセスすること、複数の問題を 処理すること、行列ファイルを標準形式で転送すること、などが可能となった。次の章では、この ようにして解くべきモデルを作成できるように、Moselモデルプログラミング言語についてより多く を学ぶであろう。以下の節においては、上と同じ作業がBCLを用いてどのようにして達成されるかが 分かるであろう。BCLを用いない場合は、Optimizer ライブラリーについて紹介されている74ページ に飛び、95ページにある節 “Xpress-MP ライブラリーを最大限利用する”に含まれる内容を見たり、 5章のMosel言語についてより多くを学んでもよい。 39 リスト 4.8 MPS行列ファイルを作成する #include <stdio.h> #include "xprm_rt.h" int main(void) { XPRMmodel simple; int nReturn; XPRMinit(); simple = XPRMloadmod("simple.bim",NULL); XPRMrunmod(simple,&nReturn,NULL); XPRMexportprob(simple,"m","simple",NULL); XPRMfree(); return 0; } ファイル名の拡張子がない場合は、MPS ファイルには.mat が付加され、LPファイルには.lp が付加されるであろう。 The Xpress-MP Xpress-BCLをさらに利用するために これまで、どのようにしてMosel ライブラリーを用いてモデルプログラムファイルをコンパイルし、 読み込み、実行するか、そしてOptimizerを用いて解を求めるかを見てきた。ほとんどの場合はこれ で十分かも知れないが、時にはより多くの柔軟性、自身のプログラムの中から直接モデルを作成す る、などが必要になるかも知れない。このためには、以下に示す3つの本質的な方法がある。 • モデルファイルを作成するためにプログラムを用い、Mosel ライブラリーを利用してそれをコン パイルし、読み込み、実行する • BCLを用いてモデルを動的に構築する • モデルを局部的に構築するためにプログラムを用い、Optimizerライブラリーを利用してそれを Optimizerに直接読み込む 最初の項目は以前見た内容に関連しているので、ここでは考慮しない。2番目の項目はこの節の問 題で、3番目は次のOptimizerライブラリーに関する内容である。 Xpress-MP Builder Component Library (Xpress-BCL) は、モデルが作成されるための別なライブラ リー環境を与える。BCLプログラムは割に単純であって、以下のページで示すように、特定の型のモ デルを構築するのに利用される。しかしながらもっと一般的なことは、BCLは数理計画モデルと最適 化を含む完全なソフトウェアシステムの開発に利用されるということである。 完全なモデルとは、いくつかのファイル形式のBCLによる出力である。選択的には、Optimizerに直 接読み込まれた場合に、BCLを用いて直接解くか、あるいはOptimizerライブラリーコマンドを用い てさらに操作を加えるかのいずれかである。それぞれの可能性について論じよう。 前節あるいは前章に述べたように、ここのアプローチは49ページにあるリスト4.1に述べられた単純 なモデルを構築するために再度BCLを用いることである。したがって必要に応じてモデルのところを 参照できるようにページに印を付けておくのが有用であろう。 Libraries モデルの最初の定式化 BCLを用いて簡単なモデルを構築するには、モデルの記述をBuilder言語に翻訳する必要がある。リ スト4.9から始めよう。 ここに5つの単純な記述がある。すなわち決定変数、制約条件、問題ポインターを宣言する、そし てライブラリーを初期化して問題を作成することである。 40 リスト4.1から明らかなことであるが、より多くの情報が必要とされており、宣言に続いて変数を加 えるということは比較的単純な作業である。リスト4.10に示すように型と限界を追加的に設定しな ければならない。 リスト 4.9 構造の宣言と初期化 XPRBvar a, b; XPRBctr first, second, profit; XPRBprob prob; XPRBinit(""); prob = XPRBnewprob("simple"); XPRBinit BCL環境を初期化する。唯一の引数は、Xpress-MPの安全保護ファイルを探索するためのパスを特定 するのに用いられる。nullの場合は、標準的な探索パスが用いられる。リターンコードは常にチェ ックされる:非零値は失敗を表すので、プログラムは終了しなければならない。 XPRBnewprob 新しい問題を作成し、名前を付ける。問題が引数であって、作成された問題に対するポインター、 型XPRBprobの変数を返す。 これまでの例ではリターンコードをチェックすることを示したが、このようなチェックは常に実行される。詳細については、 95ページの“エラーチェック”を参照されたい。 The Xpress-MP 定義された決定変数とともに制約条件が追加されるが、増加的な場合はリスト4.11に示される。 リスト 4.10 決定変数の追加 a = XPRBnewvar(prob,XPRB_PL,"a",0,200); b = XPRBnewvar(prob,XPRB_PL,"b",0,200); XPRBnewvar 単一の変数を追加する。5つの引数がある。 • 問題ポインター; • 型 — XPRB_PLは連続型、非負変数を意味する; • 出力ファイルにある名前で、後にOptimizerに送られる名前である; • 変数に対する下限と上限で、上限がない場合は無限大とする。型XPRBvarの変数を返す。 リスト 4.11 増加的な制約条件の追加 first = XPRBnewctr(prob,"First",XPRB_L); XPRBaddterm(prob,first,a,3); XPRBaddterm(prob,first,b,2); XPRBaddterm(prob,first,NULL,400); XPRBnewctr 新しい制約を作成する。問題ポインターに続いて、制約名と制約の型の2つの引数があり、XPRB_L は 制約であることを意味し、型XPRBctrの変数を返す。 ここでの限界値は、下限値は容易に得られるが、上限は制約Secondから得られる。 Libraries 目的関数は、右辺値を設定しない制約条件と同様にして追加される。XPRBsetobjコマンドを用いた 41 目的関数として特定される。リスト 4.12に例を示す。 問題をBCLを用いて解く方法 モデルを構築すると、次の作業は解を見つけることである。Moselの場合と共通に、BCLは問題を Optimizerに読み込み、それらを解き、結果にアクセスするという機能を有する。関数XPRBmaxim は、 解を求めるためにOptimizerを呼び出す。これに続いて、XPRBgetobjval とXPRBgetsolの2つの関数 が目的関数の最適値と決定変数の値をそれぞれ返す。最後に、これらの値を画面に示すために、標 準的なプリント関数が用いられる。 XPRBaddterm 制約条件に新たな項を加える。問題ポインターに続いて、3つの引数がある。 • XPRBnewctrから得られる制約条件を表す; • 右辺に定数を追加するための変数あるいはNULLである。 • 2番目の引数がNULL の場合に係数あるいは右辺の値を表す。 したがってリスト4.11の2行目には、3*a をFirstという名の制約に加える。 リスト 4.12目的関数を加える profit = XPRBnewctr(prob,"Profit",XPRB_N); XPRBaddterm(prob,profit,a,1); XPRBaddterm(prob,profit,b,2); XPRBsetobj(prob,profit); 目的関数に対する行の型式は‘unconstrained’ XPRB_Nとなる。 The Xpress-MP これらをすべてまとめると、モデルの完全な記述はリスト4.13のようになる。 XPRBmaxim Optimizerを呼び出して問題を解く。問題ポインターとオプションフラッグの2つの引数がある。 XPRBgetobjval 最適な目的関数値を返す。 XPRBgetsol 決定変数の最適値を返す。問題ポインターと値が要求される変数という2つの引数がある。 リスト 4.13 モデルの最初の定式化 #include <stdio.h> #include "xprb.h" int main(void) { double objval; XPRBvar a, b; XPRBctr first, second, profit; XPRBprob prob; if(XPRBinit("")) return 1; prob = XPRBnewprob("simple"); /* adding the variables */ a = XPRBnewvar(prob,XPRB_PL,"a",0,200); 42 b = XPRBnewvar(prob,XPRB_PL,"b",0,200); xprb.h ヘッダーファイルには、BCL関数に対するすべての定義が含まれる。 最後の2つのコマンドは、問題をメモリーから削除し、他のシステムリソースを解除するものであ る。これらはすべての問題の最後に呼び出されることが重要である。 /* adding the constraints */ first = XPRBnewctr(prob,"First",XPRB_L); XPRBaddterm(prob,first,a,3); XPRBaddterm(prob,first,b,2); XPRBaddterm(prob,first,NULL,400); second = XPRBnewctr(prob,"Second",XPRB_L); XPRBaddterm(prob,second,a,1); XPRBaddterm(prob,second,b,3); XPRBaddterm(prob,second,NULL,200); profit = XPRBnewctr(prob,"Profit",XPRB_N); XPRBaddterm(prob,profit,a,1); XPRBaddterm(prob,profit,b,2); XPRBsetobj(prob,profit); XPRBmaxim(prob,""); /* retrieving the solution */ objval = XPRBgetobjval(prob); printf("The maximum profit is %f\n", objval); printf(" a = %f, b = %f\n",XPRBgetsol(prob,a), XPRBgetsol(prob,b)); XPRBdelprob(prob); XPRBfree(); return 0; } XPRBdelprob 問題をメモリーから削除する。唯一の引数は問題ポインターである。 XPRBfree 現在使われている他のシステムリソースを解除する。 リスト 4.13 モデルの最初の定式化 The Xpress-MP 演習問題 リスト4.1の簡単な問題を解くために、リスト4.13のBCLプログラムを入力し、解の値を 画面に表示せよ。 標準的には、アプリケーションの中に明示的に書かれていない限り、いかなる問題の出力もBCLによ って与えられることはない。これは、上述のように、われわれが決定変数と目的関数の値のみを必 要とする場合には有利なことであるが、われわれにとっては、解が戻される前に最適化がどのよう に進行しつつあるかについてより多くを知りたい場合もある。このような場合には、message level を0から増加させることによってlogファイルの形のコンソール出力が得られる。message levelが3 になると、BCLはOptimizerによって求解のさまざまな段階における問題の統計量と目的関数の値を 含めたすべてのメッセージを出力することになる。これが必要な場合には、問題が入力される前に 43 message levelが設定されていなければならない。 演習問題 次の行 XPRBsetmsglevel(prob,3); をXPRBnewprobコマンドに続けて上の問題に追加し、問題を再構築せよ。これを実行し出力を見よ。 行列変数を用いた問題の定式化 上に示したように、プログラムを構築する場合には常に増加的な傾向があるため、プログラムは不 必要に長たらしいものになることが多い。しかしながら、モデルを配列変数によって構築するとい うように別のアプローチを採用すると、プログラムはより簡潔なものとなりうる。以下にこのよう な例を示そう。 リスト4.14に示したような新しい配列変数xを宣言した記述から始める。 リスト 4.14 配列変数 XPRBarrvar x; ... x = XPRBnewarrvar(prob,2,XPRB_PL,"x",0,200); 2つの新しい記述というのは、変数の型に関する宣言とそれに関する情報であって、前述の XPRBnewvar関数の場合と同様である。追加的な引数として、配列の大きさがある。この場合に宣言 が必要となるのは、最終的にXPRBsetobjとして用いられる目的関数である。他の制約条件は XPRBnewarrsum関数を用いて定義される。リスト4.15に関連部分を示す。 特に、ここで制約条件の左辺のそれぞれに対する係数を有する3つの配列を定義し、それぞれを FIRST, SECOND, PROFITと呼ぶ。これらに配列変数xを乗じて(スカラー積として) 制約条件の左辺が 得られる。 XPRBnewarrvar 新しい配列変数を定義する。引数は問題ポインターと配列の大きさ、そしてXPRBnewvarと同じ情報 に関する4つである。型XPRBarrvarの変数を返す。 リスト 4.15 配列の制約条件 double FIRST[] = {3,2}; double SECOND[] = {1,3}; double PROFIT[] = {1,2}; ... XPRBctr profit; ... XPRBnewarrsum(prob,"First",x,FIRST,XPRB_L,400); XPRBnewarrsum(prob,"Second",x,SECOND,XPRB_L,200); profit=XPRBnewarrsum(prob,"Profit",x,PROFIT,XPRB_N,0); XPRBsetobj(prob,profit); 通常はモデルが解けて解が出力されると、モデルは終了する。完全なリストがリスト4.16に与えら れている。 XPRBnewarrsum 2つの1次元配列によって新しい制約条件の左辺を定義するが、1つは変数でもう一つは係数を含 44 み、それらを要素ごとに乗じることになる。数学的には、次のように表現される。問題ポインター の後に5つの引数がある。 • 制約名; • 変数の配列; • 係数の配列; • 制約の型; • 制約の限界値 リスト 4.16 完全なモデル, ‘arraymod’ #include <stdio.h> #include "xprb.h" double FIRST[] = {3,2}; double SECOND[] = {1,3}; double PROFIT[] = {1,2}; int main(void) { double objval; XPRBarrvar x; XPRBctr profit; XPRBprob prob; if(XPRBinit("")) return 1; prob = XPRBnewprob("arraymod"); /* adding the variables */ x = XPRBnewarrvar(prob,2,XPRB_PL,"x",0,200); a x . aixi i _ = 制約の型が ‘nonbinding’XPRB_Nの時は、最後の記述は無視される。 Libraries 演習問題 新しいモデルを入力し、定式化した上で解を観察せよ。 BCLをさらに利用するために 整数計画法 リスト4.1の簡単な問題に対しては、決定変数の値がそれぞれ小数点以下1位まででa = 114.3 と b = 28.6のときに目的関数の最大値が得られるのを見た。しかしながら問題によっては、解が整数解 の場合のみ意味があるというように、このような小数解は適切ではないかも知れない。 /* adding the constraints */ XPRBnewarrsum(prob,"First",x,FIRST,XPRB_L,400); XPRBnewarrsum(prob,"Second",x,SECOND,XPRB_L,200); profit=XPRBnewarrsum(prob,"Profit",x,PROFIT, XPRB_N,0); XPRBsetobj(prob,profit); XPRBmaxim(prob,""); /* retrieving the solution */ objval = XPRBgetobjval(prob); 45 printf("The maximum profit is %f\n", objval); printf(" a = %f, b = %f\n",XPRBgetsol(prob,x[0]), XPRBgetsol(prob,x[1])); XPRBdelprob(prob); XPRBfree(); return 0; } リスト 4.16 完全なモデル, ‘arraymod’ The Xpress-MP BCLにおいて整数変数を扱うということは、単に変数の型をXPRBnewvarあるいはXPRBnewarrvarによ って適切に特定することである。前に連続型変数をXPRB_PLとして扱ったが、整数変数はXPRB_UIと 表される。最後の例で、これを実行する場合に必要な変更は、次式で与えられる。 x = XPRBnewarrvar(prob,2,XPRB_UI,"x",0,200); 新しい問題は元の問題ほど簡単ではなく、解を求めるにはより複雑な方法が必要である。— すなわ ち整数解を見つけるために、グローバル探索global search が用いられる。これは、解のルーティ ンに加えてオプショナルなg フラッグを利用するものである。 XPRBmaxim(prob,"g"); これらの2つの変更がなされると、プログラムは実行可能となる。 演習問題 リスト4.16のモデルをa と bが整数変数であるように変更して、グローバル問題を解け。 最大利益を達成するには、決定変数の値はどうあるべきかを求めよ。 配列の中の2,3の変数だけが整数であるというような場合は、作業量はさほど多くはならない。 配列の中のすべての変数が同じ整数型であると宣言されることもあるが、異なる型を有する場合に はXPRBsetvartypeコマンドによって次々に変更されなければならない。このような場合、一連の XPRBnewvarを用いてこれらの変数をC配列に置きながらモデルを構築するのがずっと簡単である。 行列ファイルの書き方 モデルの出力を行列ファイルとしてソルバーの中に独自に入力したり、あるいは他人に与えたりす ることが必要となることもよくある。BCLではコマンドXPRBexportprobによってこのような目的を達 成する。 利用可能なすべての変数の型はBCL Reference Manualに記されている。 演習問題 前の問題を、問題を解くよりもMPSファイルを書くように変更せよ。Libraries これを達成する完全なプログラムがリスト4.17に与えられている。行列ファイルarraymod.matが作 成されるが、そうでないときはプログラムが実行されるだけである。 XPRBexportprob これによって問題を行列ファイルとして2つの企業標準形式として転送する。問題ポインターの後 に2つの引数がある。 • 出力ファイル形式。XPRB_MPSあるいはXPRB_LP であって、MPS形式ファイルか LP形式ファイルか のいずれかである。; • 拡張子のない出力ファイル名 リスト 4.17 MPS行列ファイルを書く #include <stdio.h> #include "xprb.h" 46 double FIRST[] = {3,2}; double SECOND[] = {1,3}; double PROFIT[] = {1,2}; int main(void) { XPRBarrvar x; XPRBctr profit; XPRBprob prob; if(XPRBinit("")) return 1; prob = XPRBnewprob("arraymod"); x = XPRBnewarrvar(prob,2,XPRB_PL,"x",0,200); The Xpress-MP MPS形式と同様に LP行列ファイルも目的関数が最大化か最小化かについての情報を含んでいる。こ れが特定されないと、問題は標準的に最小化問題を前提とする。この問題に対するLPファイルを書 くためには、行列ファイルが転送される前に次の行が追加されなければならない。: XPRBsetsense(prob,XPRB_MAXIM); これは問題の意図を最大化問題に変更するものである。 演習問題 上のモデルを、問題に対するLP行列ファイルを書くように変更せよ。テキストエディタ ーか同様のものを用いて問題の意図が正しく設定されていることをチェックせよ。 BCLの簡単な紹介の中ですべての可能性について説明することは不可能であって、上ではライブラリ ーの概要のみを紹介している。詳細については、BCL Reference ManualかまたはXpress-MP CD-ROM に含まれる多くの例を参照されたい。以下の節では、Xpress-Optimizer ライブラリーについて述べ るが、アプリケーションからOptimizerにアクセスすることが可能となる。行列レベルで積極的にモ デルを構築、変更する利用者にとって追加的な機能を与えるには、BCLとの結合を有効に利用するこ とが必要である。これらの2つがどのようにして結合されるかについての詳細は、96ページの節“BCL とOptimizerライブラリーの結合”に示されている。それ以外の点については、95ページの節 “Xpress-MPライブラリーを最大限利用する”を参照されたい。 XPRBnewarrsum(prob,"First",x,FIRST,XPRB_L,400); XPRBnewarrsum(prob,"Second",x,SECOND,XPRB_L,200); profit=XPRBnewarrsum(prob,"Profit",x,PROFIT, XPRB_N,0); XPRBsetobj(prob,profit); XPRBexportprob(prob,XPRB_MPS,"arraymod"); XPRBdelprob(prob); XPRBfree(); return 0; } リスト 4.17 MPS行列ファイルを書く Xpress-Optimizer ライブラリーをさらに利用するために Xpress-Optimizer ライブラリーを用いることによって、ライブラリー利用者はアプリケーションか らOptimizerに完全にアクセスすることが可能となる。コンソール利用者にとって喜ばしい機能を提 供するとともに、Optimizer ライブラリーでは求解過程においていくつかの高度の行列操作関数を 利用したり、あるいはまた高度の対話型操作を行ったりすることが可能である。さらにライブラリ ーでは利用者に強力なOptimizerを提供し、システムリソースとライセンスについての詳細に関する 47 制約下ではあるが、いくつかの異なる問題を同時に扱えるようにしている。Mosel ライブラリー と BCLの場合と同様にして、Optimizer ライブラリーでは利用するそれぞれの関数を指示する問題ポイ ンターを用いたいくつかの問題を識別している。上に述べた他のライブラリーの場合と同様に、こ こでも実行中の現問題active problemという概念は存在しない。 Optimizer ライブラリーを用いて問題を解く方法 OptimizerではMPS形式か LP形式かのいずれかの行列ファイルを正しい入力として受け入れる。この 節の演習問題を試みるには、前章の演習問題で作成したような、あるいはまた3章の, “Console Xpress”で得られたような正しい行列ファイルにアクセスする必要がある。この節では、リスト4.1 のモデルで作成された行列ファイルsimple.matを参照しよう。Optimizer ライブラリーでは、利用 する前にライブラリーを初期化し、システムリソースを最後に解除するための関数を備えている。 一般には、これらの2つの関数XPRSinit と XPRSfreeは、他のすべてのライブラリーコマンドを含 み、両者はOptimizerプログラムで利用できなければならない。行列をOptimizerに読み込むために は、問題ポインターが作成され、操作に関連するあらゆる関数を利用可能な問題に対して用いられ なければならない。XPRScreateprobを用いて、これを試みてみよう。 XPRSinit 利用する前にOptimizerライブラリーを初期化する。パスワードファイルがあるところへのポインタ ーが唯一の引数である。これがNULLの場合は、標準インストールディレクトリーが探索される。 The Xpress-MP リスト4.1の問題に対する解を求めるためには、目的関数が最大化される前に行列ファイルが入力さ れなければならない。この問題を解くための完全なプログラムは、リスト4.18に与えられる。 XPRScreateprob これは特定の問題に対する問題ポインターを立ち上げる。問題ポインターを返すための唯一の引数 がある。これは最初の引数として、問題に対して作動するあらゆる関数につながっている。 XPRSdestroyprob 問題ポインターに指示された問題を削除する。 XPRSfree Optimizerで現在利用されるすべてのメモリーを解放し、あらゆるファイルを閉じる。 リスト 4.18 Optimizer ライブラリーを用いて問題を解く #include <stdio.h> #include "xprs.h" int main(void) { XPRSprob prob; XPRSinit(NULL); XPRScreateprob(&prob); XPRSreadprob(prob,"simple",""); XPRSmaxim(prob,""); XPRSwriteprtsol(prob); XPRSdestroyprob(prob); 48 XPRSfree(); return 0; } 演習問題 リスト4.18からOptimizerプログラムを入力し、行列ファイルsimple.matを入力し、それ を実行することによって目的関数を最大化し、XPRSwriteprtsolを用いて解を出力せよ。 XPRSwriteprtsolを用いて解を得る方法 Optimizerライブラリーを用いて解の情報を得るいくつかの方法がある。残りについては後述するが、 最も容易な方法は、ラインプリンターに対して解のファイルを送るのに最適なコマンド XPRSwriteprtsolを用いる方法である。このファイルはリスト4.19に示されるように、3つの部分に 分けられる。これらはおそらく1部, 3部, 2部の順に考慮するのが最も有用であろう。1部は求解プ ロセスのまとめの統計量を含んでいる。そこでは用いられた行列(問題)名(simple)と目的関数と 右辺名が与えられる。これに続いて行と列の数、最大化問題であるという事実、解くのに2回の反 復(ステップ)が必要であったこと、最良解が値171.428571を有することが記される。決定変数の 最適値は3部の列セクションColumns Sectionに与えられる。Value列が変数の最適値を与える。各 変数に対する入力コストやリデューストコストなどの追加的な解の情報もここに与えられる。これ らの詳細については、7章にある”用語のまとめ”を参照されたい。 XPRSreadprob MPS形式あるいは LP形式の問題ファイルを読む。3つの引数がある。 • 問題ポインター; • 問題のファイル名(拡張子のない) • 可能なフラッグのリスト XPRSmaxim 問題に対する最大解の探索を開始する。問題ポインターと可能なフラッグのリストという2つの引 数がある。 XPRSwriteprtsol 解をファイルproblem_name.prtに出力する。 "Section 1…" "Section 3…" The Xpress-MP 考慮すべき最後は2部の行セッションRows Sectionである。これは問題に対するいくつかの制約条 件と目的関数のリスト、そして"左辺"の値を与える。特に目的関数値はここで再び現れる。制約条 件に対するスラック値と双対値もここで得られる。これらの詳細については、7章にある”用語のま とめ”を参照されたい。 演習問題 いま作成した出力ファイルを観察して、いろいろな部分を確認せよ。決定変数と目的関 数の結果をリストにある結果を用いてチェックせよ。 リスト 4.19 XPRSwriteprtsol コマンドからの出力 Problem Statistics Matrix simple (1部) Objective *OBJ* RHS *RHS* 49 Problem has 3 rows and 2 structural columns Solution Statistics Maximization performed Optimal solution found after 2 iterations Objective function value is 171.428571 Rows Section (2部) Number Row At Value Slack Value Dual Value RHS N 1 *OBJ* BS 171.428571 -171.428571 .000000 .000000 L 2 second UL 200.000000 .000000 .571429 200.000000 L 3 first UL 400.000000 .000000 .142857 400.000000 Columns Section (3部) Number Column At Value Input Cost Reduced Cost C 4 a BS 114.285714 1.000000 .000000 C 5 b BS 28.571429 2.000000 .000000 "Section 2…"s Optimizer ライブラリーをさらに利用するために 最適化アルゴリズムを変更する Optimizerは標準的には双対単体法dual simplexを用いてLP問題を解く。しかしながら主単体法 primal simplexとニュートンバリア法Newton barrierもサポートされているので、問題によっては 解法を変更して問題を解くのに要する時間をかなり節約することも可能である。与えられた状況で ‘最良の’ 解法を選択することは一種の技術でもあるが、最良の選択は自身の問題と同様の問題に対 していろいろな解法を実験してみてはじめて明らかになるものである。解法を変更する最も簡単な 方法は最適化ルーティンに通じるフラッグを立てることである。これらは上述の3つの解法のそれ ぞれに対応する。 リスト 4.20には、最適化にニュートンバリア法を用いる例である。 演習問題 3つの解法のそれぞれを用いて問題を解け。このような簡単な問題の場合には、解法に 要する時間の差は目立たない。 b ニュートンバリア法; d 双対単体法; p 主単体法. リスト 4.20 最適化アルゴリズムを変更する XPRScreateprob(&prob); XPRSreadprob(prob,"simple",""); XPRSmaxim(prob,"b"); XPRSwriteprtsol(prob); XPRSdestroyprob(prob); The Xpress-MP 整数計画法 上で考慮した簡単な問題に対しては、目的関数の最大値は決定変数が小数第1位まででa = 114.3 と b = 28.6となるときに得られる。しかしながら問題によってはこのような小数解は受け入れられず、 決定変数のうちの1個あるいはそれ以上が整数値に限定されるという場合がある。リスト 4.21には、 整数解のみを必要とし、問題をMPS行列ファイルとして転送する場合に必要な変更を与える例を示す。 演習問題 リスト 4.21の整数問題をMoselライブラリーを用いてコンパイルし、読み込み、実行し、 50 MPS行列ファイルとして転送せよ。これをライブラリーを用いてOptimizerに読み込み、前と同様に 問題を解け。目的関数の値は現在いくつか。 新しい問題ファイル上でプログラムを実行した場合、整数変数を指定したにもかかわらず、戻され た解が不変であることに気づいたであろう。この理由は、最適整数解の探索が2段階であることに よる。第1段階では、最適解を見つけるためにLP緩和問題relaxationを解かねばならない。第2に は、最適整数解が存在することを前提として、グローバル探索が実行されることである。 リスト 4.21 問題に整数変数を導入する model integral declarations a: mpvar b: mpvar end-declarations first:= 3*a + 2*b <= 400 second:= a + 3*b <= 200 profit:= a + 2*b a is_integer b is_integer exportprob(EP_MPS,"integral",profit) end-model LP緩和問題は前の問題において変数の整数性を無視したものである。 グローバル探索は、gフラッグによってXPRSmaximを指定されたLP緩和問題の解に自動的に従う解法 であって、例がリスト 4.22に示されている。ここではプログラムのほんの一部のみを示すが、追加 的なコマンドはボールド体で示してある。これを実行して得られた解を見れば、最終的にはいずれ の決定変数も整数値をとる。 演習問題 問題に対して整数解が得られるようにプログラムを変更せよ。この場合の目的関数の最 大値はいくつか、またそれを達成するためには決定変数はどのような値をとるべきか。 演習問題 決定変数のうちの一つだけが整数値で他の条件は削除するようにモデルを変更せよ。目 的関数の最適値はいくつか。 事前解法と後処理 Optimizerでは、最適化の前に問題を単純化して解きやすくするための手続きをいくつか利用してい る。この一連の解法は前処理解法presolveとして知られている。前処理解法においては、行列中の 余分な行や列が削除されるため、前処理終了後の行列は元の行列とは非常に異なるものとなる。 XPRSwriteprtsolルーティンによって元の問題に関する情報が出力されるが、行列が前処理段階にあ る場合に行列の状態に影響されて前処理で得られた解を返す他のルーティンも存在する。元の問題 に対する解ではない、前処理終了後の解を得ることは、多くの利用者にとって混乱を招くことかも 知れない。したがって、行列あるいは解にアクセスする場合には、何を期待すべきかを知っておく ことが重要である。 リスト 4.22 整数解の探索を開始する XPRScreateprob(&prob); XPRSreadprob(prob,"integral",""); 51 XPRSmaxim(prob,"g"); XPRSwriteprtsol(prob); XPRSdestroyprob(prob); g フラッグを用いてXPRSmaximを呼び出すことはフラッグなしでXPRSmaximを呼び出して後にXPRSglobalコマンドを直接呼び 出すことと同じである。 The Xpress-MP 前処理解法はXPRSmaximとXPRSminimという解法ルーティンとして呼ばれている。線形計画問題の最 適化にしたがって、行列に対して自動的に後処理解法postsolvedがなされ、元の行列を復帰させて 解を元の問題に返す。行列が非連続変数(たとえば整数変数)あるいは特定の順序付き集合を含む 場合には、グローバル探索が呼び出されることになっているので、行列はXPRSmaxim (そして XPRSminim)の後も前処理状態のままである。特に、解法ルーティンの一つは、前述のように、g フ ラッグなしで呼び出され、後に行列は前処理状態にとどまる。グローバル探索の間中、整数解の後 処理解法が行われ、認定されると解ファイルに書かれ、元の問題に利用可能な解を与える。しかし ながら全行列の後処理解法は、グローバル探索の後に実行されることは決してないし、また元の行 列は保存されない。さらに行列にアクセスすることが必要とされる場合には、解法ルーティンを呼 び出す前に行列を読み出し、コピーしなければならない。 高度のライブラリー関数の多くは前処理終了後の行列には機能せず、それらを呼び出す前に行列が 正しい形式にあるように確認をとる必要があることに注意されたい。この例は後で説明されるコマ ンド関数XPRSaddrowsである。ここでは表面的なことのみを取り上げるが、前処理解法の詳細につい ては、Optimizer Reference Manualに見ることができる。さらなる情報については、このマニュア ルを参照されたい。 制御と問題の特性 Optimizerにはいくつかのパラメタがあり、それらの値は利用者がいろいろな制御値controls.を設 定することによって問題に応じて変更することができる。これらのパラメタ値は、特に解くのが難 しいとされる問題に対してOptimizerの性能を向上させるように細かな調整をするための考察を加 えることも可能である。このような工夫によって求解に要する時間をかなり節約することもまた可 能である。新しい利用者がこれらの多くを標準値から変更することは勧めないが、実験が役に立つ 場合もかなりある。どのようにしてこれを達成するかについて、簡単に述べよう。 "LP problems…" "IP problems…" Libraries 追加的なことであるが、最適化プロセスでは、解いている特定の問題のいくつかの特性attributes を利用する。これらの特性は制御変数と同様に検索可能なものである。設定済みのすべての制御変 数と検索可能な問題の特性の完全なリストはOptimizer Reference Manualで見ることができる。制 御変数も問題の特性も整数integer、倍精度double、文字列character stringの中の一つの型を持っ ていて、処理の仕方は型によってそれぞれ異なる。当面、制御変数のみを扱うことにする。制御変 数の値を与える3つのライブラリー関数XPRSgetintcontrol, XPRSgetdblcontrol, XPRSgetstrcontrolがある。特定の問題に対する与えられた制御変数の値にアクセスするためには、 型にあった正しい関数を用いなければならない。 XPRSmaximが設定された場合、解法が開始される前に問題を単純化するために、前処理解法が自動的 に呼び出されるのを見た。この標準的な操作は制御PRESOLVEによるが、これは最適化の最中に前処 理解法が実施されるか否かを指定する整数である。 演習問題 XPRSgetintcontrol(prob,XPRS_PRESOLVE,&presolve)を用いて制御PRESOLVEの整数値 presolveを検索し、これが標準的に設定された値に対応していることを確認せよ。 Xpress-MP ライブラリーから制御変数にアクセスするとき、それらの名前が接頭辞XPRS_を前に付け 52 ているという点でConsole Xpressにあるものと異なることに注意されたい。これは通常制御変数が 接頭辞なしで記述されるということが忘れられがちであるという点で重要である。 前処理解法の実施と停止 制御変数PRESOLVEを次のように設定する: 0 前処理解法なしで解法を開始する 1 前処理解法終了後に解法を開始する; The Xpress-MP 制御変数に新しい値を設定するには、関数XPRSsetintcontrol, XPRSsetdblcontrol, XPRSsetstrcontrolが用いられ、新しい値をそれぞれ整数、倍精度、文字列に対応させる。 演習問題 XPRSgetintcontrol(prob,XPRS_PRESOLVE,0)を用いて制御PRESOLVEの値を 前の問題に対して制御PRESOLVEを停止するときには0とする。これは変数の値によって影響を受ける XPRSmaximを呼び出す前になされなければならない。再びプログラムを実行して出力を観察せよ。 最後に紹介するこの型の関数の集合は、問題のいろいろな特性にアクセスすることを可能にするも のであって、求解プロセスの間にOptimizerによって設定される。XPRSgetintattrib, XPRSgetdblattrib、そしてXPRSgetstrattribなどの関数は制御値を得るものであって、同様にして 用いられる。この例は整数問題の特性LPSTATUSに関連して与えられるが、それらの値は解が最適か、 実行不可能か、非有界か、未終了かについての詳細を与える。リスト 4.23はこれがどのようにして 有効に用いられるかを示している。ここではこれ以上詳細については述べないが、モデリングにお けるOptimizerライブラリーの利用に関して探究を試みるために、もう一つの例を示そう。 リスト 4.23 解の状態をチェックする int lpstatus; … XPRSmaxim(prob,""); XPRSgetintattrib(prob,XPRS_LPSTATUS,&lpstatus); if(lpstatus == XPRS_LP_OPTIMAL) XPRSwriteprtsol(prob); else printf("Solution is not optimal\n"); "Problem attributes…" 問題の特性を用いて得られたLPSTATUSのすべての可能な値に関する完全なリストは、Optimizer Reference Manualに見られる。 最適化処理の対話方式 解くのに長い時間を要するとされる大きな問題に対しては、最適化プロセスの間にOptimizerからよ り多くの情報を得ることが有用であるとされている。Logファイルが準備されると、これを用いて解 がどのように進行しているかを決定することが可能となるが、これは不便で柔軟性がない。このよ うな状況に対して、Optimizerライブラリーはコールバックスcallbacksとして知られている”高度の” 関数の集合を与え、自身の関数を実行するために最適化プロセスの間の様々の時点でプログラムに 戻ることが可能となっている。コールバックスのほとんどはグローバル探索のいろいろな側面と関 連しており、これらについてはOptimizer Reference Manualに記述されている。しかしながら、よ り一般的な状況下において呼び出されるものも少しあり、おそらく最も単純なのは XPRSsetcbmessageである。これはテキスト行がOptimizerの出力となるたびに呼び出される関数を設 定し、その(部分的な)利用に関する例はリスト4.24に示されている。これは特に簡単な例であっ て、Optimizerによる標準的な出力のそれぞれの行を適当な接頭辞を付けて画面に表示するだけであ 53 る。 リスト 4.24 Optimizerライブラリーでコールバックスをを利用する void XPRS_CC callback(XPRSprob prob, void *user, const char *msg, int len, int msgtype) { char *usermsg = (char *)user; if(msgtype>=0) printf("%s: %s\n",usermsg,msg); } int main(void) { XPRSprob prob; char *usermsg = "simple.callback"; XPRSinit(NULL); XPRScreateprob(&prob); XPRSsetcbmessage(prob,callback,usermsg); … } Log ファイルはXPRSsetlogfileコマンドを用いて正しく設定される。この詳細については、Optimizer Reference Manualを 参照されたい。Usermsgは他の情報がコールバックへ送られないときにはNULLでよい。 The Xpress-MP 演習問題 コールバックスをプログラムに追加して、リスト4.24のようにOptimizer 出力を画面に プリントせよ。 メッセージの型は行の出力をOptimizerによって画面に表示するのに用いられる。メッセージの型は 出力のメッセージのレベルにしたがって、以下のように変化する。 演習問題 プログラムにあるコールバック関数を変更して、メッセージのレベルにしたがってプリ ントするときに別々にフォーマットせよ。 Optimizer Reference Manualには、ライブラリー利用者によって設定されたすべてのコールバック 関数の完全なリストが含まれている。これらが提供する可能性になじんでおくことは有用であるこ とに気づくであろうし、また大きな整数問題を定期的に解くような利用者にとっては、特に有用で ある。 XPRSsetcbmessage 出力の行表示がOptimizerによって作成されるたびに実行される関数を立ち上げる。表記方法: int XPRSsetcbmessage(XPRSprob prob, void (*uopf)(XPRSprob this_prob, void *this_user, const char *this_msg, int this_len, int this_msgtype), void *user); ここでthis_userは、利用者が定義可能なポインターであって、自身のデータがコールバック関数に 送られるようにするものである。this_msg は文字列の出力、this_len はその長さ、そして this_msgtypeはメッセージレベルを定義する整数である。 メッセージの型とレベル 1 通常のメッセージ(情報型) 2 通常のメッセージ(デバッグ型) 3 警告メッセージ 54 4 エラーメッセージ Libraries メモリーからのモデルの入力方法 Optimizerライブラリーは最大のXpress-MPライブラリーであって、大きく2種類の関数を有してい る。一つは基本的な関数であって、ほとんどがConsole Xpressコマンドの受け皿となっている。も う一つはライブラリー利用者にのみ利用可能な高度のコマンド群である。 高度のライブラリー関数には、問題をメモリーにある配列から直接Optimizerに読み込み、そこでそ れらを直ちに操作するのに必要なすべてのツールが含まれている。この節では、どのようにして簡 単な問題(リスト4.1)がこれらの関数を用いて直接入力され、そこでOptimizerとライブラリー関数 が 相互に機能するかを示す。 Optimizerライブラリーによる問題の入力方法 モデリング用語では、問題について記述する場合、常に制約条件と変数という言い方をするが、 Optimizerではそれらと構造的に等価な表現として行列の行と列を用いる。このことについては、解 がXPRSwriteprtsolによって作成されるのを前に見た。モデルがMoselによって転送される際には、 問題についての記述がOptimizerで解くために読み込めるように行列形式に変換される。この節では、 どのようにしてこの行列をOptimizerライブラリーによって直接作成するかということについて調 べてみよう。これは高度のライブラリー関数XPRSloadlpによって実行される。 XPRSloadlpは多くの引数を持っているが、例によって利用方法を説明しよう。49ページにあるリス ト4.1に注目するのがよいであろう。 XPRSloadlp これは問題をOptimizerのデータ構造の中に読み込むためのもので、すべての引数の完全なリストは Optimizer Reference Manualの中で見られる。 The Xpress-MP リスト4.25ではXPRSloadlpへのいろいろな引数が宣言され、前に述べたのと同じ簡単な問題に対す る値が割り当てられる。 • 問題に対する名前であって、ここでは”高度”を選んだ。前述のように、問題名はファイルが作成さ れ、特定されなければならない場合の基本となるものである。 • 上記の手続きに引き続いて、行列中の列(変数)の数が2 (a と b)、行(制約条件, 目的関数を含ま ない) の数も 2であると定義する。 次の6つの配列は、行列の行を構築する場合にモデルの制約条件についての詳細を与える。 • 最初に、目的関数を行とは見なさずに、”真の” 制約条件と考える。最初の配列qrtypeは、モデル 中の各制約条件の型を特定する。これは各行に対して‘L’を用いることによって、XPRSloadlpに送ら れる。 •上記の手続きに引き続いて、制約条件の右辺値が一つづつ定義される。最初の値は制約条件からも たらされるが、2番目の値は 3a 2b + 400 . a 3b + 200 . からもたらされる。 リスト 4.25 XPRSloadlpに対する引数を宣言する char probname[] = "advanced"; int ncol = 2; 55 int nrow = 2; char qrtype[] = {’L’,’L’}; double rhs[] = {400.0, 200.0}; int mstart[] = {0, 2, 4}; int mrwind[] = {0, 1, 0, 1}; double dmatval[] = {3.0, 1.0, 2.0, 3.0}; double objcoefs[] = {1.0, 2.0}; double dlb[] = {0.0, 0.0}; double dub[] = {200.0, 200.0}; . Libraries • 次の2つの配列は、以下で定義される行列要素に対する分岐点と出発点について記述するもので ある。これらについては、以下のパラグラフで詳細を述べる。 • 配列dmatvalは、行列の要素になる制約条件に含まれる実際の係数を含んでいる。これは垂直方向 に並んだ1次元配列であって、一度に一つづつの変数の非零要素を入れたものである。行列を左か ら列ごとに効率的に特定する。 • 最後に、目的関数の係数が特定される。最後の2行はモデルの変数に関する上限と下限を与える。 おそらくこのリストの中で最も興味ある要素は配列mstart と mrwindである。行列要素に対する分 岐点と出発点によって行列を定義する方法は、多くの零要素を持つ問題を記述する非常に効率的な 方法である。 これまでわれわれが考えた最も簡単な例は零要素を持たないので、これらの利用方法はそれほど明 白ではないかも知れない。そこで次のような例を考えてみよう。問題1の行列は20個の要素を持ち、 そのうち10個は零である。このモデルと同じように、大きな問題は通常疎であるので、XPRSloadlp が用いられる。問題が疎な形式であれば、より効率的である。これを利用したリスト4.26に与えら れている。 問題 1 Maximize: 0.1x1 + 0.2x2 + x3 Subject to: x1 + 2x2 = 0 x3 + x4 - x5 = 0 x4 - 0.27x5 >= 0 3x1 + x4 - 0.4 x5 <= 0 リスト 4.26 疎行列データを入力する int mstart[] = {0, 2,3,4, 7, 10}; int mrwind[] = {0,3,0,1,1,2,3, 1, 2, 3}; double dmatval[] = {1,3,2,1,1,1,1,-1,-0.27,-0.4}; "Offsets and start points…" 多くの零要素を有する行列は疎といわれる。 The Xpress-MP 行列の要素 mstart は、Optimizerに対してdmatvalにあるデータが下記にしたがって行列の列に分 けられることを知らせる。 dmatval[0], dmatval[1] 列0; dmatval[2] 列1; dmatval[3] 列2; dmatval[4], dmatval[5], dmatval[6] 列3; 56 dmatval[7], dmatval[8], dmatval[9] 列4. このようにして、mstartにある要素は、dmatvalに与えられる一連のデータの中にある列に対する出 発点(と終了点)を定める。したがってmstart配列は、列i が mstart[i]で始まり、列mstart[i+1]-1 で終わるというように、列の数より1つだけ多くの要素を含む。列が定義されると、mrwindに含ま れる情報は、各列に対する要素を下記のように正しい行におく。 dmatval[0] 行0; dmatval[1] 行3; dmatval[2] 行0; dmatval[3] 行1; dmatval[4] 行1; dmatval[5] 行2; dmatval[6] 行3; dmatval[7] 行1; dmatval[8] 行2; dmatval[9] 行3. 一度位置が特定されると、要素自体はdmatvalにあるデータから決定される。われわれの”高度の”問 題に戻ると、リスト4.25は列0の行0と1にあるdmatvalの最初の2つの要素、そして列1の行0と1にあ るdmatvalの3番目と4番目の要素をおく。XPRSaddnamesを用いると、定められた行列の中の変数に名 前が付けられる。リスト4.27では、XPRSloadlpを用いた行列の定義とともに、このことを示す。変 数名は文字配列colnamesに無制限リストとして保存される(したがって\0文字が2つの間にある)。 すべての行と列の添字はCでは0から始まることを覚えておいて欲しい。 "Anyway…" Libraries 問題の解を得る方法 問題の行列が定められると、目的関数は前のように最大化される。しかしながらわれわれはこれま では解を見たり、プリントしたりするためにファイルに出力してきたが、Optimizerライブラリーで はもう一つの関数XPRSgetsolを有しており、それによって利用者のプログラムから解へ直接アクセ スすることが可能となる。 リスト 4.27 問題を読み込み、変数名を付ける XPRSprob prob; char colnames[] = "a\0b"; ... XPRSinit(NULL); XPRScreateprob(&prob); XPRSloadlp(prob, probname, ncol, nrow, qrtype, rhs, NULL, objcoefs, mstart, NULL, mrwind, dmatval, dlb, dub); ... XPRSaddnames(prob,2,colnames,0,ncol-1); XPRSaddnames 行列に行や列の名前を付ける。以下の5つの引数がある。 • 問題; • 名前が行か列かによって1か2の値をとる。 • 名前の無制限な配列 • 最後の2つの引数は行列の最初と最後の行(列)に名前を付与する。 XPRSgetsol 問題に対する解を検索する。問題に対して4つの引数があり、解に関するどのような情報が必要と されるかによるが、いずれもNULLでもよい。 ここで2つのNULLは行の範囲と各列の非零要素の数に関連している。問題を特定した場合は、これらは必要ではない。詳細 57 については、Optimizer Reference Manualを参照されたい。 The Xpress-MP リスト4.28ではいろいろな変数の最適値を得てそれらを画面に出力するためにはどうすればよいか を示す。XPRSgetsolによって得られない解の一部としては、目的関数値がある。この値を戻すよう な高度ライブラリー関数は存在しない。これはむしろLPOBJVALという問題の特性の中に保存されて おり、前述の問題の状態に関するアクセスと同様にして得られる。これを実行するコードはリスト 4.29に示される。 演習問題 リスト4.25とリスト4.29の概念を結合して、リスト4.1の問題を直接読み込んで解き、解 を画面に出力するプログラムを書け。得られた値が前の演習問題と一致することを確かめよ。 リスト 4.28 変数の値を得て提示する #include <stdlib.h> double *vars; ... vars = malloc(ncol*sizeof(double)); XPRSgetsol(prob,vars,NULL,NULL,NULL); printf("a = %2.1f\nb = %2.1f\n", vars[0], vars[1]); リスト 4.29 目的関数の値を得て提示する double lpobjval; ... XPRSgetdblattrib(prob, XPRS_LPOBJVAL, &lpobjval); printf("The objective value is %2.1f\n", lpobjval); Libraries 問題の行列の変更 Optimizerライブラリーでは、行列の読み込みがなされた後に、それを変更することも可能である。 例として、XPRSaddrowsを用いて、追加制約がどのようにして問題に付加されるかについて述べよう。 いま、追加制約を問題に付加したいとする。これをどのようにして実現するかについては、リスト 4.30にある追加の行が必要であることを示している。XPRSloadlpは線形制約条件式の変数に関する 係数を特定し、aclindはamatvalの要素に関する列の添字を特定し、そしてastartは新しい行に対す る要素最初の分岐点を特定する。この場合、新しい行が行列の中の要素0にあるamatval[0] (列0) と要素3にあるamatval[1] (列1)に付加され、行列の列ごとに非零要素を特定する。XPRSaddrowsの 2番目と3番目の引数は新しい行の数と付加された行の非零要素の数である。 演習問題 前の問題に対して、解く前に新しい行を追加するようにプログラムを変更せよ。プログ ラムを実行すると、新しい目的関数値が169.2に減少するはずである。これを達成するのに必要な決 定変数の値はいくつか。 XPRSaddrows Optimizerのデータ構造にある行列に追加の制約行を加える。 リスト 4.30 行列に追加の行を加える char atype[] = {’L’}; double arhs[] = {800.0}; int astart[] = {0,2}; int aclind[] = {0,1}; double amatval[] = {6.0,5.0}; 58 ... XPRSaddrows(prob,1,2,atype,arhs,NULL,astart,aclind, amatval); これを前のリスト4.5にある‘変更’ モデルと比較せよ。 6a 5b + 800 . The Xpress-MP 問題の全域要素の追加 Moselモデルプログラミング言語を用いると、決定変数が整数値になるように問題を変えるにはモデ ルのプログラムファイルにわずか2行を追加すればよいことを前に見た。同様に、Optimizerライブ ラリーを用いると、決定変数が整数値になるように特定することが可能であるが、この場合、問題 はもはやXPRSloadlpを用いて読み込むことはできず、代わりに関数XPRSloadglobalを用いなければ ならない。 XPRSloadglobalの最初の14個の引数はXPRSloadlpの場合と全く同様であるので、これ以上は述べな い。残りの引数のうちで、2,3についてここで述べる。リスト4.31でそれらの宣言を与え、設定 を行う。 • 新しい引数のうちで、ngentsは、問題中のグローバル要素の数を表し、ここでは、2つの変数が 整数であるので、2となる。 XPRSloadglobal グローバル要素を有する問題をOptimizerデータ構造に読み込む。最初の14個の引数はXPRSloadlp の場合と全く同様である。残りの引数については、Optimizer Reference Manualを参照されたい。 リスト4.31 ライブラリーを用いて整数問題を読み込む int ngents = 2; int nsets = 0; char qgtype[] = {’I’,’I’}; int mgcols[] = {0,1}; XPRSloadglobal(prob, probname, ncol, nrow, qrtype, rhs, NULL, objcoefs, mstart, NULL, mrwind, dmatval, dlb, dub, ngents, nsets, qgtype, mgcols, NULL, NULL, NULL, NULL, NULL); ‘グローバル要素’はXpress-MPの傘下で非連続変数と特定順序つき集合として用いられる。SOSを有する問題に対するその他の 引数に関する詳細は、Optimizer Reference Manualを参照されたい。 Libraries • 整数 nsetsは問題の中の特定順序付集合Special Ordered Sets(SOSs)の数を表す。ここでは用い ない。 • 配列 qgtype は両変数が整数値となるように設定する。 • mgcols はこのような型を設定された変数の数を表す。 最後の5つの引数はすべてSOSに関連している。ここではこれらを用いないので、NULLと設定される。 このような混合型整数問題に対しては、最適な目的関数値を得るために問題の特性であるLPOBJVAL を用いるべきではない。代わりに特性MIPOBJVALが用いられなければならない。グローバル(MIP)探 索をしている間は、いくつかのLP問題が解かれ、それぞれはいろいろなグローバル変数に関する追 加的な限界を与えることによって可能な解に絞り込んでいくという操作をすることになる。グロー バル探索が終わると、特性値LPOBJVALに最後のLP問題に対する最適な目的関数値が得られる。しか しながら、これがMIPOBJVALに保存されているグローバルな最大値(解かねばならなかったすべての LP問題の中で)である必要はない。さらに興味のある利用者はOptimizer Reference Manualを参照 59 されたい。 演習問題 元の問題(わずか2本の制約を有する問題)を変更して整数問題を読み込め。これを解 いて、解が前に解いた最後の整数問題と一致することを確かめよ。 演習問題 これを変更して、追加制約を有する新しいグローバル問題を読み込め。これはa が 107 でb が 31のときに最適値を有する。これを解いて確認せよ。 このライブラリーを用いたモデリングに関するより詳細な情報はOptimizer Reference Manualの中 にある。そこでは、読み込まれた問題を変更したり、解いたりするのに必要なすべての関数が示さ れている。 すべての処理方法について述べたわけではないが、少なくともモデリングに関してこの方法の利点 については紹介することができたはずである。この章を終えるにあたっては、Xpress-MP ライブラ リーの利用方法を他の言語を用いて説明する前に、利用者にとって有用なXpress-MP ライブラリー についてのいくつかの一般的なコメントを与えることにする。 変数の添字は0から始まる変数配列の位置を表す。 6a 5b + 800 . The Xpress-MP Libraries 4 Xpress-MP ライブラリーから最良解を得ること エラーの検出方法 ほとんどすべてのライブラリー関数は、関数の呼び出しがうまくいった場合には整数値0を返し、 そうでないときは非零値を返すようになっている。このような関数の返却値を利用すると、プログ ラムは起こりうるエラーに対処することができ、前もってこれらの値をチェックすることによって 問題の診断が可能となる。この章のこれまでのリストではいずれにおいてもエラーチェック機能を 利用していないが、これは純粋に明白にするためになされることであるので、ライブラリー関数が 利用されるか否かに関わらず、あらゆるプログラムの中で利用者がエラーチェック機能を用いるこ とを奨めたい。 Optimizerライブラリーを用いた基本的なエラーチェック機能の例は、リスト4.32に与えられている。 この例は特に単純であって、単にメッセージをプリントし、エラーがある場合に終了するというも のである。実際、当然のことであるが、プログラムの中でエラーを扱う手続きとしては、もっと複 雑なものが可能であるが、これらについては、読者に任せることにする。 リスト 4.32 基本的なエラーチェック #include <stdio.h> #include <stdlib.h> #include "xprs.h" void error(XPRSprob my_prob, const char *function) { char errmsg[256]; XPRSgetlasterror(my_prob,errmsg); printf("%s did not execute correctly:\n\t%s\n", function, errmsg); if(my_prob != NULL) XPRSdestroyprob(my_prob); XPRSfree(); exit(1); 60 } int main(void) { XPRSprob prob=NULL; Libraries 演習問題 この章で用いたプログラムの一つをエラーチェック機能を持つように書き換えよ。 BCL とOptimizer ライブラリーの結合 利用者が必要とするすべての作業を、基本的なBCL関数だけでOptimizerの中で実行するには十分で ない場合がある。解決策は、問題を構築して、それをBCLを用いてOptimizerの中に読み込み、あと でOptimizer関数を呼び出すことである。こうすることによって、Optimizerライブラリーのすべて の機能を問題に対して利用することが可能となるが、これ以降はBCLを用いて問題にアクセスするこ とはできないという欠点がある。あらゆる求解段階においてBCLを用いて問題にアクセスしたい場合 は、上述のように、BCL関数を用いて最適化作業を実行する必要がある。BCL とOptimizer ライブラ リーを結合するには、2つの段階が必要である。最も単純な場合は、ライブラリー関数を用いて Optimizer に読み込めるように、MPS ファイル (リスト4.16にあるように)を書くためにBCLを用い るということである。しかしながらASCIIファイルを作成し、読むためには、かなりの時間を要する ディスクアクセスが必要である。このため、問題を直接Optimizerに転送するためにBCLが用いられ る。 if(XPRSinit(NULL)) error(prob,"XPRSinit"); if(XPRScreateprob(&prob)) error(prob,"XPRScreateprob"); if(XPRSreadprob(prob,"simple","")) error(prob,"XPRSreadprob"); if(XPRSmaxim(prob,"")) error(prob,"XPRSmaxim"); if(XPRSwriteprtsol(prob)) error(prob,"XPRSwriteprtsol"); if(XPRSdestroyprob(prob)) error(prob,"XPRSdestroyprob"); XPRSfree(); return 0; } リスト 4.32 基本的なエラーチェック BCLとOptimizer ライブラリーによって用いられる問題ポインターが異なるために、このようにして ライブラリーを結合することはかなり複雑なものとなる。その結果、どのような問題が用いられる としても、最初にBCL、そして次にOptimizer ライブラリーに送られなければならない。リスト4.33 にこれがどのようにしてなされるかを示す。 リスト 4.33 BCLとOptimizer ライブラリーを結合する #include <stdio.h> #include "xprb.h" #include "xprs.h" double FIRST[] = {3,2}; 61 double SECOND[] = {1,3}; double PROFIT[] = {1,2}; int main(void) { XPRSprob opt; /* Optimizer problem pointer */ XPRBprob bcl; /* BCL problem pointer */ XPRBarrvar x; XPRBctr profit; bcl = XPRBnewprob("combined"); x = XPRBnewarrvar(bcl,2,XPRB_PL,"x",0,200); XPRBnewarrsum(bcl,"First",x,FIRST,XPRB_L,400); XPRBnewarrsum(bcl,"Second",x,SECOND,XPRB_L,200); profit=XPRBnewarrsum(bcl,"Profit",x,PROFIT, XPRB_N,0); XPRBsetobj(bcl,profit); /* load the matrix into the Optimizer */ XPRBloadmat(bcl); /* obtain the problem pointer for the Optimizer */ opt = (XPRSprob)XPRBgetXPRSprob(bcl); Libraries この例では、BCLの関数 XPRBnewprobはBCLとOptimizerを同時に初期化するので、関数XPRSinitを別 に呼び出す必要はない。この関数からの出力はBCLの問題ポインターであって、それが問題に対して 機能するすべてのBCL関数によって利用される。行列がXPRBloadmatを用いてOptimizerの中に読み込 まれ、その後にOptimizerの問題ポインターが必要となる。これは関数XPRBgetXPRSprobを用いてBCL によって得られるが、その後Optimizer関数が前と全く同様にして用いられる。 演習問題 リスト4.33の例を構築し、実行を試みよ。MPSファイルが作られ、ライブラリーが機能し て完成した行列がBCLからOptimizerに直接送られるのを確認せよ。 Optimizer関数が呼び出された後は、BCLから問題にアクセスすることはもはや不可能となることに 注意して欲しいことを再度述べておこう。これが必要な場合は、BCL解のルーティンがOptimizerル ーティンに先だって用いられなければならない。 XPRSmaxim(opt,""); XPRSwriteprtsol(opt); /* tidy up using the BCL function */ XPRBdelprob(bcl); XPRBfree(); return 0; } XPRBloadmat Optimizer関数をOptimizerで動かすために行列を読み込む。 XPRBgetXPRSprob BCL によってOptimizerに読み込まれた問題に対するOptimizer問題ポインターを返す。 リスト 4.33 BCLとOptimizer ライブラリーを結合する The Xpress-MP 62 Visual BasicとXpress-MPライブラリーの利用 Xpress-Optimizerライブラリーは単独で、あるいはExcel, Access の中で用いられるVisual Basic for Applications (VBA) としてVisual Basicとともに用いられる。ここでは単に‘VB’として両者を 表すことにする。読者がVBを用いない場合は、ここを飛ばしてもよい。 Xpress-MPライブラリーは、VBモジュールの中で定義される外部関数宣言xpress.basを用いたVBプロ ジェクトに組み込まれ、Xpress-MP ライブラリーの一部として配布されている。Xpress-MPライブラ リー関数自体は、C関数と同じ名前を有するが、これによって利用者はReference Manualsにあるプ ロトタイプと対応するVBとの間で容易に翻訳ができるようになっている。Xpress-MPライブラリー関 数は長い値を返すが、この例はリスト4.34に与えられ、これが前の問題であるリスト4.18のVB版と なっている。この節の残りの部分では、C と VBのインターフェイスの間の相異について述べ、この 環境におけるライブラリー利用者の陥りやすい落とし穴について紹介する。ここで用いるアプロー チは本質的にはまとめであるので、利用者はOptimizer Reference Manualをも参照するのが有用で あろう。 リスト 4.34 VBの中でXpress-MP ライブラリーを用いる Public Sub main() Dim nReturn As Long nReturn = XPRSinit("") nReturn = XPRScreateprob(prob); nReturn = XPRSreadprob(prob,"simple","") nReturn = XPRSmaxim(prob,"") nReturn = XPRSwriteprtsol(prob) nReturn = XPRSdestroyprob(prob) nReturn = XPRSfree() End Sub このモジュールのコピーは、NULL引数を用いる関数プロトタイプを定義するときに結局はカストマイズすることになるので、 利用者のプロジェクトに加えられなければならない。ここでは明白にはしていないが、すべてのライブラリー関数が返す値 をチェックすることを奨める。詳細については、95ページにある“エラーチェック”を参照されたい。 Libraries 内部ファイル Xpress-MP分布の一部分であるファイルmodbyte.basには、この章で用いられるいくつかの有用な関 数の定義が含まれており、このファイルはリストに述べられているプログラムのソースの中に含ま れていることを前提としている。特に、関数ByteConversionがここで定義され、その結果、以下の いくつかのリストはこれらの関数なしでは機能しない。この関数はCD-ROMにあるディレクトリー \examples\optimizer\vbに見られる。 主要構成 引数の型: ルーティンに渡されるVB パラメタは、Cパラメタと以下に示すような関連がある。 定数: C定数は同じ名前を有するVBグローバル定数となる。例を示す。 Global Const XPRS_PLUSINFINITY = 1E+20 Global Const XPRS_LPLOG = 9 数値配列: Optimizer libraryにあるいくつかのルーティンでは数値配列が一つの引数に渡されるこ とが必要である。例はルーティンXPRSgetobjによって与えられるが、その2番目の引数は倍精度配 列である。これをVBからOptimizerに渡す例はリスト4.35に与えられる。注目すべき重要な点は、配 列の最初の要素だけが渡され、配列全体ではないということである。これはすべての数値配列が Xpress-MPとVBの間を行き来する場合である。 63 C 引数の型 int x int *x double x double *x char *x VB 引数の型 ByVal x As Long x As Long ByVal x As Double x As Double ByVal x As String or x As Byte The Xpress-MP 文字配列: 文字配列をいろいろなライブラリーサブルーティンに渡すことがときどき必要となる。 VBでは、文字配列はByte変数型で渡されるが、たとえばXPRSchgboundsの定義の中では、次のように なる。: Declare Function XPRSchgbounds Lib "XPRS.DLL" _ (…, qbtype As Byte,…) As Long これを用いるには、バイト配列がリスト4.36にあるAsc関数を用いてVBコードで初期化されなければ ならない。これは"LU" に対するqbtype配列を初期化して、XPRSchgboundsルーティンにすぐに渡さ れるようにするものである。Optimizerライブラリールーティンでは、利用者が多重列を含む文字配 列を渡すことが必要とされる。これもByteデータ型を用いてなされなければならない。各系列を分 離するには、C 系列終了記号 (バイト値 0)を用いることが必要であって、これにはVB内で何らかの 変換がなされなければならない。この変換をXpress-MP利用者が容易に行うために、Xpress-MP ライ ブラリーによって提供されるVBの例の中にファイルmodbyte.basが含まれたことがある。このモジュ ールは一連のVB 配列を有するByteConversion関数を含み、Xpress-MPルーティンに渡されるバイト 配列を返す。リスト4.37にこれがどのようにしてなされるかを示す。Optimizerライブラリーの中の ルーティンには、バイト配列形式でVBへの文字配列を返すものもある。これらのバイト配列はVBの 操作をより容易にするために記号列に翻訳される。たとえば3つの要素のバイト配列を3つの要素 の文字配列に変換するためにリスト4.38にあるコードが用いられる。 リスト 4.35 数値配列を渡す Dim gpObjCoef(2) As Double Dim nReturn As Long nReturn = XPRSgetobj(prob,gpObjCoef(0),0,2) リスト 4.36 VBのバイト配列を初期化する Dim qbtype(1) As Byte qbtype(0) = Asc("L") qbtype(1) = Asc("U") "Passing character arrays…" "Returning 64 character arrays…" Libraries Optimizerライブラリーの中の他のルーティンは記号配列をVBに戻すが、そこではByte から String への変換が有効になされなければならない。たとえば、XPRSgetnamesルーティンは2番目の引数と してバイト配列を持っているが、そこでOptimizerから行あるいは列の名前を受け取る。これはリス ト4.39のコードを用いて容易に記号配列に翻訳される。 リスト 4.37 ByteConversion 関数を用いる Dim sRowName(3) As String Dim bRowName() As Byte sRowName(0) = "c1": sRowName(1) = "c2" sRowName(2) = "c3": sRowName(3) = "c4" ’ Transform the row names in bytes bRowName = ByteConversion(sRowName,4) リスト 4.38 バイト配列を文字配列に変換する For i = 0 To 2 sRowType = sRowType + Chr(qrtype(i)) Next I リスト 4.39 バイト配列をVBの記号列に翻訳する Dim nReturn As Long Dim nmLen As Long Dim i, j As Long Dim nRow As Long Dim bV() As Byte Dim sV() As String nReturn = XPRSgetintcontrol(prob, _ XPRS_MPSNAMELENGTH, nmLen) nReturn = XPRSgetintattrib(prob, XPRS_ROWS, nRow) "Converting arrays from Byte to String…" NULL 引数: ルーティンによってはnull引数を用いてCから呼ばれるものもあるが、これは引数が無 視されることを意味する。null引数をVBに渡すのと同じことを実行するために用いられるデータ構 造は、String型のvbNullString定数である。これを利用するためには、xpress.basの関数プロトタ イプは変更されなければならないことが多く、別のプロトタイプが関数引数リストにあるnull引数 のそれぞれの構成要素に対して宣言される。例として、Cコードと同じことを実行したい場合を仮定 しよう。 nReturn = XPRSgetsol(prob,x,NULL,NULL,NULL); ここで後半の3つの引数リストの値は不要である。VBにおいて、xpress.bas ファイルにある XPRSgetsol関数のプロトタイプは、以下のように宣言される。 Declare Function XPRSgetsol Lib "XPRS.DLL" _ (ByVal XPRSprob As Long,x As Double, _ 65 slack As Double,dual As Double,dj As Double) _ As Long 最後の3つの引数とともにvbNullStringの利用を可能にする、カストマイズされたXPRSgetsolの定 義は、以下のようになる。 Declare Function XPRSgetsolx Lib "XPRS.DLL" _ Alias "XPRSgetsol" (ByVal XPRSprob As Long, _ x As Double, ByVal slack As String, _ ByVal dual As String, ByVal dj As String) _ As Long ReDim bV(nmLen * nRow + nRow - 1) ReDim sV(nRow - 1) nReturn = XPRSgetnames(prob, 2, bV(0), 0, nRow - 1) For i = 0 To nRow - 1 For j = 0 To nmLen - 1 sV(i) = sV(i) + Chr(bV(i * (nmLen + 1) + j)) Next j Next I リスト 4.39 バイト配列をVBの記号列に翻訳する 修正された引数形式を有する新しい関数は、リスト4.40にあるように、次々に用いられる。 プロトタイプXPRSgetsolxとXPRSgetsolルーティンに関する他の有用なプロトタイプはxpress.bas の中で定義されることに注意されたい。カストマイズされたプロトタイプを必要に応じて中央の xpress.bas ファイルに追加することはもっと容易であることに気づくであろう。 Visual Basicのコールバックスの利用 Optimizer コールバックスを利用するには、Visual Basicの中で正しいパラメタを有する関数ある いはサブルーティンを宣言し、そのアドレスを適切なXPRSsetcbルーティンに渡すことが必要である。 これはAddressOfオペレーターを用いて得られる。しかしながら、コールバックスでは利用者によっ て定義されたオブジェクトがコールバックス関数に最終引数として渡されるので、コールバックス に対する新しいプロトタイプが定義されなければならない。 この例は上に示したもので、XPRSgetsol関数のプロトタイプがvbNullStringを引数として受け入れ るように変更されなければならなかった。この例はリスト4.41に与えられる。ここでは2つの記号 列と2つの整数がコールバックスに渡され、そこでオブジェクトMyInfoがこのようなデータを含む ように定義される。続いて、コールバックス関数XPRSsetcblplogは、2番目の引数として型MyInfo のデータを受け取るように定義された新しいプロトタイプを持つことになる。コールバックスが立 ち上がると、オブジェクトmilへの指示が関数に渡される。 リスト 4.40 null 引数をVBの中で用いる Dim x() As Double Dim cols As Long nReturn = XPRSgetintattrib(prob,XPRS_COLS,cols) ReDim x(cols-1) Call XPRSgetsol(prob, x(0), vbNullString, _ vbNullString, vbNullString) 66 リスト 4.41 Visual Basicの中でコールバックスを用いる Public Type MyInfo int1 As Long int2 As Long var1 As Variant var2 As Variant End Type … Declare Function XPRSsetcblplog Lib "XPRS.DLL" _ (ByVal XPRSprob As Long, _ ByVal Address As Long, _ ByRef p As MyInfo) As Long … Public Function lplog(ByVal prob As Long, _ ByRef mi As MyInfo) As Long … End Function … mi1.var1 = "The first string" mi1.var2 = "The second string" mi1.int1 = 1234 mi1.int2 = 5678 nReturn = XPRSsetcblplog(prob, AddressOf lplog, mi1) Java によるXpress-MP ライブラリーの使用方法 BCL と Optimizer ライブラリーはJavaとともに用いられるが、標準的なJava Native Interfaceを サポートする最近のJava Virtual Machine (JVM)が必要である。Microsoft JVMは標準的ではなく、 通常はサポートしていない。Java ライブラリーのクラスはパッケージcom.dashoptimization.*に含 まれている。このパッケージはXpress-MP ライブラリーを用いるJavaソースファイルに入っている ことを奨める。BCLへの入口はこのパッケージに見られるクラスXPRBprobである。Optimizerでは、 クラスXPRSprobである。 Java を用いたBuilder Component Library (BCL) BCLのJava インターフェイスは、データの入力、出力、エラー処理以外のすべてのCの機能を提供す るため、そこでは標準的なJava関数が用いられなければならない。変数、制約条件などのモデリン グ要素と問題はすべてクラスに変換され、関連する関数はJavaの中の対応するクラスの手法に変換 される。このようなすべてのクラス (たとえば XPRBprob, XPRBvar, XPRBctr, XPRBsos, XPRBindexSet, XPRBbasis) はXPRBindexSetを例外として同じ名前を有し、そこではJavaの大文字使 用形式が採用されている。Java インターフェイスのすべての詳細については、CD-ROMにあるBCL Reference Manual と JavaDoc資料を参照されたい。使用例はリスト4.42に与えられるが、これは章 全体で用いられる例のJava版である。この例のいくつかの特徴は注目に値する。Java インターフェ イスでは、初期化と一般的なソフトウェアの状態(init と freeを含めて)とすべてのパラメタの定 義に関連する方法を含むXPRBクラスを利用する。実際、これは、標準BCLにおける接頭辞XPRB_を有 するすべてのパラメタはJavaのクラスXPRBの定数として指定されなければならないことを意味する。 たとえば標準BCLにおけるXPRB_BVはここではXPRB.BVとなる。 67 The Xpress-MP Javaでは、制約条件の定義のために代数的オペレーターを上書きすることは、C++の場合のように可 能ではない。代わりに、いくつかのクラスがこの操作を補うために追加されている。たとえば線形 表現(クラスXPRBlinExp)は制約条件や特定順序付き集合Special Ordered Setsを定義するために必 要となる。また2次表現expressions (クラスXPRBquadExp)は2次の目的関数を用いるのに用いられ、 線形関係(クラス XPRBlinRel)は制約条件を定義するときの媒介として用いられる。さらにadd ある いは eqlのような簡単な方法の集合が与えられ、いろいろな型やパラメタの数を受け入れるように 上書きされる。 リスト 4.42 Javaの中でBCLを用いる import com.dashoptimization.*; public class simple { static XPRBprob p; public static void main(String[] args) { XPRBvar a; XPRBvar b; XPRB.init(); p = new XPRBprob("simple"); a = p.newVar("a",XPRB.PL,0,200); b = p.newVar("b",XPRB.PL,0,200); p.newCtr("First",a.mul(3).add(b.mul(2)).lEql(400)); p.newCtr("Second",a.add(b.mul(3)).lEql(200)); p.setObj("Profit",a.add(b.mul(2))); p.maxim(""); System.out.println("Profit: "+p.getObjVal()); System.out.println(" a = "+a.getSol()); System.out.println(" b = "+b.getSol()); XPRB.free(); } } 方法 isValid はBCLに対する有用な追加機能であるが、getVarByName, getCtrByNameなどの方法と ともに用いられなければならないし、また説明を要する。これらの方法は常に標準BCLにおける対応 する関数ではなく、希望する型のオブジェクトを返すが、それが見つからない場合はNULLポインタ ーを返す。方法isValidによってのみ、オブジェクトが妥当であるか否か、それが問題の定義に含ま れているか否かをテストすることが可能となる。すべてのオブジェクトの状態は、このようにして 使用前にチェックされなければならない。 この節の残りの部分では、C とJavaのインターフェイスの間の違いがOptimizerライブラリーで論じ られる。しかしながら、後に“主要構造”の節で論じられる大部分の事柄は、ライブラリー と BCL利 用者の両方に関連するもので、これらを適用するのは難しくはないはずである。 Java を用いたOptimizer ライブラリー Optimizer Reference Manualに述べられているルーティン(方法)のほとんどは、XPRSprobクラスの メンバー関数である。XPRSprobメンバー関数は、XPRS接頭辞を省略したりJavaの大文字形式を採用 68 したりするにもかかわらず、利用者がC プロトタイプと対応する Java用語の間の翻訳作業を容易に 行えるように、実質的にはCから名前を持ってくる。しかしながら、これらのページに論じられる重 要事項が2,3ある。Javaインターフェイスのより詳細については、CD-ROMにあるJavaDoc資料を参 照されたい。 使用例はリスト4.43に与えられているが、これも章全体で用いられる例のJava版である。 リスト 4.43 JavaでOptimizer ライブラリーを用いる import com.dashoptimization.*; import java.io.*; public class simple { public static void main(String[] args) { この例で注目すべきは、Javaインターフェイスが静的なメンバーだけを含むXPRSクラスを利用して いるということである。これらには2つの方法 init とfreeだけでなく、定数、制御、そして特性 の名前が含まれている。関数initは上書きされるので、標準的なパスがライセンスの詳細について チェックされる場合でも、null も null 記号も引数としては不要である。 freeは例外は許さないものの、initはXPRSprobExceptionよりもむしろ標準的な例外を有している。 制御、そして問題の特性の名前の使用方法の例については、111ページの“制御と問題の特性” の節を参照されたい。 JavaインターフェイスはC Optimizer 関数XPRScreateprob と XPRSdestroyprobに相当するものを 含まない。問題のオブジェクトの作成と削除はXPRSprobの構築部分とfinalize法の中で処理される。 try { XPRS.init(); } catch (Exception e) { System.out.println("Cannot initialize"); } try { XPRSprob prob = new XPRSprob(); prob.readProb("simple",""); prob.maxim(""); prob.writePrtSol(); } catch(XPRSprobException xpe) { xpe.printStackTrace(); } XPRS.free(); } } 69 リスト 4.43 JavaでOptimizer ライブラリーを用いる 4The Xpress-MP 主要構成 データの型: ルーティンに渡されるJavaのパラメタは、以下のようにC型式と関連している。型 IntHolder, DoubleHolder, StringHolderはすべてcom.dashoptimization パッケージの中にあるこ とに注意されたい。 数値配列: Java配列を用いて整数と倍精度の配列を渡すということはただちに可能である。配列は 常に関数を呼ぶ前に割り当てられていなければならない。例を示そう。 double sol[] = new double[ prob.getIntAttrib(XPRS.COLS)]; prob.minim(""); prob.getSol(sol, null, null, null); 失敗すると、JVMを壊すことになる。 数値参照: Cのポインターを用いて整数と倍精度の数値を関数から得るには、IntHolder と DoubleHolderの2つのラッパーwrapperクラスのうちの一つが用いられる。これらのクラスのそれぞ れはvalueという名の各型の単一共有変数を含んでいる。たとえば IntHolder ni=new IntHolder(); prob.getIndex(1,"rowname",ni); C 引数の型 int* (array of int) int* (reference to an int) double* (array of double) double* (reference to a double) char* (array of char) char* (pointer to a ‘\0’ terminated string passed to a routine) char* (pointer to a ‘\0’ terminated string received from a routine) Java 引数の型 int [] IntHolder double [] DoubleHolder byte [] java.lang.String StringHolder System.out.println("Index of ’rowname’ is: "+ni.value); 整数(あるいは倍精度)の両者を必要な場合には、それを関数に送る前に適切な値で値域を初期化す るということを知っておいて欲しい。 文字配列と記号列: Optimizerルーティンには、記号列を修正前にパラメタとして送っておくことが 必要である。この場合、java.lang.String が用いられ、たとえば prob.readProb(args[0],""); ここで args[0] は java.lang.Stringである。もし送られた記号列が呼ばれた関数によって修正さ 70 れる必要がある場合には、クラスStringHolderが用いられる。たとえば StringHolder p_name=new StringHolder(); prob.getProbName(p_name); System.out.println("The problem name is: " +p_name.value); 最後に、文字配列がルーティンによって必要とされた場合には、前もって割り当てられていたbyte[] のJava配列を用いよ。 制御と問題の特性: Optimizerで用いられるすべての制御と問題の特性は、XPRSクラスの静的な最終 メンバーである。ここにそれらがどのようにして用いられるかを示す2つの例がある。 // get a problem attribute double sol[] = new double[ prob.getIntAttrib(XPRS.COLS)]; // set a control prob.setIntControl(XPRS.LPLOG,10); エラーをチェックする: 場合によっては、すべてのXPRSprobメンバー関数は把握されなければなら ない例外を作成する。例外クラスXPRSprobException はjava.lang.Exceptionから得られるが、それ に対して2つのメンバー関数を追加する。これらは、例外と関連するOptimizerエラーコードを返す public int getCode();と例外がもたらされる原因となった問題のオブジェクトの参照箇所を返す public XPRSprob getXPRSprob();の2つである。 場合によっては、XPRSprobオブジェクトは例外がもたらされた場合でも正当とし、このチェックは getCode (9章のOptimizer Reference Manualにある “エラーメッセージと返却コード” を見よ。)の 値を調べてはじめて決定される。XPRSprobが正当として、利用者のアプリケーションの中で例外を 用いている限りは、プログラムが停止しないので、例外は回復可能である。この例は、プログラム がファイルからワームスタートをする場合である。ファイルが存在しない(したがって例外を起こ す)ためにこれが失敗すると、標準の初期状態が代わりに用いられる。コードの特定の部分の周辺に try-catchブロックを付加することによって、このような回復可能な例外の処理がなされる。例がリ スト4.44に与えられる。 リスト 4.44回復可能な例外を処理する try { XPRSprob prob = new XPRSprob(); ... } catch(XPRSprobException xpe) { xpe.printStackTrace(); System.out.println("Exit code: "+xpe.getCode()); System.out.println("Message: "+xpe.getMessage()); if(xpe.getXPRSprob()!=null) { ... } } 71 The Xpress-MP Javaのコールバックスの利用 Optimizerからのコールバックスを利用するために、柔軟性のあるイベント構造がJava Optimizer ライブラリー APIによって用いられる。それぞれのコールバックスはリスナーListenerインターフ ェイスのイベントとして定義される。オブジェクトがコールバックスの対象である場合、クラスは 対応するリスナー(たとえば XPRSmessageListener, XPRSlpLogListener, など)を実行しなければ ならない。その後にオブジェクトがXPRSprobオブジェクトとして登録されしなければならない。オ ブジェクトがイベント(コールバックス)を受け取るのに関心を持っていない場合、明示的に登録解 除をしなければならない。リスナーを登録したり解除をしたりするのに用いられる方法を示す。 public void add<listener name>Listener(Listener listener, Object data) throws XPRSprobException; および public void remove<listener name>Listener() throws XPRSprobException; コーディングを便利にするために、いかなるObject表記もリスナーを登録するときには可能となっ ている。同じObject表記が、対応するEvent法に対するパラメタとして戻されるであろう。リスト4.45 にはMessageコールバックスがどのようにして用いられるかを示す最も簡単な例を与える。(リスト 4.43とのコーディングの違いはボールド文字で示されている。) リスト 4.45 Javaでコールバックスを用いる import com.dashoptimization.*; import java.io.*; public class CbTest implements XPRSmessageListener { public static void main(String[] args) { Optimizerが呼ぶ場合があるので、リスナーオブジェクトを壊さないように注意されたい。 Libraries それぞれのリスナーインターフェイスに対してOptimizerは、イベントをせいぜい一つのリスナーオ ブジェクトに送るであろうということに注意されたい。コールバックスの完全なリストと関連する リスナーとイベントは、Optimizer Reference Manualから得られる。詳細については、オンライン 資料かOptimizer Reference Manualを参照せよ。 try { XPRS.init(); } catch (Exception e) { System.out.println("Cannot initialize"); } CbTest ML = new CbTest(); try { XPRSprob prob = new XPRSprob(); prob.addMessageListener(ML,null); prob.readProb("simple",""); 72 prob.maxim(""); prob.writePrtSol(); prob.removeMessageListener(); } catch(XPRSprobException xpe) { xpe.printStackTrace(); } XPRS.free(); } public void XPRSmessageEvent(XPRSprob prob, Object data, String msg, int len, int type) { System.out.println(msg); } } リスト 4.45 Javaでコールバックスを用いる ヘルプの利用 本章においては、これまでMosel ライブラリーを用いてプログラムをどのようにしてコンパイルし、 読み込み、実行するか、さらには多重モデルを同時に処理するか、行列ファイルを出力として作成 するかを学んだ。またBCLを用いてどのようにして問題を構築し、解くか、そしてこのライブラリー から書き出すかをも学んだ。 さらに、どのようにしてこれらのファイルをOptimizerライブラリーを用いてOptimizerに読み込ん で最適化し、解を見て、最適化機能に影響を与える制御変数を変更するかをも学んだ。 最後に、どのようにしてモデルが‘高度な’関数を用いてOptimizerの中で直接に構築されるかを見た。 他の言語についても論じた。5章では、強力なMoselモデルプログラミング言語に焦点を絞って、自 身のモデルを構築する仕方について紹介しよう。 ライブラリーを用いる際の追加的なヘルプ機能は、Xpress-MP CD-Romの中に例の形で見られるし、 またMosel, BCL、 そしてOptimizer Reference Manualsにも示されている。 まとめ この章では、モデルをコンパイルし、読み込み、実行するのにMosel ライブラリーをどのように用 いるか、そしてBCLを用いてどのようにして問題を構築し、解くか、そしてOptimizerライブラリー を用いて線形問題を解き、解にアクセスするか、さらには整数問題を解き、Optimizerライブラリー を用いて問題を入力するか、あるいは他の言語を用いてXpress-MP ライブラリーを利用する方法、 等を学んだ。 73 Xpress-Mosel 5 第 5章 Xpress-MPによるモデルの構築 概要 この章では以下について紹介する。 • • • • Moselモデルプログラミング言語を用いて簡単なモデルを構築する。 添字付きの変数と定数を用いて多種のモデルを開発する。 モデルのデータをモデルの構造から分離することを学ぶ。 データファイルとパラメタの使用について学ぶ。 はじめに 最後の2,3章を通じてXpress-MPのそれぞれのインターフェイスがどのようにして簡単なモデルを 読み込み、解き、そして線形問題に対する最適解を得るかを見てきた。しかしながら、この章では、 Moselモデルプログラミング言語をより詳細に探求し、どのようにして新しいモデルを構築し、得ら れた解を解釈するかについて述べる。同時に、モデルの特別な例を構成するデータとモデルの構造 を分離することによって、柔軟性のあるモデリングが望ましい形となることについても探求しよう。 Xpress-Mosel Xpress-moselモデルプログラミング言語は、同じ問題をいろいろな方法でモデル化するのによく用 いられる。ここに与えられた例は、このようなアプローチの一つである。これらを通して、われわ れは言語について大部分のことを紹介することを目指しているが、もっとも大事なことは、良いモ デリングの実践方法を発展させることであるとわれわれは常に考えている。 最初のモデルの構築 盗人問題 盗人のビルがある晩、自分の欲しいものを盗むための大きな袋を持って、ある家に侵入したとする。 彼は以下のような重さと推定価値をもった多くの品物を見つけている。しかしながら、ビルは合計 で重さ102ポンドまでしか運び出すことができない。これを条件として、彼の目的は彼の運び出す品 物の推定価値を最大化することである。 重さと価値 カメラCamera2 15 ネックレスNecklace 20 100 花びんVase20 90 絵Picture30 60 TV 40 40 ビデオVideo30 15 金庫Chest60 10 積み木Brick10 1 74 Modeling with 問題の特定 盗人問題を数学的に定式化することは比較的簡単である。決定変数をcameraとして、ビルがそれを 運び出すときには値1、そうでないときは値0としよう。また同様に、他の品物に対しても決定変数 を同じ様に定義しよう。このとき、盗人問題は以下のように表されるであろう。 この問題には、読者にとってすでになじみのある部分が多くある。変数camera, necklace、などは 決定変数decision variablesとしてよく知られているが、解を求める操作の中で決定されるもので ある。標準型線形計画問題の中では、決定変数は任意の非負実数値を取りうるし、またそれらはこ れまでの3つの章で解いてきた種類の問題であった。しかしながら、盗人問題の場合には、ビデオを 半分運び出すとか、ただ1個しかないネックレスを5個運び出すということはほとんど意味をなさな い。 これは決定変数が0か1かのどちらかに限定されるという混合型整数計画MIP問題と呼ばれて いる。事実、これらはバイナリーbinary変数である。運び出されるすべての品物の総重量は、次の ように与えられる。 2*camera + 20*necklace + 20*vase + 30*picture + 40*tv + 30*video + 60*chest + 10*brick 上の表現の値は、ビルが運ぶことのできる品物の重さの合計値によって制約される。換言すると、 これは102に等しいか、それ以下にならなければならないということである。LPあるいは MIP問題は このような制約条件をいくつか有しているが、このモデルの目的としては、ただひとつの条件だけ が要求される。 問題 1 Maximize: 15*camera + 100*necklace + 90*vase + 60*picture + 40*tv + 15*video +10*chest + 1*brick Subject to: 2*camera + 20*necklace + 20*vase + 30*picture + 40*tv + 30*video + 60*chest + 10*brick camera, necklace, vase, picture, tv, video, chest, brick 102 . 0 1 ,{}( "The decision variables…" "The constraint" 目的関数objective functionは運び出す品物の価値である。モデリング操作を実施する目的は、い ろいろな制約条件の下で、この関数を最大化することである。 モデルのXpress-Moselへの導入 Moselモデルプログラミング言語を用いて特定されたモデルは、いくつかのブロックblocksに分けら れる。その中で最も外部にあるのはmodelブロックである。ここで問題に対して名前が与えられるが、 演習問題で用いるために、ここでは問題名をburglarとする。問題の中に含まれる指示がすべて終了 したときには、すべてのブロックはend-<blockname>というキーワードを使用して明示的に終了され なければならない。われわれが最初に用いる2番目の主要ブロックは、変数が型とともに宣言される declarationsというブロックであって、その中では決定変数は型mpvarを有する。これをリスト5.1 75 で説明しよう。 リスト 5.1 決定変数を宣言する model burglar declarations camera, necklace, vase, picture: mpvar tv, video, chest, brick: mpvar end-declarations end-model モデル モデルブロックは問題の名前を定義し、モデルの一部と考えられるすべての記述を含む。end-model の後に現れるテキストはコメントとして扱われ、無視される。 宣言 declarationsブロックの中では、変数と型と集合とスカラー量が定義される。 "The objective function…" 問題の名前はOptimizer とMosel で作成されたいくつかのファイルの基本となっている。 決定変数はバイナリー変数と特定されなければならない。これはある意味では問題に対するもう1 つの制約であるので、他の制約があるdeclarationsブロックの外で与えられる。この構文は単に次 のようになる。 variable is_binary 最後に制約条件と目的関数が加えられなければならない。これらは名前を割り当てられ、次のよう になる。 name := linear_expression 線形表現が特に長い場合には、必要に応じていくつかの行に分割してもよい。 Moselには分割された行を表示する文字やコマンドはないが、さらに入力があると期待される場合に は、その行は継続するとみなされる。例として、行の最後に‘+’のようなバイナリーオペレーターが ある場合、次に他の用語が来ることが期待される。これはリスト5.2で示され、ここで完全なモデル が与えられる。 リスト 5.2 モデルに制約条件を加える model burglar declarations camera, necklace, vase, picture: mpvar tv, video, chest, brick: mpvar end-declarations camera is_binary necklace is_binary vase is_binary picture is_binary tv is_binary video is_binary chest is_binary brick is_binary TotalWeight := 2*camera + 20*necklace + 20*vase + 76 30*picture + 40*tv + 30*video + 60*chest + 10*brick <= 102 Similarly, variables can be described as integral using is_integer. "Continuation lines…" The Optimizer Library Module モデルが構築されると、あとは単に問題を解き、ビルが運び出すことのできるものの価値の最大値 を見つけることである。これはMoselの内部からも実行できる。標準的なMoselの構文に加えて、外 部のアプリケーションとのやりとりによって、追加的な機能がライブラリーモジュールの形でその 中に読み込まれる。Optimizer ライブラリーモジュールであるmmxprsを用いると、MoselがOptimizer を呼び出し、問題を解き、解をディスプレイに戻すことが可能となる。これを実行するには、uses というキーワードを用いる。目的関数はmaximizeコマンドを用いて最大化される。モデリングと解 の構成部分を用いると、これは正確にはモデルプログラムmodel programとして説明される。問題に 対する完全なモデルプログラムは、リスト 5.3に追加部分をボールド体で強調して示されている。 TotalValue := 15*camera + 100*necklace + 90*vase + 60*picture + 40*tv + 15*video + 10*chest + 1*brick end-model リスト 5.3 完全な盗人問題モデルプログラム model burglar1 uses "mmxprs" declarations camera, necklace, vase, picture: mpvar tv, video, chest, brick: mpvar end-declarations camera is_binary necklace is_binary vase is_binary picture is_binary tv is_binary video is_binary chest is_binary brick is_binary リスト 5.2 モデルに制約条件を加える ライブラリーモジュールの名前には、‘Mosel Module’を表すために、接頭辞mmを付ける。このモデルはXpress-MP CD上にファ イルburglar1.mosとして入っている。 このモデルプログラムの最後の部分には、得られた解をどのように扱うかが述べられている。 writelnコマンドを用いると、Mosel内でgetobjvalを使用することによって、目的関数値のような解 の情報を出力する指示が可能となる。 演習問題 リスト5.3のモデルプログラムを入力し、コンパイルし、読み込み、選択したインターフ ェイスを使用してMoselの中でそれを実行せよ。ビルが運び出せる盗品の最大値はいくらか。これを 77 達成するためには、彼はどの品物を持っていくべきか。 TotalWeight := 2*camera + 20*necklace + 20*vase + 30*picture + 40*tv + 30*video + 60*chest + 10*brick <= 102 TotalValue := 15*camera + 100*necklace + 90*vase + 60*picture + 40*tv + 15*video + 10*chest + 1*brick maximize(TotalValue) writeln("Objective value is ", getobjval) end-model uses ライブラリーモジュールをMoselに読み込む。 maximize / minimize Moselを呼び出して、引数として宣言された目的関数を最適化するためのコマンド。 writeln Moselに対して、情報を標準出力に送るように指示する。引数をいくつ入れてもよい。それぞれは指 示された順番に返される。 getobjval 解に従って目的関数値を返す リスト 5.3 完全な盗人問題モデルプログラム 行列によるモデルの構築 行列変数と添字の導入 上で述べた形式でモデルを作成するのは、決定変数が少数の場合には便利であるが、問題のサイズ が増加するにつれて、扱うのがすぐにやっかいになる。すでにわれわれの最初のモデルの仕様の中 で、各変数がバイナリーであると明示的に言わねばならないのは面倒であったし、またわれわれが Moselを用いて決定変数の値を出力しなければならないとしたら、やはりそうであろう。このような 小さな問題ですら、配列変数array variablesあるいは添字付き変数subscripted variablesと呼ば れるような変数を利用するのが普通である。 リスト5.4では盗人問題の宣言部分を変更したものを示し、使用されるいろいろな配列を紹介する。 ここで最初に定義されるのは、Itemsという配列要素のための添字集合である。Itemsは8つの整数 値を持つように割り当てられ、効果的に配列の要素に番号を付ける。これに続いて、さらにWEIGHT と VALUE という2つの配列が定義され、それぞれ選ばれる品物の重さと価値を表している。それらは Itemsによって配列として指標化され、それぞれの数値要素は実数型realを有している。Moselの実 数型realは、Cの倍精度浮動小数点に対応する。他のプログラミング言語の場合と同様に、Moselも 整数型integer と文字型stringをサポートしている。declarationsブロックの外にあるこれら2つの 配列は、データをコンマで分けられたリストとして入力する。 リスト 5.4 データを宣言して配列に入力する declarations Items = 1..8 WEIGHT: array(Items) of real VALUE: array(Items) of real x: array(Items) of mpvar end-declarations 78 WEIGHT := [ 2, 20, 20, 30, 40, 30, 60, 10] VALUE := [ 15,100, 90, 60, 40, 15, 10, 1] declarationsブロックで定義される最後の配列は、決定変数として機能するxである。それは配列 であって、同時にItemsによって指標化され、型mpvarを有する要素である。 ループ化と和計算 配列を用いるのは、ただ単に変数宣言をより整然とするためだけではない。決定変数の配列をルー プ化することによって、変数がバイナリーであることをわずか1行で宣言することもできる。Mosel では、forallというコマンドを用いることによって、簡単なルーピングをサポートしている。Mosel はまた、sumコマンドによって総和計算の表記をサポートしているが、これは制約条件の線形表現や 目的関数を構築するのに広範に用いられる。これらを用いて決定変数がバイナリーであると定義し たり、制約条件と目的関数を設定したりする例がリスト5.5に示されている。 forall 添字集合によってルーピングを実行する。 forall(var in set) statement ここで var はダミー変数で、set は添字集合である。 statement はそれぞれの指令によって実行される。 sum 添字集合上で総和計算を行う。構文は以下の通りである。 sum(var in set) expression ここで varはダミー変数で、set は添字集合である。 expression isは総和をとる総称である。 do / end-doブロックの中でforallループを用いると、多重計算が可能となる。forall構造についての詳細を含めて、より詳 細については、165ページを参照されたい。 コメント 配列と添字化操作を用いることによって、盗人問題のより良いモデルはほぼ完成する。しかしなが ら、いかなるプログラミングに関しても、自分自身と他の人が解読するには、モデルについて適切 なコメントを付けておくことは良いことである。end-modelという記述の後にはどれだけのコメント でも付け足すことが出来るということはすでに述べた。しかしながら、モデルそれ自体の中にコメ ントを入れたいときはどうなるであろうか。まさにこの目的のために、Moselモデルプログラミング 言語では2種類のコメントをサポートしている。単一行のコメントは、!という文字によって導入 される。この文字に続くテキストはすべて、行の終わりまでコメントとして無視される。多くの行 にわたるコメントには、!と!をペアにして使わなければならない。盗人問題のためのモデルプロ グラムでコメントが加えられたものはリスト5.6に与えられている。 リスト 5.5 盗人問題におけるルーピングと総和計算 forall(i in Items) x(i) is_binary TotalValue := sum(i in Items) x(i)*VALUE(i) TotalWeight:= sum(i in Items) x(i)*WEIGHT(i)<=102 リスト 5.6 盗人問題に対するもう一つの試み model burglar2 (! Modeling with arrays, subscripts, summations, looping and comments !) uses "mmxprs" declarations 79 Items = 1..8 ! 8 items WEIGHT: array(Items) of real VALUE: array(Items) of real x: array(Items) of mpvar end-declarations ! Item: 1, 2, 3, 4, 5, 6, 7, 8 WEIGHT := [ 2, 20, 20, 30, 40, 30, 60, 10] VALUE := [ 15,100, 90, 60, 40, 15, 10, 1] このモデルはファイルburglar2.mos としてXpress-MP CD に載せられている。 モデルの最後に付け足された行は、Moselに目的関数値と決定変数の解を出力するように指示するも のである。これによって別のforallループが用いられ、getsolによってそれぞれの要素の解を戻す 変数配列が調べられる。 演習問題 モデルプログラムを配列変数を用いてリスト5.6のように変更せよ。それをコンパイルし、 読み込み、実行せよ。ビルはどの品物を持ち出すであろうか。 ストリング添字の使用方法 上に開発したモデルプログラムは、最初に示したものよりはるかに簡単なものである。しかしなが ら、解を解釈することは、それが示す品物の変数の添字にわれわれが人的に合わせなければならな いので、より難しくなっている。 ! All x are binary forall(i in Items) x(i) is_binary ! Objective: maximize the haul TotalValue := sum(i in Items) x(i)*VALUE(i) ! Constraint: weight restriction TotalWeight:= sum(i in Items) x(i)*WEIGHT(i)<=102 maximize(TotalValue) writeln("Objective value is ", getobjval) forall(i in Items) writeln(" x(",i,") = ", getsol(x(i))) writeln end-model getsol 問題の解にしたがって、決定変数の最適値を返す。単一の引数は、値が返されることになっている 変数である。 リスト 5.6 盗人問題に対するもう一つの試み Moselでは数字の添字よりも文字の添字の方がよく用いられている。Moselモデルプログラミングの 中で添字集合を使用するのは、おそらく数値を用いるよりも容易で、しかも自然であろう。Items を文字集合として定義することが必要とされる唯一の変化であって、これはリスト5.7に示される。 リスト 5.7盗人問題で文字添字を用いる model burglar3 uses "mmxprs" 80 declarations Items = {"camera", "necklace", "vase", "picture", "tv", "video", "chest", "brick"} WEIGHT: array(Items) of real VALUE: array(Items) of real x: array(Items) of mpvar end-declarations ! Item: ca, ne, va, pi, tv, vi, ch, br WEIGHT := [ 2, 20, 20, 30, 40, 30, 60, 10] VALUE := [ 15,100, 90, 60, 40, 15, 10, 1] ! All x are binary forall(i in Items) x(i) is_binary ! Objective: maximize the haul TotalValue := sum(i in Items) x(i)*VALUE(i) ! Constraint: weight restriction TotalWeight:= sum(i in Items) x(i)*WEIGHT(i)<=102 maximize(TotalValue) writeln("Objective value is ", getobjval) forall(i in Items) writeln(" x(",i,") = ", getsol(x(i))) writeln end-model このモデルはファイルburglar3.mosとしてXpress-MP CD に載せられている。 演習問題 モデルプログラムを文字添字集合を用いるように変更せよ。それをコンパイルし、読み込 み、実行して、その結果を前と比較せよ。 モデリングにおける汎用性 総称的モデルと例示的モデル 盗人問題は、ナップザック問題のようなよく知られている、たくさんの似たモデリング問題の中の 単なる1例にすぎない。上のモデルは盗人問題をモデル化するための第1歩としては適切なものであ るが、品物の個数が増やされたり、あるいは問題が別のナップザック問題に変えられたりした場合 には、一般化することによってより簡単になるであろう。問題はすでに配列変数によって明確にさ れているので、残っている課題は、データが現在のところ未だモデルに’硬く連結’されているーすな わちモデルとデータ構造の間が分離していないーという事実によるものである。本節では、これら の限界を克服する方法について述べよう。モデルは、一般的には、様々な決定変数を表す記号を用 いて、一連の等式と不等式によって表されるこれらの変数の間の関係―すなわち制約条件が定式化 される。これらの制約条件を体系的に記述すると、一般的genericモデルが得られる。これを与えら れたデータ集合と結合すると、特定の数値numericalモデル、すなわちモデルの例示model instance が得られ、それが最適化される。モデル構造を数値データから分離すると、より柔軟性のあるモデ ルになるため、管理するのが容易になる。こういうわけで、次の2,3ページでは、このようなモデ ルの違いを明らかにすることを目的としている。 実数の宣言 81 リスト5.7のモデルを見ると、制約条件はすでにほぼ適切で柔軟性のある形になっている。しかしな がら、ビルが運び出すことのできる最大の重さについては、特定の情報は未だにモデルの中に組み 込まれている。ここで一つのスカラー量、MAXWTを定義し、102という値を割り当て、モデルを通し てそれを使うことにする。 分離することによって、モデルをBIM ファイルとして頒布し、知的財産権を保護することが可能となる。 このような追加的な量は、モデルファイルの先頭にあるdeclarationsブロックから値を指定される。 値を指定された量はすべて一定であると仮定されている。 テキストファイルからのデータの入力 この段階でモデルプログラムに対してわれわれがなすべきもう一つの変更点は、モデルの配列デー タを分離して、それを外部のデータファイルから入力することである。Moselでは、initializations ブロックを用いてこれを実行する。 Data サブディレクトリーの中にあるファイルburglar4.datが次のような情報をもっていると仮定 する。すなわち、配列のWEIGHT と VALUE をinitializationsブロックに入れた後に、Moselはデー タを読み取り、実行時に配列を満たすことになる。これを実行するモデルプログラムの完全なリス トは、変更部分をボールド体で強調した上でリスト5.9に与えられている。 リスト5.8のデータをData サブディレクトリーの中にあるファイルburglar4.datに入れ、このファ イルからデータを入力するようにモデルを変更せよ。これをコンパイルし、読み込み、実行してス ムーズに動いてくるかをチェックせよ。 初期化 テキストファイルからデータを入力できるようにする。配列と定数は任意の順番で特定され、実行 時に読み取られる。 リスト 5.8 外部データファイルの構造 ! Data file for burglar4.mos WEIGHT: [ 2 20 20 30 40 30 60 10] VALUE: [15 100 90 60 40 15 10 1] 配列の値はもはやコンマで分離されていないことに注意せよ。 盗人問題の解 最後の演習問題を終えたとしたら、明らかに次の質問は、 あとどれくらいデータファイルから入 力できるか? ということであろう。確かに、スカラー量MAXWTは、モデルプログラムの中で特定化 される必要はなく、データファイルに保存される。選択すべき品物の個数と名称は、事実モデルの 構造の一部というよりもモデルのデータであるので、添字集合に関しても同様のことがいえる。 リスト 5.9 ソースファイルからデータを入力する model burglar4 uses "mmxprs" declarations Items = {"camera","necklace","vase","picture", "tv","video","chest","brick"} WEIGHT: array(Items) of real VALUE: array(Items) of real 82 MAXWT = 102 x: array(Items) of mpvar end-declarations ! Read in data from external file initializations from 'Data/burglar4.dat' WEIGHT VALUE end-initializations ! All x are binary forall(i in Items) x(i) is_binary ! Objective: maximize the haul TotalValue :=sum(i in Items) x(i)*VALUE(i) ! Constraint: weight restriction TotalWeight:=sum(i in Items) x(i)*WEIGHT(i)<=MAXWT maximize(TotalValue) writeln("Objective value is ", getobjval) forall(i in Items) writeln(" x(",i,") = ", getsol(x(i))) writeln end-model このモデルはファイルburglar4.mos としてXpress-MP CD上にある。 データファイルが以下の情報を含むように修正されたと仮定しよう。モデルプログラムがこのすべ ての情報を実行時に入力できるとしたら、最終的にはすべてのモデルデータとモデルの構造とを分 離したことになる。リスト5.11は、これを処理すべくモデルプログラムを変更したものである。 リスト 5.10 盗人問題に対するデータファイルの修正 ! Data file for burglar5.mos Items:["camera" "necklace" "vase" "picture" "tv" "video" "chest" "brick"] MAXWT:102 WEIGHT:[ 2 20 20 30 40 30 60 10] VALUE:[15 100 90 60 40 15 10 1] リスト 5.11 盗人問題のモデルプログラムを完成する model burglar5 uses "mmxprs" parameters WeightFile = 'Data/burglar5.dat' end-parameters declarations Items: set of string MAXWT: real WEIGHT: array(Items) of real VALUE: array(Items) of real x: array(Items) of mpvar 83 end-declarations ! Read in data from external file initializations from WeightFile Items MAXWT WEIGHT VALUE end-initializations forall(i in Items) create(x(i)) 集合は文字配列として定義され、ファイルburglar5.mos としてXpress-MP CD上に載せられている。 declarationsブロックにジャンプした後、最初に注意すべきことは、データが引き続き読み込まれ る場合に、Items と MAXWTの型を宣言しなければならないということである。まずItemsは文字列で ある要素からなる(添字)集合であって、またMAXWTは配列のVALUE、 WEIGHTと同じ型を有すると定 義した。 これらの変更を適用した上でモデルが実行されると、実行時にモデルが空であるということで失敗 するであろう。この場合、問題となるのは決定変数xの添字集合の大きさが宣言の中でまだ決定され ていないという事実である。添字集合は後になるまで入力されないので、決定変数の配列は絶えず 変化し、要素がない状態で初期化される。この場合、配列は明示的に作成されなければならず、そ のためにここではcreateコマンドを用いる。事実、これはこの問題を解く唯一の方法ではない。2 番目のdeclarationsブロックは、initializationsブロックに続いてただちに読み込まれ、変数xが ここで宣言される。モデルの関連部分の例はリスト5.12に与えられる。 ! All x are binary forall(i in Items) x(i) is_binary ! Objective: maximize the haul TotalValue :=sum(i in Items) x(i)*VALUE(i) ! Constraint: weight restriction TotalWeight:=sum(i in Items) x(i)*WEIGHT(i)<=MAXWT maximize(TotalValue) writeln("Objective value is ", getobjval) forall(i in Items) writeln(" x(",i,") = ", getsol(x(i))) writeln end-model create 前に宣言された動的配列の一部である変数を明示的に作成する。 リスト 5.11 盗人問題のモデルプログラムを完成する "Understanding Listing 5.11…" initializationsブロックに引き続いてItemsの大きさと要素がただちに判明するので、それに関す る線形の性質がモデルファイルからMoselの記述として読み込まれ、解釈されると、Itemsが2番目の 宣言に関する記述の集合の中で正しく定義されることになる。現在のモデルでは、これら2つの方法 のどちらかは完全にうまくいくであろうが、明確さを重視するために、ここでは前者を用いて両ブ ロックを結合する。 演習問題 モデルプログラムをリスト5.11のように書き換えよ。前と同様にそれをコンパイルし、 読み込み、実行して問題を解け。 84 リスト 5.12 2種類の宣言を利用する declarations Items: set of string MAXWT: real WEIGHT: array(Items) of real VALUE: array(Items) of real end-declarations ! Read in data from external file initializations from WeightFile Items MAXWT WEIGHT VALUE end-initializations declarations x: array(Items) of mpvar end-declarations Moselによるパラメタの利用 まだ議論されていないが、リスト5.11にある最後の変更点はモデルプログラムの先頭にあって、 parametersブロックを利用するものである。これを用いると、パラメタが定義され、それらの値は モデルプログラミングを通して入力されたことになる。それらの値は数値または文字列のいずれか の型を有するであろう。仮に異なる最大重量を選択した上で問題を実行したかった場合には、MAXWT はスカラー量ではなく、パラメタとしてここに定義できたであろう。この例では、どのようにして データファイルの名前が最初に定義され、後にinitializationsブロックの中でモデルの例を作成す るためにどのようにしてデータ入力されるかを示そう。 ここで2番目の例を示す。 ハイカー問題 ヘンリーは休日にハイキングに行くことに決め、必要な品物すべてをリュックサックに詰める。彼 は5つの品物の中から何を持っていくかを選択する。それぞれの品物はある一定の量の節約額を有す る。たとえば、テントと寝袋を持っていくことによって宿泊代が節約できる。品物とそれに伴う節 約額は以下のとおりである。 parameters このブロックには、実行時に選択すべき値が前もって定められない場合のデフォルト値とともに、 パラメタの記号のリストが含まれる。 重量 節約額 テントTent 60 100 寝袋Sbag 28 50 カメラCamera 20 35 時計Clock 8 10 食料Food 40 50 しかしながら、彼は品物を最大100ポンドの重量までしか運ぶことが出来ない。ヘンリーはどの品物 を持っていけば節約額が最大になるだろうか? ハイカー問題の解法 この問題はナップザック問題のもう一つの例であって、モデルの構造はこの章で作成したものと同 一である。したがって、問題を解くために唯一必要なのは、異なるモデルデータセットで同じモデ 85 ルプログラムを再び実行すればよい。これはリスト5.13に示されていて、そこでは節約額は配列 VALUEに読み込まれるであろう。 演習問題 リスト5.13のデータをDataサブディレクトリーの中のファイルhiker.datに入力せよ。コンパイルさ れたファイルburglar5.bimはMoselの中に読み込まれ、実行される。ここでパラメタ WeightFile=’Data/hiker.dat’をrunへの引数として用いる。このとき、ヘンリーはどの品物を持っ ていくべきか、そしてそのときの最大の節約額はどうなるか? Mosel を Consoleアプリケーションとして用いる例の完全な記述はリスト5.14に示される。 リスト 5.13 ハイカー問題のためのデータファイル ! hiker.dat - Data file for Hiker Problem Items:["Tent" "Sbag" "Camera" "Clock" "Food"] MAXWT: 100 WEIGHT: [ 60 28 20 8 40] VALUE: [100 50 35 10 50] Xpress-MP Essentials Getting Help 137 ヘルプの利用 この章を通して、Moselプログラミング言語の基礎的な要素をいくつか紹介するため、比較的簡単な ナップザック問題について述べた。このような手続きを継続的に繰り返すことによって、われわれ はモデリングの汎用性の観点から、モデル構造とデータを分離することが便益となることを見た。 しかしながらこのような説明は、単に表面をなでているに過ぎず、Mosel言語には未だ紹介されてい ない多くの特徴がある。次の章では、これらについて2, 3簡単に述べ、他にどのようなことが可能 であるかを伝えるることにする。より完全な詳細に関しては、Mosel Reference Manualを参照され たい。これは、ソフトウェアに付随する標準的なライブラリーモジュールのいくつかの機能につい て記述しているだけでなく、この言語のあらゆる可能性に関しても詳細に記述している。この章に 書かれている様々なモデルについては、Mosel言語を用いた他のたくさんの例とともにすべてインス トールしたCD-ROM上で見ることが出来る。 リスト 5.14 ハイカー問題を解く C:\Mosel Files>mosel ** Xpress-Mosel ** (c) Copyright Dash Associates 1998-zzzz >load burglar5 >run WeightFile='Data/hiker.dat' Objective value is 160 x(Tent) = 1 x(Sbag) = 1 x(Camera) = 0 x(Clock) = 1 x(Food) = 0 Returned value: 0 > 86 まとめ • • • • • • この章では以下のようなことを学んだ。 変数、配列、定数、添字集合の宣言の仕方 制約条件と目的関数の入力の仕方 Optimizer ライブラリーモジュールを用いて問題をき、writelnを用いて解の情報を返す 仕方 添字付き変数、総和計算、単純なルーピング構造の利用の仕方 外部ファイルからデータを入力し、モデルデータと構造を分離する仕方 • Furt 87 6章 Mosel に関する話題 概要 この章では、以下について述べる。 ・ 集合と配列の使用について学ぶ ・ データファイルとスプレッドシートを用いてデータを読み込み、転送する方法を学ぶ ・ 条件付変数と制約条件について学ぶ ・ Mosel 言語の基本的なプログラミング構造について述べる ・ 自身のモデルにサブルーチンを書くことについて学ぶ はじめに これまで2−4章では、インターフェイスを学ぶための基本として用いられてきた単純な例を示し、前章では、モデルがMosel プログラミング言語を用いていくつかの異なる方法でどのようにして表現されるかを見てきた。そこで扱われた概念は多様 性に富むものではあったが、Mosel 言語に関係するいくつかの話題については、簡潔に議論する価値がある。ここで述べら れる話題はそれぞれが完結しているので、モデリングに特別の関心がある場合には、独立して読むことも可能である。 ここで述べる内容は、以下のとおりである。 ・ 一定集合、動的集合、及び集合演算 ・ 多次元で動的な疎配列 ・ ファイルからのデータの読み込みと、ODBCで得られた成果 ・ 変数と制約の条件付作成法 ・ 選択とループ構成 ・ 自身の手順と関数の書き方 さらに詳細については、Mosel Reference Manualを参照されたい。 集合について 集合は同じ種類の対象物を集めたもので、そこでは順序付けは問題とはならない。Mosel の集合はす べて初歩的な型で定義される:すなわち基本型(integer, real, string, boolean) とXpress-MP 型 (mpvar, linctr)の2種類である。それらは3つの異なった方法で初期化できる。それらの方法につ いて簡単に述べよう。 一定集合:要素がモデルの中のdeclarations によって宣言された集合が一定集合である。─それら の内容は後で変えることはできない。例を示そう: declarations SnowGear = {’hat’,’coat’,scarf’,’gloves’,’boots’} end-declarations ファイルの初期化:集合の要素はデータファイルに保存され、initializationsブロックを用いてモ デルの中に読み込まれる: declarations FIB: set of integer end-declarations initializations from ’datafile.dat’ FIB end-initializations データの読み込みについては、148ページの「データの読み込みと転送」で詳しく述べる。 88 ここでファイルdatafile.datは、以下のようなデータを含む。 FIB: [1 1 2 3 5 8 13 21] 暗示的なファイルの初期化:配列の初期化と同時に、配列の添字を表すのに用いられる集合も間接 的に初期化される。モデルとしての例を示す。 declarations REGION: set of string DEMAND: array(REGION) of real end-declarations initializations from ’transport.dat’ DEMAND end-initializations where the file transport.dat might contain data such as: DEMAND: [(North) 240 (South) 159 (East) 52 (West) 67] the set REGION will be initialized as: {’North’,’South’,’East’,’West’} 動態的、固定的、最終集合 Moselでは、一定でない集合は動的であると見なされる。つまり、要素は任意の時点で集合に加えら れたり、集合から削除されたりすることが可能である。一度、集合が配列の添字を示すために用い られると、それは固定的となり、それ以上要素を追加することは可能であるが、削除することは不 可能となる。しかしながら、多くの場合、一度初期化された集合の中身は変化しないし、またその ような状況では、一定集合より動的集合の方がよいとする理由はほとんどない。むしろ以下のよう な理由で、逆の方が真実となるであろう:すなわち、添字が動的集合によって指定された配列は、 Moselにおいてはそれ自身動的に作成される。Moselでは動的配列よりも静的配列(一定集合によっ て示されるもの)の方が可能な限りより効率的に扱われるので、できるだけモデルには静的配列を 用いる方が望ましい。このようなわけで、Moselには動的集合を一定集合に変換させる機能として finalize がある。 前の例に引き続いて、次のように使用されることもある。 finalize(FIB) declarations x: array(FIB) of mpvar end-declarations 集合演算 このマニュアルでは、これまでのすべての例において、集合は単に他のモデリングの対象となるも のを指定するためだけに用いられてきた。しかしながら、実際はこのためだけではなく、他にも利 用可能性が存在するので、それらのいくつかについて詳細を述べよう。これらについてはリスト6.1 に示されている。ここでは3つの集合演算の使用方法について説明する。 • 集合和 (‘+’); • 集合積 (‘*’); • 集合差 (‘-’). リスト 6.1 集合演算を利用する model Sets declarations Cits = {"Rome","Bristol","London","Paris", "Liverpool"} Ports = {"Plymouth","Bristol","Glasgow", "London","Calais","Liverpool"} Caps = {"Rome","London","Paris","Madrid"} end-declarations writeln("Union of all places: ", Cits+Ports+Caps) 89 writeln("Intersection of all three: ", Cits*Ports*Caps) writeln("Cities that are not capitals: ",Cits-Caps) end-model 最初と最後では、数字の場合と同じように集合に対して実行される演算として‘+=’ と ‘-=’を定義し、 他の集合の要素に従って集合を修正している。 演習問題 リスト6.1の例を入力し、それらの和、積、差を求めよ。演算‘+=’ と ‘-=’を用いるように 例を変更せよ。 Mosel supports a number of other operators for use with sets. The in operator has already been seen in several examples of the forall structure, allowing us to loop over all elements of a set. Comparison operators may also be used and include: subset (‘<=’), superset (‘>=’), difference (‘<>’) and equality (‘=’), returning boolean expressions. Their use is illustrated in Listing 6.2. Moselでは、他にもいくつかの集合演算をサポートしている。in演算についてはforall構造のいくつ かの例ですでに見たが、この演算によって集合のすべての要素に対してループ操作をすることが可 能となった。比較演算も使用されるが、たとえば部分集合subset (‘<=’), 包含集合superset (‘>=’), 差集合difference (‘<>’)、そして等号 (‘=’)などによってブールboolean 表現が許されている。この 利用については、リスト6.2.で説明する。 演習問題 リスト6.2のモデルを入力し、出力の中に他の比較演算を含むように変更せよ。出力はど うなるか。 リスト6.2 集合比較演算を利用する model "Set Comparisons" declarations RAINBOW = {"red","yellow","orange","green", "blue","indigo","violet"} BRIGHT = {"orange","yellow"} DARK = {"blue","brown","black"} end-declarations writeln("BRIGHT is included in RAINBOW: ", BRIGHT<=RAINBOW) writeln("BRIGHT is not the same as DARK: ", BRIGHT<>DARK) writeln("RAINBOW contains DARK: ", RAINBOW>=DARK) end-model 行列の利用について 集合とは対照的であるが、配列は同じ種類のものの順序付き集合であって、基本型 (integer, real, string, boolean) とXpress-MP型 (mpvar, linctr)という2つの基本的分類がなされている。配列 は集合によって指標化され、その例については前項で見たが、より単純には、集合を定義すること なく、自然数のみを用いることもある。 データを初期化して一次元配列に入力するには、次のようにすればよい。 declarations OneDim: array(1..10) of real end-declarations OneDim:= [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 一次元的配列をセットアップする多くの例については、第5章ですでに見た。多次元的配列を初期 化する場合もほぼ同様にできるが、この場合には、結果をよりわかりやすくするためにフォーマッ ト操作が有効に用いられる。2次元の場合は、以下のようにして行う。 declarations 90 TwoDim: array(1..2,1..3) of real end-declarations TwoDim:= [11, 12, 13, 21, 22, 23] これはもちろん構文的には以下の表現と同じである。 TwoDim:= [11, 12, 13, 21, 22, 23] これらの指示は、Moselでは、数値を配列TwoDim に行形式に並べるものと解釈される — 最初に変 化するのは最後の添字である。このようにして、配列の中の数値は次のようになる。 Further Mosel TwoDim[1][1] = 11 TwoDim[1][2] = 12 TwoDim[1][3] = 13 TwoDim[2][1] = 21 TwoDim[2][2] = 22 TwoDim[2][3] = 23 In higher dimensions, the same principle may be applied. Listing 6.3 describes a small model which enters data into a three-dimensional array and then prints out the array elements along with their values. より高次元の場合でも同様の原則が適用可能である。リスト6.3では、データを3次元配列に入力し、 それらの数値と共に配列要素をプリントする小さなモデルについて説明する。 演習問題 リスト6.3のモデルを入力し、コンパイルし、読み込み、実行するせよ。配列要素の添字 が要素の数値に対応することを確認せよ。 リスト6.3 3次元配列を初期化する model ThreeDimEx declarations ThreeDim: array(1..2,1..3,1..4) of integer end-declarations ThreeDim:= [111, 112, 113, 114, 121, 122, 123, 124, 131, 132, 133, 134, 211, 212, 213, 214, 221, 222, 223, 224, 231, 232, 233, 234] forall(i in 1..2, j in 1..3, k in 1..4) do writeln("ThreeDim(",i,",",j,",",k,") = ", ThreeDim(i,j,k) ) end-do end-model See page 165 for details of the ‘forall’ structure. 動的、固定的行列 Mosel言語における配列は固定されたサイズを持つか,あるいはモデルプログラムが実行されるのに したがって動的に変動する。標準的には、すべての添字集合が固定サイズのものならば、つまりそ れらが一定であるか、またはそのサイズを最終決定しているならば、配列は固定されたサイズで作 成される。固定的配列は、配列のタイプによって数値0 かまたは’’ (空の記号列)を与えられた未 初期化セルと宣言時に作成されたすべてのセルに対するスペースを有している。 これとは対照的に、配列のサイズが宣言時に知られていない場合は、それは動的に作成される。外 部ファイルから添字集合が読み込まれることになっているために、添字集合の一つが固定サイズを 持たない場合にも、このことは起こりうる。動的配列は最初は空の状態で作成されるが、数値を割 り当てられるに従ってセルを加え、配列は必要に応じて大きくなる。 配列は、dynamic指定を受け 91 ることによって、動的配列となりうる。 型mpvarを有する配列は、動的と宣言されるか、あるいはまた、添字集合の少なくとも一つのサイズ が宣言時に知られていないために暗にそうなる場合には、対応する変数は作成されない。このよう な場合には、個々の要素はすべてcreateコマンドを用いて作成されなければならない。これがなさ れなければ、配列のサイズは零で、決定変数は存在せず、問題は空集合となる。この例は前章のリ スト5.11で見られるが、159ページの”条件付き変数と制約”の項でも再度提示する。 疎行列 ほとんどすべての大規模LPあるいはMIP問題は、疎性として知られている特性を有している。すなわ ち、それぞれの変数は制約条件の全体集合の中の非常に小さな部分のみで非零係数を有している。 この特性は、モデルの中で使用されるデータ配列においては、多量の零係数として考慮される。 この場合には、全ての数値のリストを作成するよりもむしろ、非零数値だけを与える方がより便利 である。これはまた、データを2次元以上のデータ配列に入力する場合でも最も簡単な方法である。 さらなる利点は、使用されるメモリーがより少なくてすむということである。リスト6.4の情報を含 むデータファイルSparseEx.datを持っていると仮定しよう。 集合の型についての詳細は、前の “集合についての作業”を参照されたい。 この例では、配列COSTの要素は、Depot1 から Customer1へ、Depot2 からCustomer1へ、等に対して 製品を送るのに割り当てられた非零数値を持つ。しかしながら、Depot2 からCustomer3へ製品を送 るのに必要なコストは定義されていないので、この配列は疎と見なされる。リスト6.5では、モデル はこのファイルに含まれるデータを読み込み、すべての可能なDEPOT-CUSTOMERの組み合せについて その関連コストを伴ったリストをプリントする。割り当てられたコストのない要素には数値0が与え られる。 この例では、配列COSTのサイズは、宣言時にいずれの添字集合のサイズも知られていないため、動 的に扱われる。データを保存するために使われるスペースは追加された要素の個数に対応する — それ以外の零要素の為のスペースは作られない。コンソールでコマンドdisplay COSTを用いると、 これが見られる。 リスト6.4 疎データフォーマット配列の例 ! SparseEx.dat - Data for SparseEx.mos COST: [(Depot1 Customer1) 11 (Depot2 Customer1) 21 (Depot3 Customer2) 32 (Depot2 Customer2) 22 (Depot1 Customer3) 13] リスト6.5 疎データフォーマットにデータを入力する model SparseEx declarations DEPOT, CUSTOMER: set of string COST: array(DEPOT,CUSTOMER) of integer end-declarations initializations from 'SparseEx.dat' COST end-initializations forall(d in DEPOT, c in CUSTOMER) writeln(d," -> ",c,": ",COST(d,c)) end-model このファイルフォーマットでは、括弧に入れられた語は数値が記憶される配列の中の場所を表す。 Topics 演習問題 上記のモデルを入力し、Moselの中でそれを実行せよ。配列COSTの数値を表示することに よって、この配列が動的であることを確認せよ。 92 データの入出力 モデル実例を構成する特定のデータとモデルの一般的形式あるいは構造を分離することによって明 白な効用が得られる。Moselモデルプログラミング言語によってこのようなモデリングの原理の理解 が進み、データの読み込みと転送が非常に容易になる。 4つの主要な方法によってMoselモデルプログラム配列にデータを入力できる。 ・ モデルプログラムファイルに直接 ・ initializationsブロックを用いて ・ ODBCを用いて ・ readlnコマンドを用いて 最後の3つの方法によって、データにしたがって得られた解を転送する方法が与えられる。この項 では、これらについての関連する効用について述べる。 モデルデータの要素 データをMosel配列に入力する最も簡単な方法は、モデル自身の中にデータを直接埋め込むことであ ろう。前章でこのような例をいくつか見たが、どのようにしてデータが入力されるかについてのよ り詳しい例をリスト6.6に示す。 この例では、サイズ5の1次元配列が作成され、データが入力される: A(1)は数値1,A(2)は数値 2.5、等のように割り当てられる。最終的には、writelnを用いて配列がコンソールに表示される。 Further Mosel 利用するには速くて容易であるものの、この方法に依存すると、いかなる方法を用いてもデータと モデル構造を切り離すことは不可能となる。したがって、このような方法の利用は、小さなテスト 例題の作成あるいはプロトタイプモデルの開発に限られるであろう。 初期化ブロックを用いたデータの転送 initializationsブロックは、スカラー、配列、外部データファイルからの集合などの基本的なもの を初期化し、モデルプログラムの中の解データを後で転送するのに使用することができる。 ASCIIファイルからのデータの読み込み:初期化データファイルはそのフォームの複数個のレコード を含まねばならない。 label: value ここでlabelはテキスト記号列であって、またvalueは定数、あるいはスペースで切り離され、括弧 で囲まれた数値の集まりである。数値の集まりは、集合あるいは配列を初期化するのに用いられる。 データ入力の間、初期化しなければならないものは外部ファイルのlabelに関連付けられる。これは 一般的には対象となるものの名称と同じラベルであるが、修正子asが用いられる場合は異なること もある。initializationsブロックが実行されると、与えられたファイルが開かれ、対応する対象を 初期化するために、要求されたラベルがこのファイルの中で探索される。 このファイルでは特別なフォーマット作業は必要ない:スペース、タブ、行の区切りはすべて通常 の分離記号となる。さらに、1行以内のコメントがファイル内でサポートされる。そのようなファ イルの例はリスト6.7に与えられる。 リスト6.6 配列への原データの入力 model NativeEx declarations A: array(1..5) of real end-declarations A:=[1, 2.5, -6.1, 10, 77] writeln("A is: ",A) end-model このファイルでは、1つの整数(Item)が2つの配列(A1とalbatross)と一緒に定義される。リスト6.8 はこれらの数値をメモリーに読み込むモデルファイルの例である。 この例にはいくつかの特筆すべき事柄がある。まず、データファイルの名前はinitializationsブロ ックの始めと同じ行になければならず、語fromの後に引用されなければならない。initializations ブロックはデータの転送にも使用されるので、修正子fromはデータ移動の意味を特定するものであ る。この場合は、情報を外部の情報源から入手している。 93 リスト6.7 初期化で使用するASCIIファイルフォーマット ! InitEx.dat: Using the initialization block Item: 25 albatross: [12.9 76 1.55 0.99] A1: [23 15 43 29 90 180] リスト6.8 ASCIIファイルからの入力を提示するモデルファイル model InitEx declarations Item: integer A1: array(1..6) of integer A2: array(1..4) of real end-declarations initializations from ’InitEx.dat’ Item A1 A2 as "albatross" end-initializations writeln("Item is: ",Item) writeln("A1 is: ",A1) writeln("A2 is: ",A2) end-model 第2に、initializationsブロックに含まれる項目はスペースで区切られているので、これらのうち のいくつかは同じ行に置かれるかもしれない。この例ではItemとA1を用いてこれを実行した。ここ で対象とするものは付随するデータファイルの中のデータと必ずしも同じ順序である必要はないこ とに注意されたいが、明確さを重視する場合に順序を保つことは往々にして賢明なことではある。 最後に、データファイルの中のラベルalbatrossと関連する配列データは、配列A2に読み込まれるこ とになっている。これはモデルファイルの中で修正子asを使用することによって達成される。この 構成が使用される場合にはデータファイルの中のラベルがモデルファイルに引用されなければなら ないので、モデルファイル内のラベルは、必要に応じて複数個の語から成る場合も生じる。 演習問題 上記の例を入力するか、またはデータを読み込んで表示する自身のプログラムを作成せ よ。自身の例は上述のすべての可能性を提示するものでなければならない。 データをASCIIファイルに転送する:initializationsブロックに対するフォーマットを利用して、 Moselからのデータを問題の解にしたがって外部のデータファイルに転送する場合、同等の方法が使 用されることもある。 initializations to filename identifier [as label] end-initializations この形式が実行される場合、ファイル内のすべての与えられたラベルの数値は、対応する識別子の 現在の数値に更新される。ラベルが見つからない場合には、ファイルの最後に新しいレコードが付 加され、またラベルが存在しない場合には、ファイルが作成される。 演習問題 上記の演習問題の中の自身のモデルを変更し、フォーマットを用いて別のファイルにデ ータを転送せよ。テキストエディターを用いて新しいファイルを作成し、どのようにしてデータが 入力されたかを調べよ。 Topics ODBCを用いたデータの転送 テキストファイルにデータを作り、保持することは極めて単純な操作であるが、多くの人々にとっ てより効率的で役に立つ様式としてスプレッドシートとデータベースを用いる方法がある。Mosel 言語には、構造的質問言語であるSQL(structured query language)を用いて、ODBCに基づくスプレ ッドシートとデータベースプログラムからデータを入力したり、転送したりする機能が存在する。 それを実行するには、Xpress-MPライセンスの特別許可に加えて、ライブラリーモジュールmmodbc を使用することが必要である。 ODBCを立ち上げる: Myss.xlsと呼ばれるスプレッドシートの中で下記のデータがセルに入力されて 94 いて、範囲B2:C5がMyRangeと呼ばれているとしよう。データがスプレッドシートからXpress-MPに入 力される時、指定範囲の第1行は常に列の見出し名を含むと仮定されているので、無視される。こ の例では、MyRangeからのデータはサイズ3*2の配列を満たすであろうし、またODBCはこのデータを 配列A(3,2)の中に抽出するのに用いられるであろう。 演習問題 “ODBCを利用する”のボックスの中で述べられたように、Excelのユーザーデータソースを 立ち上げよ。 A B C 1 2 First Second 3 6.2 1.3 4 -1.0 16.2 5 2.0 -17.9 ODBCを使用する ウィンドウズのコントロールパネルで、32ビット ODBCを選び、MyExcelと呼ばれるユーザーデータ ソースを立ち上げよ。その際、Addをクリックし、Microsoft Excel Driver (*.xls)を選び、ODBC Microsoft Excelセットアップダイアログを書き込め。続いてOptions >>をクリックし、Read Only のチェックボックスを消せ。 ここの例を用いて作業をする場合には、Microsoft Excelにアクセスする必要がある。 スプレッドシートの範囲では、最上行に列の表題をつけなければならない。 Further Mosel ODBCに基づく製品からのデータを入力する:リスト6.9では、このようなExcelスプレッドシートか らMoselへどのようにしてデータを読み込むかを示す。ここで注意すべきことは、要求されたデータ をスプレッドシートから得るためにはSQL文法が使用されねばならないことである。極めて単純なこ とであるが、‘SELECT * FROM MyRange’によって、MyRangeの中のすべての情報が戻され、その後要求 に応じて配列Aの中に置かれる。 演習問題 よ。) このようにしてExcelからデータを得るモデルを作成せよ(または前のモデルを変更せ ODBCに基づく製品へデータを転送する: スプレッドシートにデータを転送して戻すことはほとんど 同じやり方でできる。この例はリスト6.10にあり、そこでは新しいシートNewが作られ、そこに配列 Aからデータが入力される。 MoselとODBCに基づくアプリケーションの間のいかなるコミュニケーションにもSQLが使われるので、 これよりもはるかに複雑な操作が可能となる。 リスト6.9 Excelスプレッドシートからのデータを読み込む model ODBCImpEx uses "mmodbc" declarations A: array(1..3,1..2) of real CSTR: string end-declarations CSTR:= ’DSN=MyExcel; DBQ=Myss.xls’ SQLconnect(CSTR) SQLexecute("SELECT * FROM MyRange",[A]) SQLdisconnect forall(i in 1..3)do writeln("Row(",i,"): ",A(i,1)," ",A(i,2)) end-do end-model その可能性の詳細については、SQLに関するどの標準的なテキストを調べてほしい。 95 演習問題 自身の前のモデルを変更して、問題の解を含むデータを出力してエクセルに戻すように せよ。あるいは前述の例の1つを変更して、同様のことをせよ。問題がある場合には、次の項を参 照せよ。 Microsoft Excelの表を開いて使用する: MoselからMicrosoft Excelにデータを書き込む時、考慮す べき点がいくつかある。Excelはスプレッドシートのアプリケーションであって、ODBCは本来データ ベース用に作られたものであるので、ODBCを用いてエクセルを読み書きするには特別なルールに従 わなければならない。 ・ データの表を参照するには、Excelワークシートの中では名前を有する範囲が使用されなければ ならない。 ・ 列の名前はフィールドの名前として使用されねばならない。 ・ 各フィールドのデータタイプは列の見出しの下に見本データの行を用いて定義されなければな らない。 ・ リスト6.10 Excelスプレッドシートへデータを書き込む model ODBCExpEx uses "mmodbc" declarations A: array(1..3,1..2) of real end-declarations forall(i in 1..3,j in 1..2) A(i,j) := i*j SQLconnect(’DSN=MyExcel; DBQ=Myss.xls’) SQLexecute("CREATE TABLE New(Col1 integer, Col2 integer)") SQLexecute("INSERT INTO New(Col1,Col2) VALUES (?,?)",A) SQLdisconnect end-model Further Mosel ExcelでOBDCを使用する場合には、各々の範囲の最上行は列の見出しをを含むことが重要である。— さもないとエラーが生じ、データのワークシートとのやりとりが正確に行われないであろう。見本 データの行はまた、各々の列のデータの型を確認するために見出しの下の行に必要とされる。この 見本の行もまた範囲に含まれなければならない。 利用者は、Excelで名前の付いた範囲として明示されたデータベースの表に書き込む場合には、新し いデータが加えられるとすると、範囲のサイズが大きくなることを認識すべきである。いま範囲の 中にODBCを用いて、すでに書き込まれたデータの最上段にさらにデータを書き込みたいとする。こ のとき、Excel内では、前のデータを選んでDeleteキーを打つだけでは、それらのデータを削除する のには十分ではない。これをすると、削除されたデータがそれまであったところの空白の長方形の 後にさらなるデータが加えられるであろう。代わりに、Excel内でEdit, Delete, Shift cells up を用いて前のデータの痕跡をすべて消去し、拡大された範囲を用いることが重要である。 MicrosoftのExcelの表は、一度に一人の利用者しか作成したり、開いたりすることができない。し かしながら、Excelのドライバーオプションで利用される‘Read Only’オプションを用いると、多数の 利用者が同じ.xlsファイルから読み取ることが可能となる。 スプレッドシートデータに対する配列のサイズ調整: Moselは扱う対象を処理する順番に評価する ので、表の大きさは動的に調整される。実際問題として、モデルを書き込んだ後に追加データが入 力されると、スプレッドシートの領域の大きさは実際に変化するという可能性がある。これは特に 便利なやり方なので、その使用方法について簡潔に述べておこう。 スプレッドシートMyss.xlsでの作業を続けていて、データがさらに加えられたときにMyRangeの大き さを変えたいとしよう。これはスプレッドシートに追加の領域を作ることによって対処することが できるが、それをSizesと称する。この中に、問題を特徴づける数を次のようにして挿入する。 Number of Rows Number of Columns =ROWS(MyRange)-1 =COLUMNS(MyRange) 行の数を1だけ減らすと、列名を含む行を減らすことになる。 Topics 96 最初の列の2つのセルによって形成される範囲をNrowsと命名し、2番目の列の2つのセルによって 形成される範囲をNcolsと命名すると、リスト6.11のコマンドがモデルの導入部を形成することにな る。 演習問題 前のモデルを変更して、表のサイズを動的に変えられるようにせよ。次に読み込むべき データの行の数を変更し、Moselの中で新モデルを実行せよ。余分なデータが入力されることをチェ ックせよ。 Readlnとwritelnによるデータの転送 ASCIIファイルからデータを読むためのより一般的な方法は、readln と readコマンドによって与え られる。これらのコマンドは、現在の入力列に対して与えられた記号列を割り当てるか、または与 えられた表現を読み込まれるものと適合させようとするものである。 リスト6.11 スプレッドシートによって表の大きさを動的に調整する model SizingEx uses "mmodbc" declarations NRows, NCols: integer end-declarations SQLconnect(’DSN=MyExcel;DBQ=Myss.xls’) NRows:=SQLreadinteger("select NRows from Sizes") NCols:=SQLreadinteger("select NCols from Sizes") declarations A: array(1..NRows,1..NCols) of real end-declarations SQLexecute("select * from MyRange",[A]) SQLdisconnect forall(i in 1..NRows,j in 1..NCols) do writeln("A(",i,",",j,") = ", A(i,j)) end-do end-model Further Mosel readコマンドは複数行にわたって実行されるが、readlnコマンドの場合は、すべての記号列が単一 行に含まれなければならない。 これらのコマンドの使用については、例を用いた説明が一番わかりやすいであろう。リスト6.12で 述べられるような情報を含む入力データファイルがあると仮定しよう。 ファイルReadlnEx.datは、リスト6.13で述べられるモデルプログラムによって読み込まれる。その モデルは、プログラムに必要な変数を宣言するという、普通の方法で始まる。ファイルの中のデー タは配列Aの中に読み込まれることになっており、その配列Aは疎であって、動的に大きさが調整さ れることになっている。 リスト6.12 readlnによるファイル形式 ! File ReadlnEx.dat read_format( 1 and 1)= 12.5 read_format( 2 and 3)= 5.6 read_format(10 and 9)= -7.1 リスト6.13 readlnによるデータの読み込み model ReadlnEx declarations A: array(range,range) of real i, j: integer end-declarations fopen("ReadlnEx.dat",F_INPUT) readln("!") 97 repeat readln("read_format(",i,"and",j,")=",A(i,j)) until getparam("nbread") < 6 fclose(F_INPUT) writeln("A is: ",A) end-model ここで動的配列Aは、動的集合rangeによって指標化されることに注意されたい。 ‘repeat’構造の詳細については、巻末のページを参照せよ。 Topics fopenコマンドを用いると、データファイルが開けられ、現在の入力列F_INPUTに割り当てられる。 その後、ファイルの1行目はコメントを含んでいるので、!文字を含む行を読む。これに続いて、反 復ループを用いて、リスト6.12で述べられるフォーマットの中で可能な限り多くの行を読み込み、= 記号の後の数値を‘and’という言葉を添付された、数値の添字を有する配列要素に割り当てる。最後 に、入力列は閉じられ、その配列はコンソールにプリントされる。 プログラムの実行中、readとreadlnコマンドによって制御パラメタnbreadを1行で実際に認識可能 な項目の個数に設定する。これを調べることによって、与えられた記号列に適合しない情報がいつ 読み込まれたかが明らかになる。われわれの例では、1行目の後、1行につき6項目がパーサー parserによって認識されることになっている。読み込むべきデータが存在しない時、反復ループを 終了するのにこれを用いることができる。 演習問題 readlnとreadを用いるモデルを作成し、外部ソースファイルからデータを入力し、既に 読み取ったデータを表示せよ。 write とwritelnによるデータの転送 配列と解のデータは、writelnとwriteコマンドを用いるのとほとんど同じ方法でファイルに書き込 まれる。ファイルを開いて、それを現在の出力列F_OUTPUTに割り当てると、その後のコマンドとし ては、writeとwritelnのいずれも出力はコンソールでなくてファイルとなる。この例はリスト6.14 に与えられているが、そこでは2章から4章の簡単な問題が解かれ、解の値がデータファイルに転 送される。 このやり方で情報が転送される場合、現在ファイルに書き込み中のすべての情報が失われ、ファイ ルは新しいデータで完全に上書きされることに注意しなければならない。これを望まない場合は、 代わりに、データを追加するためにはfopen(Filename,F_APPEND)を用いてファイルをく必要がある。 演習問題 リスト6.14のモデルを入力して実行し、ファイルWritelnEx.datにデータを書き込め。次 にデータを追加するようにモデルを変更し、プログラムを再度実行せよ。出力ファイルをテキスト エディターで読み込め。 Further Mosel 条件付変数と制約 条件付上下限 N変数の集合のすべての要素ではないが、それらの一部に上限値を適用したいとする。適用される上 限値は各々の変数によって異なり、これらは集合で与えられる。 しかしながら、データ表の要素がある量、たとえば20、より大きい場合に限って、それらは適用さ れるべきである。もし境界値が、これまで通例であったように、数値に依存しない場合には、forall(i in 1..N) x(i) <= U(i) を用いて表現されたであろう。しかしながら、条件付境界の場合には、これは少々変更されなけれ ばならない。 リスト6.14 writelnを用いて解データを出力する model WritelnEx uses "mmxprs" declarations a,b: mpvar end-declarations first:= 3*a + 2*b <= 400 98 second:= a + 3*b <= 200 profit:= a + 2*b maximize(profit) fopen("WritelnEx.dat",F_OUTPUT) writeln("Profit = ",getobjval) writeln("a=",getsol(a),": b=",getsol(b)) fclose(F_OUTPUT) end-model xi Ui CONDi CONDi Topics すなわち、下式のようになる。 forall(i in 1..N ¦ COND(i) > 20) x(i) <= U(i) このようにして、これらの例の2番目の行は、 COND(i)が20より大きい時はいつでも、i=1 to Nに 対して、変数x(i)はU(i)よりも小さいか等しくなければならない と読むことができる。 条件付変数 変数が動的配列であると宣言し、さらにある条件を満たす変数のみを作成するという場合、このよ うな変数の存在もまた条件付となりうる。この例はリスト6.15に示される。ここで実際に定義され る変数は、x(1), x(2), x(3), x(6), x(8) そして x(10)である。小さなモデルを作成してコンソー ルにそれを出力することによって、これは明らかとなる。 ¦ 垂直な棒の記号(¦)は、後に続く論理的な表現とともに条件付オペレーターを表し、 以下の表現が 正確である時はいつでもなされるか、またはそのようなもの と読むことができる。 リスト6.15 条件付変数の作成方法 model ConditionalEx declarations Elements = 1..10 COND: array(Elements) of integer x: dynamic array(Elements) of mpvar end-declarations COND:= [1, 2, 3, 0, -1, 6, -7, 8, -9, 10] この例はすでに第5章で見てきたが、そこでは添字集合もまた外部のファイルから入力された。詳細については、132ページ のリスト5.11を参照されたい。 Further Mosel 演習問題 リスト6.15のモデルを作り、実行せよ。作成された問題のLP形式行列を調べ、6つの変 数のみが存在することを確認せよ。 このようにしてexportprobを用いると、作成された問題を直接見ることができる。出力用のファイ ル名を明示しなくとも、行列は画面上に表示される。 基本的計画問題の構造 Moselモデルプログラミング言語はモデルの仕様に関するコマンドや手順ばかりでなく、高度なプロ グラミング言語の可能性をすべて有している。これはモデリング環境にとって強力な追加機能とな っている。それらのいくつかについて、以下に論じる。 if コマンド if 記述の一般形は以下の通りである。: if expression_1 then commands_1 [ elif expression_2 then commands_2 ] [ else commands_3 ] 99 end-if forall(i in Elements ¦ (COND(i) = i)) create(x(i)) ! build a little model to show what’s there obj:= sum(i in Elements) x(i) c:= sum(i in Elements) i*x(i) >= 10 exportprob(0,"",obj) end-model リスト 6.15 条件付変数の作成方法pics ブール表示expression_1を評価することによって、選択が行われる。すなわち、これがtrueの場合 はcommands_1が実行され、end-if記述に続く制御が実施される。オプションのelif部分が含まれる 場合は、ブール表示expression_2が評価され、これがtrueの場合は、end-if記述に続く制御が実施 される前にcommands_2が実行される。if あるいは elifのいずれの接頭辞を有する表現もtrueと評 価されない場合は、オプションのelse記述に続くcommands_3が実行される。プログラムはend-if記 述に続いて実行される。この例はリスト6.16に与えられている。この例においては、モデルの実行 中の出力を制御するために、整数DEBUGが用いられている。通常はDEBUGは0に設定され、Moselの出 力に関する記述では、データを読み込む時と場所が指定される。しかしながら、DEBUGはパラメタで あるので、値は実行中に変更され、たとえば値が1のときは、読み込まれるデータは画面に提示され、 正しく入力されているか否かの確認ができる。リスト6.17は、Moselを用いて、まずモデルプログラ ムが正常に実行され、続いてDEBUGパラメタの値を1に設定する例を示したものである。 リスト 6.16 モデリングの中で if 記述を用いる model IfEx parameters DEBUG=0 File=’InitEx.dat’ end-parameters declarations A: array(1..6) of integer Item: integer end-declarations initializations from File Item A as "A1" end-initializations if(DEBUG=1) then writeln("Item is ",Item,"\nA is ",A) else writeln("Data read in from ",File,"...") end-if end-model パラメタブロックには、実行時に変更されるものが標準的な値とともにリスト表示されている。コマンド列\nは、2重の引 用符で囲まれている場合には、newline文字として解釈される。 Further Mosel この機能を用いると、長いモデルのデバッギングには特に便利である。エラーの原因を探りたい場 合には、writelnコマンドを利用して変数の値を出力するデバッギングを行うのが普通のやり方であ る。この目的のために追加された記述がifで囲まれている場合は、これらはデバッギングが終了し てもモデルの中にそのまま残されることになり、それらを認識した上で削除するという操作は行わ れない。さらに詳細が続けて追加される場合には、追加的なデバッギングが必要となる。この場合 も、デバッギング記述はモデルの中に残ることになる。 演習問題 リスト6.16の例を入力し、通常の場合とDEBUG特性を用いた場合を実行せよ。モデルプロ グラムをelif記述を用いて変更し、次のレベルのDEBUGによって、データが収集されたファイル名を 追加的にプリントするようにせよ。 リスト 6.17 モデルのデバッギング特性を用いる 100 C:\Mosel Files>mosel ** Mosel ** (c) Copyright Dash Associates 1998-zzzz >cload IfEx Compiling `IfEx'... >run Data read in from InitEx.dat... Returned value: 0 >run DEBUG=1 Item is 25 A is [23,15,43,29,90,180] Returned value: 0 > もう一つの例は170ページのリスト6.22にある。 Topics case コマンド case 記述の一般形は、以下のようになる。: case Expression_0 of Expression_1: Statement_1 or Expression_1: do Statement_list_1 end-do [ Expression_2: Statement_2 or Expression_2: do Statement_list_2 end-do ...] [ else Statement_list_3] end-case ブール表示Expression_0を評価し、続けてそれをExpression_iと比較して合致していることを確認 することによって、選択が行われる。ここではStatement_i か Statement_list_iかのいずれかが実 行され(構築方式による)、end-case記述に続く制御が実施される。いずれの表現も合致せず、else 記述がある場合には、Statement_list_3が実行され、end-case記述に続く制御が実施される。これ を示す簡単な例がリスト6.18に与えられている。この例においては、配列Aの要素が探索され、規則 (いくらか奇妙ではあるが)にしたがって分類される。すなわち、値が0か、1から3の間か、8か10 の場合は’興味ある’であるが、それ以外の場合は、’興味ない’とされる。配列Aの各要素が分類され、 値に関する記述が画面にプリントされる。 演習問題 case構造を利用するモデルプログラムを構築せよ。あるいは前のプログラムをcase構造を 利用していくつかのデバッギングレベルを処理するように修正せよ。 Further Mosel forall ループ forall 記述の一般形は、以下のようになる。: forall (Iterator_list) Statement or forall (Iterator_list) do Statement_list end-do ここでStatement あるいは Statement_listはIterator_listによって作成されたそれぞれの添字の 組にしたがって繰り返し実行される。この例はリスト6.19に与えられている。 この例においては、Moselによって配列Fの最初の 20個にフィボナッチ数列の数を入れ、それらをコ ンソールにプリントする。この数列において、最初の2つの数字は1で、続く数字は前の2つの数字の 和とされる。添字集合Elemsでループを実施すると、簡単なif記述によって、最初の2つの項を初期 化しているか、そうでないときには、数列の次の項を決定するようにアルゴリズムを適用する、と いう手続きを決定する。 リスト 6.18 簡単な case 例 model CaseEx 101 declarations A: array(1..10) of integer end-declarations A:=[1,2,3,0,-1,1,3,8,2,10] forall(i in 1..10) do case A(i) of 0: writeln('A(',i,') is 0') 1..3: writeln('A(',i,') is between 1 and 3') 8,10: writeln('A(',i,') is either 8 or 10') else writeln('A(',i,') is not interesting') end-case end-do end-model Topics forallループを含む他の例として、リスト5.6にあるように決定変数をバイナリーに設定したり、リ スト5.11にあるように変数を明示的に作成したりするものがある。これらはいずれも前章に示した ものである。 演習問題 forallループを利用してモデルプログラムを作成し、入力し、ここに与えられた例を実行 せよ。 while ループ while 記述の一般形は、以下のようになる。: while (Expression) Statement or while (Expression) do Statement_list end-do 上の構成を用いると、ブール表示Expressionが評価され、Expression が trueである限りStatement あるいは Statement_listが実行される。Expression がfalseであると評価されると、while記述は 完全に飛ばされる。例がリスト6.20に与えられている。 ここでwhile記述が用いられて‘時間表times table’が作られ、行と列の数の積がプリントされる。各 行の最後にnewline文字がプリントされ、行の数字の間にはtab文字が用いられる。 リスト 6.19 簡単な forall 例 model ForallEx declarations Elems=1..20 F: array(Elems) of integer end-declarations forall(i in Elems) do if(i=1 or i=2) then F(i):=1 else F(i):= F(i-1) + F(i-2) end-if writeln("F(",i,")\t= ",F(i)) end-do end-model コマンド列 \t は、2重の引用符で囲まれた場合のみtab文字と解釈される。 演習問題 リストにある例を入力し、while記述を用いて自分の例を作成せよ。それをコンパイルし、 読み込み、実行して、出力をチェックせよ。 repeat ループ 最後のループ構造はrepeat ループであって、次のような一般形を有している。 repeat Statement_1 [ Statement_2…] 102 until Expression リスト 6.20 簡単な while 例 model WhileEx declarations i,j: integer end-declarations i:=1 j:=1 while(i <= 10) do while(j <= 10) do write(i*j) if(j=10) then writeln else write("\t") end-if j+=1 end-do j:=1 i+=1 end-do end-model ここでブール表示 Statement_1 (他の記述も含めて)は、ブール表示Expressionがfalseと評価され るまで繰り返し実行される。それに対してwhileループでは、Expressionが評価される前に実行され るので、repeatループにある記述が少なくとも一度は実行されることが保証されている。リスト6.21 に例が与えられている。 この例は、与えられた数値が素数であるか否かをテストする短いプログラムである。実行すると、 利用者は数字を入力するように要求され、それらが繰り返しテストされ、除数が見つかるか、また は素数であると判定される。 演習問題 リストにある例を入力して、実行せよ。入力数値が整数値で0より大きいかをテストする ように、プログラムを変更せよ。1の入力をどのように処理するかを別に考える必要があるであろう。 リスト 6.21 簡単な repeat 例 model RepeatEx declarations i: integer number: integer end-declarations i:=1 writeln("Input a positive integer:") readln(number) repeat i+=1 until ((number mod i =0) or i>sqrt(number)) if(i>sqrt(number)) then writeln(number," is prime!") else writeln(number," is divisible by ",i) end-if end-model Further Mosel プロシ-ジャと関数 記述と宣言の集合をサブルーティンの形に結合し、モデルの実行中に何度もそれを呼び出すことが 可能である。Moselでは、手順と関数という2種類のサブルーティンをサポートしている。これらは いずれもパラメタを有し、局所的データを定義し、再帰的に呼び出すことができる。 103 プロシージャ 手順はいくつかの記述からなり、数カ所で同時に実行できる。実行されると記述にしたがって実施 されるが、数値は返されない。 手順の例はリスト6.22に与えられており、モデルを実行すると最初に標識がプリントされる。パラ メタを用いたモデルに対しては、このような例によって出力を分類する有用な方法が得られ、特に 情報が画面でなくてファイルに送られる場合には効果的となる。リスト6.23はConsole Xpressから このプログラムを利用する例を示している。 モデルを実行すると最初に標識を出力する簡単な手順を作成せよ。モデルがパラメタや他の設定を 外部ファイルから行う場合は、これらの出力を用いてもよい。 手順procedure 手順ブロックは次のような形式を有する。: procedure proc_name [ (param_list) ] proc_body end-procedure ここで proc_name は手順名であって、param_listはコンマで分離されたパラメタのリストである。 この手順が呼ばれると、proc_bodyにある記述が実行される。 Topics リスト 6.22 簡単な手順 model ProcEx parameters DEBUG=0 File='InitEx.dat' end-parameters procedure banner(DEBUG:integer,File:string) writeln("This is the ProcEx model, release 1") if(DEBUG = 1) then writeln("\tDebugging on...") end-if if(File <> 'InitEx.dat') then writeln("\tInput file ",File," in use...") end-if writeln end-procedure banner(DEBUG,File) end-model リスト 6.23 パラメタを入力して手順を実行する C:\Mosel Files>mosel ** Mosel ** (c) Copyright Dash Associates 1998-zzzz >cload ProcEx Compiling `ProcEx'... >run This is the ProcEx model, release 1 Returned value: 0 >run 'DEBUG=1,File=data.dat' This is the ProcEx model, release 1 Debugging on... Input file data.dat in use... Returned value: 0 > 手順は、キーワードforwardを用いて使用する前に宣言されている限りは、モデルの最後に一緒においてもよい。詳細につい ては、以下の節を参照されたい。デバッギングに関する追加情報は、compile あるいはXPRMcompmodでgフラッグを利用すれ 104 ば入手できる。 Further Mosel 関数 関数は1ヶ所あるいは数カ所で一緒に実行される記述の集合であって、数値を返す。 リスト6.24は完全数perfect numbersを求める3つの関数を用いる例である。完全数は、除数の和が その数自体と等しくなるような数である。最初の関数isPrimeは数が素数であるか否かをチェックす るし、また2番目の関数は数の正のべき乗を計算する。最終的に、3番目の関数はこれらの関数を 呼び出してメルセンヌ素数Mersenne Primes数を生成し、ユークリッド公式を用いて完全数を計算す る。 この例で注意したいことは、いずれの関数についての記述もキーワードreturnedを備えていなけれ ばならないということである。その値は関数が存在する場所に戻され、関数の最初に宣言されてい るように、型typeの値に設定される。この例では、if記述によって、あるいはまたべき乗計算によ って整数値がreturnedに割り当てられる。 演習問題リスト6.24にある例を入力して実行し、最初の4個か5個の完全数を求めよ。関数がより 一般的で、不正な引数を棄却するようにプログラムを修正せよ。 関数 関数ブロックは、以下のような形式を有する。: function func_name [ (param_list) ]: type func_body end-function ここで func_name は関数名で、param_list はコンマで分離された形式的なパラメタのリストで、 typeは返却する値の基本的な型を表す。手順が呼ばれると、func_bodyにある記述が実行され、数値 が返される。 Topics リスト 6.24 関数を用いて完全数を計算する model PerfectEx function isPrime(number: integer): boolean i := 1 repeat i+= 1 until ((number mod i = 0) or i > sqrt(number)) if(i > sqrt(number)) then returned := true else returned := false end-if end-function function power(a,b: integer): integer pow := 1 while(b > 0) do pow := pow*a b-=1 end-do returned := pow end-function function Perfect(n: integer): integer Mn := power(2,n)-1 if(isPrime(Mn)) then returned := power(2,n-1)*Mn else returned := 0 end-if end-function i := 1; k := 1 while(k<5) do i+=1 if(Perfect(i) > 1) then 105 write(Perfect(i)," = ") forall(j in 0..i-1) write(power(2,j),"+") write(power(2,i)-1) forall(j in 1..i-2) write("+",(power(2,i)-1)*power(2,j)) writeln; k+=1 end-if end-do end-model 関数の前進型宣言も許されている。詳細については、以下の節を参照されたい。 Further Mosel 再帰性 関数も手順はいずれも直接あるいは間接にそれらを呼び出すことによって、再帰的に利用すること ができる。これは特に便利なので、例を紹介しよう。リスト6.25において、関数hcfは再帰的に呼ば れ、2つの数の最大公約数が決定される。 演習問題リスト6.25にある例を入力して、いくつかの例を用いて実験せよ。 リスト 6.25 関数の再帰性 model HcfEx function hcf(a,b: integer): integer if(a=b) then returned:=a elif(a>b) then returned:=hcf(b,a-b) else returned:=hcf(a,b-a) end-if end-function declarations A,B: integer end-declarations write("Enter two integer numbers:\n A: ") readln(A) write(" B: ") readln(B) writeln("Highest common factor: ",hcf(A,B)) end-model Topics 前進型宣言 Moselでは再帰性ばかりでなく、交差再帰性cross recursionという、2つのサブルーティンが交互 にお互いを呼ぶ性質も可能であることをすでに述べた。しかしながら、すべての関数と手順は呼ば れる前に宣言されていなければならないので、最初に定義されたとサブルーティンは未だ定義され ていないもう一つのサブルーティンを呼ぶ必要に迫られる。解決法としては、キーワードforward を用いて、コマンドが定義される前にひとつか2つのサブルーティンを宣言しておけばよい。 ここで重要なことは、用いられるいろいろな用語の間の相違点を明確にしておくことである。サブ ルーティンの宣言declarationには、名前、パラメタ(型と名前)、そして関数に対しては返却する 数値の型が記述されている。モデルプログラムの中で後に続くサブルーティンの定義definitionに は、サブルーティンの本体である、呼ばれた場合に実行されるコマンド群が含まれる。 この違いを例示するのがよいであろう。リスト6.26では、ランダムに並べられた数値の配列を増加 順に並べるクイックソートアルゴリズムを実行する。ソーティングアルゴリズムを開始する手順 startは、プログラムの最後に定義されているが、呼ばれる前に、最初に宣言されておく必要がある。 リスト 6.26 サブルーティンの前進型宣言 model "Quick Sort" 106 parameters LIM=50 end-parameters forward procedure start(L:array(range) of integer) declarations T: array(1..LIM) of integer end-declarations forall(i in 1..LIM) T(i):=round(.5+random*LIM) writeln(T) start(T) writeln(T) "Declaration v. definition…" Further Mosel ! swap the positions of two numbers in an array procedure swap(L:array(range) of integer, i,j:integer) k:=L(i) L(i):=L(j) L(j):=k end-procedure ! main sorting routine procedure qsort(L:array(range) of integer, s,e:integer) ! Determine partitioning value V:=L((s+e) div 2) i:=s; j:=e repeat ! Partition into two subarrays while(L(i)<V) i+=1 while(L(j)>V) j-=1 if(i<j) then swap(L,i,j) i+=1; j-=1 end-if until i>=j ! Recursively sort the two subarrays: if(j<e and s<j) then qsort(L,s,j) end-if if(i>s and i<e) then qsort(L,i,e) end-if end-procedure ! start of the sorting process procedure start(L:array(r:range) of integer) qsort(L,getfirst(r),getlast(r)) end-procedure end-model リスト 6.26 サブルーティンの前進型宣言 Topics クイックソートアルゴリズムの概念は、ソートされる配列を2つに分割することである。これらの 一つは分割値よりも小さなすべての値を含むし、またもう一つはこの値よりも大きなすべての値を 含む。分割操作は、すべての値がソートされるまで、再帰的に2つの部分配列に対して適用される。 演習問題 リスト6.26にあるモデルを入力せよ。あるいは前の例に対して、すべてのサブルーティン 107 の定義をプログラムの最後におくように修正せよ。 をして、いくつかの例を用いて実験せよ。 forwardキーワードは、モデルファイルに何らかの構造を与え、それらをより連続的なものにする場 合に特に有用である。モデルに関する詳細をすべてサブルーティンの中におき、これらについての 詳細な記述を最後におくことによって、プログラムの主要な流れが明白になる。 ここに至って、Mosel モデルプログラミング言語の諸特性を利用することによって、より複雑なモ デルを作成し、好きなインターフェイスを用いて解くことが可能となっていることに気づいたであ ろう。これらの開発過程においては、Mosel言語のさらなる特性が必要となるかも知れないが、これ らについてはMosel Reference Manualを参照されたい。次の最終章では、これまで用いた用語に関 する何らかのまとめを与える。 まとめ 本章では、以下のことを学んだ。: いろいろな次元を有する固定サイズあるいは動的配列を初期化する; 外部ソースやアプリケーションを用いてモデルデータを入力あるいは転送する; 条件付表現あるいは変数を作成する; 選択あるいはループ構造を作成する; 関数あるいは手順を書く. 108 第7章 用語のまとめ 2値変数(binary variable) 解において、0か1のいずれかの値をとる決定変数。2値変数を含む問題は、混合型整数計画問題で ある。 境界値(bound) 決定変数に関する単純な上界値あるいは下界値を与えたり、または決定変数をある値に固定させる のに用いられる簡単な制約条件。 制約条件(constraint) 最適解における決定変数の値が満たすべき線形不等式(または等式)。 制約条件(constraint type) 5つの型の制約条件がある。すなわち、(より大きい、または等しい), (より小さい、または等しい), (等しい), 領域が与えられているか無制約, たとえば目的関数。 連続変数(continuous variable) 0と無限大の間(一般には何らかの下限と上限の間)の値をとる決定変数。1次変数とも呼ばれる。 制御(control) 求解過程に影響するような中のパラメタ。 .. = 決定変数(decision variable) 最適化によって値が決定される。線形計画法 (LP)においては、すべての決定変数は線形(あるいは 連続)で、0と無限大の間(一般には何らかの下限と上限の間)の値をとる。混合型整数計画法 (MIP) においては、変数によっては2値あるいは整数値、あるいは特定の順序付集合となる。単に‘変数’ と呼ばれることもある。 dj リデューストコストを見よ。 双対値(dual value) 制約条件の右辺値の単位量の変化に伴う目的関数値の変化量。シャドウプライスー制約条件によっ て割り当てられる資源の‘価格’ーと呼ばれることもある。双対値は制約条件がどれだけきついかを表 す基準である。制約条件がきつい場合は、少しだけ緩和されたとすると、目的関数値がよりよくな ることが期待される。双対値はこれに対する数値的基準を与える。一般に、行の右辺値が1だけ増加 すると、目的関数値は行の双対値だけ増加する。より具体的には、双対値は限界的な概念であるの で、右辺値が十分少量だけ増加すると、目的関数値は双対値だけ増加することになる。 グローバル要素(global entity) 2値あるいは整数値、あるいは部分的整数あるいは半連続変数、あるいは特定の順序付集合。 入力コスト(input cost) The original objective coefficient of a variable. 整数計画問題(IP problem) 混合型整数計画問題 (MIP) の別名で、特に連続変数を含まない問題。 . 109 ™ ™ ⋅Glossary of 整数変数(integer variable) 0, 1, 2, など上限値までのいずれかの値をとる決定変数。整数変数を含む問題は混合型整数計画問 題である。 キーワードkeyword (in Mosel) Xpress-MP model の中のキーワードは、モデルのコマンドかあるいはブロックを導入/終了するかで ある。Mosel モデルプログラミング言語の要素か、または読み込まれたライブラリーモジュールか らの要素かのいずれかである。典型的なキーワードはdeclarations とusesである。 線形表現(linear expression) constant decision variableの形を有する項(あるいは項の和)。 線形不等式あるいは等式(linear inequality or equation) 定数項(右辺値)に対して‘より大きい、または等しい’, ‘より小さい、または等しい’, ‘等しい’のいず れかが成立するという線形表現。 線形計画法 (LP) problem 線形の決定変数、制約条件、目的関数からなる決定問題。制約条件と目的関数は決定変数の1次関 数である。データ値(すなわち変数の係数と制約条件の定数項)はすべて既知である。 1次変数(linear variable) 0と無限大の間(一般には何らかの下限と上限の間)の値をとる決定変数。連続変数とも呼ばれる。 混合型整数計画問題(mixed integer programming (MIP) problem) いくつかのグローバル要素、すなわち2値変数あるいは整数変数、あるいは特定の順序付集合を含 む線形計画問題。 モデル(model) 問題を定義する決定変数、制約条件、目的関数、そしてデータの集合。問題に対するXpress-MP表現 を意味する場合もある。 モデルの例(model instance) 明示的なデータ、すなわちデータとパラメタの値、を完備したモデル構造。 モデルプログラム(model program) 解くための指令を備えたモデル。これらはMoselによって実行され、Moselモデルプログラミング言 語で書かれている。 モデルの構造(model structure) 決定変数、データ表、制約条件に関する定義とそれらの間の代数的関係であって、データとパラメ タの値は明示されていない。 目的関数(objective function) 決定変数の値を選択することによって最適化(最大化あるいは最小化)される線形あるいは2次表現。 最適解(optimal solution) 目的関数の値を最適(最大あるいは最小)にする解。 部分的整数変数(partial integer variable) 解において、0, 1, 2, など上限値までのいずれかの値をとり、それを超える部分が連続値である決 定変数。部分的整数変数を含む問題は混合型整数計画問題である。 問題の特性(problem attribute) 求解過程においてOptimizerによって設定される値で、ライブラリー利用者によって検索可能である。 これは解くべき問題あるいは解の重要な特性である。 110 リデューストコスト(reduced cost) 決定変数の目的関数における係数値で、当該変数が限界から動いた場合の変化量を表す。 場合によっては、‘dj’と表されることもある。 右辺値(right hand side (RHS)) 制約条件における定数項。慣例として、Xpress-MPでは必要ではないが、値は制約条件の右辺に書か れる。 半連続変数(semi-continuous variable) 解の決定変数で、0か下限値と上限値の間の連続値をとる。半連続変数を含む問題は混合型整数計画 問題である。 シャドウプライス(shadow price) 双対値を見よ。 スラック値(slack value) 制約条件と右辺値の差。 解(solution) 制約条件を満たす決定変数の値の集合。最適解を見よ。 特定順序付集合(special ordered set (SOS)) 特定の条件を満たさねばならない変数の順序付集合。型1の特定順序付集合( ‘SOS1’ か ‘S1 set’)は たかだか1個の非零変数を含む。型2の特定順序付集合( ‘SOS2’ か ‘S2 set’)はたかだか2個の非零 変数を含み、2つの変数が非零の場合は、それらの順序はお互いに隣り合っていなければならない。 Xpress-MPにおいては、順序は‘参照行reference row’として与えられ、それらは通常の制約条件であ るか、または別の無制約条件である。変数が整数値をとらなければならないという条件はない。 変数(variable) 決定変数を見よ。 111 索引 記号Symbols ! コメントcommentsを見よ \0 無終了null-terminatedを見よ \n 新行文字newline characterを見よ \t タブ文字tab characterを見よ ¦ 条件付オペレーターconditional operatorを見よ A 現問題active problem 17, 33, 34, 55, 74 アルゴリズムalgorithm 40, 44, 78 設定setting 40, 78 配列arrays バイト配列byte arrays 102 動的配列dynamic arrays 133, 146, 147, 157, 160 入データentering data 144, 146, 148 固定サイズ配列fixed size arrays 146 多次元配列multi-dimensional arrays 144 疎配列sparse arrays 146, 157 as 149 B バッチモードbatch mode 26, 32 BCL 48, 61 制約条件constraints 63, 64, 68 エラーチェックerror checking 95 ヘッダーファイルheader file 65 初期化initialization 62 初期化と終了initialization and termination 62 Optimizer にモデルを読み込むloading models in the Optimizer 61, 98 ログファイルlog files 67 メモリー管理memory management 66 メッセージレベルmessage level 67 目的関数objective function 64 最適化optimization 64, 65 sense 73 問題管理problem management 62, 66, 96 問題ポインターproblem pointer 98 プログラム出力program output 67 返却値return values 95 解情報solution information 65 変数variables 62, 67 Optimizer ライブラリーを用いたwith the Optimizer library 61, 96 行列ファイルを書くwriting matrix files 71 ブロックblocks 120 112 宣言declarations 120, 130, 133 関数functions 171 初期化initializations 130, 134, 149 モデルmodel 120 パラメタparameters 135, 162 手続きprocedure 169 限界値bounds 62, 88, 177 条件付conditional 159 分枝限定法Branch and Bound 43 強盗問題Burglar Problem 118 バイト変換ByteConversion 101 C コールバックcallbacks 84, 104, 113 ケースcase 164 cload 31 列columns 40, 76, 86, 87 変数variablesを見よ コメントcomments 126 コンパイルcompile 29 条件付オペレーターconditional operator 160 コンソールConsole Xpress 4, 5, 25 現問題active problem 33, 34 Xpress-MP Essentials Index 184 バッチモードbatch mode 32 モデルファイルをコンパイルするcompiling model files 29 制御controls. See controls デバッグdebugging 30 整数問題integer problems 42 読み込まれたモデルのリストlist of loaded models 34 問題を読み込むloading problems 30, 37 モデル管理model management 33, 35 Mosel. See Mosel 最適化optimization 29, 37 Optimizer. See Optimizer モデルを実行するrunning models 30, 137, 170 解情報solution information 31, 37 行列ファイルを書くwriting matrix files 35 制約条件constraints 12, 40, 86, 119, 177 条件付conditional 159 限界値boundsをも見よ。 連続行continuation lines 121 制御controls 3, 43, 44, 81, 82, 111, 177 ライブラリー接頭辞library prefix 82 作成するcreate 133, 146 D データベースdatabases 3, 152 113 デバッグdebugging 14, 30, 51, 95, 163, 170 決定変数decision variables. See variables 宣言declarations 120, 130, 133 DEFAULTALG 44 削除するdelete 35 提示するdisplay 31 dj. リヂューストコストreduced costを見よ。 DLLs 4, 52 実行するdo 164 DoubleHolder 110 双対値dual values 40, 54, 178 動的dynamic 146 E elif 161 else 161, 164 要素entities. グローバル要素global entitiesを見よ。 エラーチェックerror checking 95, 111 エラーメッセージerror messages 14, 30 終了コードexit codes 95 転送データexporting data 148 スプレッドシートへto spreadsheets 153 テキストファイルへto text files 151 writelnを用いる 158 exportprob 35, 161 F F_APPEND. ファイルfiles, データを追加するappending dataを見よ F_INPUT. 入力列input streamを見よ F_OUTPUT. 出力列output streamを見よ fclose 157, 159 フィボナッチ列Fibonacci sequence 165 ファイル形式file formats データファイルdata files 149 LP形式LP format 36, 73 疎データ形式sparse data format 147 ファイルfiles データを追加するappending data 158 バッチファイルbatch files 4 バイナリーモデルファイルbinary model files (.bim) 14, 30 Excel (.xls) 155 ヘッダーファイルheader files (.h) 50, 65, 75 logファイルlog files (.log) 67, 84 LP行列ファイルLP matrix files (.lp) 19, 36, 59, 71 Mosel ファイルfiles (.mos) 28, 49 MPS行列ファイルmatrix files (.mat) 19, 35, 59, 71 解ファイルsolution files (.prt) 38, 39, 76 終了するfinalize 141 114 fopen 158, 159 forall 125, 165 forward 170, 174 function 171 関数functions 171 再帰recursion 173 G getobjval 123 getparam 157 getsol 127 GLOBAL 43 グローバル要素global entities 38, 93, 178 グローバル探索global search 43, 80 グラフ化graphing 15, 21 H ハイカー問題Hiker Problem 135 I if 161 データを読み込むimporting data 148 テキストファイルからfrom text files 130, 149 readlnを用いて 156 添字集合index sets 140 初期化initialization 140 数値指標numerical indices 124 オペレーターoperators 142 記号列指標string indices 127, 132 初期化initialization 50, 62, 74 initializations 130, 134, 149 入力コストinput cost 40, 178 入力列input stream 156, 158 ソフトウェアをインストールするinstalling software 2, 6 整数integer 124, 145 整数計画法 41, 42, 79, 93, 119, 178 整数解integer solutions. グローバル探索global searchを見よ IntHolder 110 is_binary 121 is_integer 42, 79, 121 反復iteration. ループloopingを見よ IVEaddtograph 21 IVEinitgraph 21 J Java 4, 106 コールバックスcallbacks 113 文字配列と記号列character arrays and strings 111 エラーをチェックするchecking errors 111 115 制御と問題の特性controls and problem attributes 111 数値配列と参照numerical arrays and references 110 K キーワードkeywords 13, 179 ナップサック問題knapsack problem 129 L ライブラリー 4, 47 結合combining 96 構成要素components 48, 50 デバッグdebugging 51 エラーチェックerror checking 95 ヘッダーファイルheader files 50, 65, 75 メモリー管理memory management 56, 66, 75 Mosel. Mosel ライブラリーを見よ Optimizer. Optimizerライブラリーを見よ 問題ポインターproblem pointers 55, 74, 98 返却値return values 95 線形計画法 3, 179 リストlist 35 load 30 ループlooping 125, 165 LP 緩和relaxation. グローバル探索global searchを LPLOG 16 LPOBJVAL 91, 94 LPSTATUS 83 M 行列ファイルmatrix files 19, 35, 59, 71, 74 MAXIM 37 フラッグflags 41, 43 最大化maximize 123 メモリー管理memory management 56, 66, 74, 146 メルセンヌ素数Mersenne Primes 171 MINIM 37 最小化minimize 123 MIPOBJVAL 94 混合型mixed 整数計画法 3, 179 mmive 21 mmodbc 3, 152 mmxprs 122 mod 168 モデルmodel 180 データdata 118, 148 モデルを提示するdisplaying model 161 エディターeditor. Xpress-IVEを見よ 遺伝的generic 129 例instance 118, 129, 148, 180 116 キーワードkeywords 13, 179 モデル管理model management 33, 35, 54 モデルプログラムmodel program 2, 13, 28, 122, 180 構造structure 180 モデルmodel 120 モデリングmodeling 言語language 9, 26, 48, 117, 139 汎用性versatility 129 Mosel 2, 9, 26 現問題active problem 33, 34 バッチモードbatch mode 32 バイナリーモデルファイルbinary model files 14, 30 コンパイラーライブラリーcompiler library. See Mosel ライブラリー モデルファイルをコンパイルするcompiling model files 13, 29, 50 デバッグdebugging 12, 30 フラッグflags 32 グラフ利用者インターフェイスgraphical user interface 4, 9 ライブラリー. See Mosel ライブラリー ライブラリーモジュールlibrary modules 2, 26 ODBC, mmodbc 3, 152 Optimizer, mmxprs 2, 122 読み込まれたモデルのリストlist of loaded models 34 モデルを読み込むloading models 14, 30 メモリー管理memory management 146 モデル管理model management 33, 35 モデルプログラムmodel program. See model 最適化optimization 12, 29 パラメタparameters 30, 51, 135, 137, 163, 170 実行時間ライブラリーrun time library. See Mosel ライブラリーを見よ モデルを実行するrunning models 14, 30 コマンドを短くするshortening commands 31 解情報solution information 31, 123 行列ファイルを書くwriting matrix files 19, 35 Mosel 言語 language. モデリングmodelingを見よ Mosel ライブラリー 48, 49 コンパイラーライブラリーcompiler library 50 モデルファイルをコンパイルするcompiling model files 50, 51 エラーチェックerror checking 95 ヘッダーファイルheader files 50 初期化と終了initialization and termination 50 モデルの実行を中止するinterrupting a model run 58 読み込まれたモデルのリストlist of loaded models 55 モデルを読み込むloading models 49, 51 モデル管理model management 51, 54, 56, 58 問題ポインターproblem pointer 55 返却値return values 95 117 実行時間ライブラリーrun time library 50 解情報solution information 52, 53 行列ファイルを書くwriting matrix files 59 mosel See Mosel MPS file. See matrix files mpvar 120, 146 マルチプロセッサー計算multiprocessor computing 3 N 新行文字newline character 162 零終了null-terminated 89 O 目的関数objective function 12, 40, 88, 120, 180 最大化maximizing 37, 65, 122 最適値optimum value 14, 31, 53, 91, 94, 180 解を得るobtaining a solution. 解solutionを見よ ODBC 152 Optimizer 3, 9, 36, 74 アルゴリズムalgorithm. アルゴリズムalgorithmを見よ 制御controls. 制御controlsを見よ 入力ファイルinput files 36, 74 ライブラリーlibrary. Optimizerライブラリーlibraryを見よ log ファイルfiles 84 行列ファイルmatrix files 36, 76 メモリー管理memory management 74 メッセージレベルmessage level 85 Mosel モジュールmodule. Moselを見よ 出力output 84 パラレルparallel 3 性能performance. 性能調整performance tuningを見よ 後処理postsolve 81 前処理presolve 80 問題の特性problem attributes. 問題problemを見よ 特性attributes 解solution 37, 40, 76 脈絡安全thread-safety 74 Optimizerライブラリーlibrary 48, 74, 99 行/列名を追加するadding row/column names 90 高度ライブラリー関数advanced library functions 74, 86 アルゴリズムalgorithm. アルゴリズムalgorithmを見よ コールバックス関数callback functions 84 制御controls. 制御controlsを見よ エラーチェックerror checking 95 初期化と終了initialization and termination 74 log ファイルfiles 84 行列matrix 86, 92 メモリー管理memory management 74 118 最適化optimization 74, 76 出力output 84 問題の特性problem attributes. See problem 特性attributes 問題の入力problem input 74, 76, 86, 98 問題ポインターproblem pointer 74, 98 返却値return values 95 解情報solution information 76, 90, 91, 94 脈絡安全thread-safety 74 解を理解するunderstanding the solution 76 with BCL 61, 96 optimizer Console Xpressを見よ 出力列output stream 158 P パラメタparameters 30, 51, 135, 137, 170 parameters 135, 162 完全数perfect numbers 171 性能調整performance tuning 43, 81 後処理postsolve 81 PRESOLVE 82 前処理presolve 80 設定を変更するchanging settings 82 素数prime numbers 168 メルセンヌMersenne 171 PRINTSOL 38 問題の特性problem attributes 82, 111, 181 ライブラリー接頭辞library prefix 82 問題名problem name 27, 40, 120 問題の統計量problem statistics 20, 37, 38, 67 procedure 169 手続きprocedures 169 再帰性recursion 173 Q 2次計画法 3 クイックソートアルゴリズムquick sort algorithm 174 QUIT 27 停止quit 27 R read / readln 156 READPROB 37 再帰性recursion 173 リヂューストコストreduced cost 40, 54, 181 repeat 157, 167 制限restrictions. トライアルモードtrial mode returned 171 行rows 38, 40, 86, 87 119 制約条件constraintsを見よ run 30, 137, 170 S スカラーscalars 129 安全システムsecurity system 10, 27, 52 select 35 選択selections 162 スラック値slack values 40, 54, 181 ソフトウェアのインストールsoftware installation 2, 6 解solution 14, 39, 53, 65, 90, 181 グラフgraphs 15, 21 出力を理解するunderstanding output 40, 76 観察するviewing 37, 76, 123, 127 疎sparsity 88, 146 特定順序付き集合Special Ordered Sets (SOS) 38, 94, 181 スプレッドシートspreadsheets 3, 152 配列規模array sizing 155 Microsoft Excel 154 SQL 153 SQLconnect 153, 154, 156 SQLdisconnect 153, 154, 156 SQLexecute 153, 154, 156 SQLreadinteger 156 sqrt 168 string 124, 132, 134 記号列指標string indices 127 StringHolder 110 サブルーティンsubroutine ライブラリー. See ライブラリー sum 125 総和summation 125 T タブ文字tab character 166 テキストベースtext-based. Console Xpressを見よ then 161 時間表times table 166 トライアルモードtrial mode 10, 27 U until 167 uses 123 V 変数variables 配列array 67, 124 バイナリーbinary 119, 121, 177 条件付conditional 160 連続continuous 177 作成creation 133, 146 120 決定変数decision variables 12, 40, 86, 119, 178 動的配列変数dynamic array variables 133, 147, 160 整数integer 41, 79, 93, 121, 179 線形linear 179 部分的整数partial integer 180 半連続semi-continuous 181 添字付変数subscripted variables 124 vbNullString 103 Visual Basic 4, 99 引数型argument types, 100 コールバックスcallbacks 104 文字配列character arrays 101 NULL arguments 103 数値配列numerical arrays 100 W 警告メッセージwarning messages 14, 30 while 166 Windows DLLs. See DLLs write / writeln 123, 158 WRITEPRTSOL 37, 38 X XPRBaddterm 64 XPRBarrvar 68 XPRBctr 63 XPRBdelprob 66 XPRBexportprob 72 XPRBfree 66 XPRBgetobjval 65 XPRBgetsol 65 XPRBgetXPRSprob 98 XPRBinit 62 Xpress-MP Essentials Index 189 XPRBloadmat 98 XPRBmaxim 65 XPRBnewarrsum 69 XPRBnewarrvar 68 XPRBnewctr 63 XPRBnewprob 62, 98 XPRBnewvar 63 XPRBprob 62 XPRBsetmsglevel 67 XPRBsetobj 64 XPRBsetsense 73 XPRBsetterm 64 XPRBvar 63 Xpress-IVE 4, 9 121 ファイルを追加するadding files 11 制御オプションcontrol options 16 デバッグdebugging 12, 14 環境設定environment settings 22 キーワードメニューkeyword menu 13 モデルエディターmodel editor 10, 12 モデル管理model management 13, 14, 17 プロジェクト管理project management 10, 11 設定settings 22 解グラフsolution graphs 15, 21 ウィンドウwindows 構築枠Build pane 14 要素枠Entities pane 15 言語/タブ枠Language/Tabs pane 22 配置枠Locations pane 15 その他枠Misc pane 22 出力/入力枠Output/Input pane 14 プロジェクトファイル枠Project Files pane 10 Sim:Obj(iter) pane 15 利用者グラフ枠User Graph pane 21 writing matrix files 19 Xpress-MP 1 構成要素components 2 Console Xpress. Console Xpressを見よ グラフ利用者インターフェイスgraphical user interface 9 インストールinstallation 2, 6, 27 インターフェイスinterfaces 4, 6 ライブラリー. See ライブラリー ライセンスlicenses 10 Mosel. Moselを見よ Optimizer. Optimizerを見よ 利用上の制限restrictions on use 10, 27 安全システムsecurity system 10 ライブラリーを設定する 2 Xpress-IVE. Xpress-IVEを見よ xprm_mc 50 xprm_rt 50 XPRMalltypes 53 XPRMcompmod 51 XPRMexportprob 59 XPRMfindident 53 XPRMfree 50 XPRMgetdual 54 XPRMgetmodinfo 56 XPRMgetnextmod 56 XPRMgetobjval 53 122 XPRMgetrcost 54 XPRMgetslack 54 XPRMgetvsol 53 XPRMinit 50 XPRMisrunmod 58 XPRMloadmod 51 XPRMmodel 51 XPRMmpvar 53 XPRMrunmod 51 XPRMstoprunmod 58 XPRMunloadmod 56 XPRSaddnames 90 XPRSaddrows 81, 92 XPRSchgbounds 101 XPRScreateprob 75 XPRSdestroyprob 75 XPRSfree 75 XPRSgetdblattrib 83 XPRSgetdblcontrol 82 XPRSgetintattrib 83 XPRSgetintcontrol 82 XPRSgetnames 102 XPRSgetobj 100 XPRSgetsol 90 XPRSgetstrattrib 83 XPRSgetstrcontrol 82 XPRSinit 74 XPRSloadglobal 93 XPRSloadlp 86 XPRSmaxim 76 flags 78, 80 XPRSreadprob 76 XPRSsetcbmessage 85 XPRSsetdblcontrol 83 XPRSsetintcontrol 83 XPRSsetlogfile 84 XPRSsetstrcontrol 83 XPRSwriteprtsol 76 123