第8章 OptaPlanner SolverManager

SolverManager は、REST およびその他のエンタープライズサービスの計画問題の解決を簡素化するための 1 つ以上の ソルバー インスタンスのファサードです。

Solver.solve (...) メソッドとは異なり、SolverManager は、次の特性があります。

  • SolverManager.solve (…) はすぐに戻ります。呼び出し元のスレッドをブロックせずに、非同期解決の問題をスケジュールします。これにより、HTTP およびその他のテクノロジーのタイムアウトの問題が回避されます。
  • SolverManager.solve (…) は、同じドメインの複数の計画問題を並行して解決します。

内部的には、 SolverManager は、Solver.solve (…) を呼び出すソルバースレッドのスレッドプールと、最適なソリューション変更イベントを処理するコンシューマースレッドのスレッドプールを管理します。

Quarkus と SpringBoot では、 SolverManager インスタンスがコードに自動的に挿入されます。Quarkus または SpringBoot 以外のプラットフォームを使用している場合は、 create (…) メソッドを使用して SolverManager インスタンスをビルドします。

SolverConfig solverConfig = SolverConfig.createFromXmlResource(".../cloudBalancingSolverConfig.xml");
SolverManager<CloudBalance, UUID> solverManager = SolverManager.create(solverConfig, new SolverManagerConfig());

SolverManager.solve (…) メソッドに送信される各問題には、一意の問題 ID が必要です。後で getSolverStatus (problemId) または terminateEarly (problemId) を呼び出すと、その問題 ID を使用して計画の問題を区別します。問題 ID は、LongStringjava.util.UUID などのイミュータブルなクラスである必要があります。

SolverManagerConfig クラスには、並行して実行されるソルバーの数を制御する parallelSolverCount プロパティーがあります。たとえば、 parallelSolverCount プロパティー `が 4 に設定されていて、5 つの問題を送信すると、4 つの問題がすぐに解決を開始し、最初の問題の 1 つが終了すると 5 番目の問題が開始します。これらの問題がそれぞれ 5 分間解決した場合、5 番目の問題は 10 分で終了します。デフォルトでは、 parallelSolverCountAUTO に設定されており、ソルバーの moveThreadCount に関係なく、CPU コアの半分に解決されます。

最適なソリューションを取得するには、終了を解決した後、通常は SolverJob.getFinalBestSolution() を使用します。

CloudBalance problem1 = ...;
UUID problemId = UUID.randomUUID();
// Returns immediately
SolverJob<CloudBalance, UUID> solverJob = solverManager.solve(problemId, problem1);
...
CloudBalance solution1;
try {
    // Returns only after solving terminates
    solution1 = solverJob.getFinalBestSolution();
} catch (InterruptedException | ExecutionException e) {
    throw ...;
}

ただし、ユーザーがソリューションを必要とする前にバッチの問題を解決する方法と、ユーザーがソリューションを積極的に待っている間にライブで解決する方法の両方について、より良いアプローチがあります。

現在の SolverManager 実装は単一のコンピューターノードで実行されますが、将来の作業は、クラウド全体にソルバーの負荷を分散することを目的としています。

8.1. 問題のバッチ解決

バッチ解決とは、複数のデータセットを並行して解決することです。バッチ解決は夜間処理で特に役立ちます。

  • 通常、深夜には問題の変化はほとんどまたはまったくありません。一部の組織は期限を強制します。たとえば、深夜零時までに休日のリクエストを送信するといったものです。
  • ソルバーは、結果を待つ人が誰もいないため、CPU リソースが安価であることが多いため、はるかに長く、多くの場合は数時間実行できます。
  • 翌営業日に従業員が職場に到着したときにソリューションが利用できます。

手順

parallelSolverCount によって制限した上で並列バッチでの問題解決するには、各データセットのためには、以下のクラスによって作成した以下の各データについて solve (...) を呼び出します。

public class TimeTableService {

    private SolverManager<TimeTable, Long> solverManager;

    // Returns immediately, call it for every data set
    public void solveBatch(Long timeTableId) {
        solverManager.solve(timeTableId,
                // Called once, when solving starts
                this::findById,
                // Called once, when solving ends
                this::save);
    }

    public TimeTable findById(Long timeTableId) {...}

    public void save(TimeTable timeTable) {...}

}