0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

プロジェクトの全体像と設計原則~連載 TSPソルバ実装記01~

Last updated at Posted at 2025-10-18

はじめに

私は, 巡回セールスマン問題(TSP)のソルバを本格的に開発し, その大規模な実験結果を効率的に管理したいと考えていました.

最初は Python でプロトタイプを実装しましたが, 大規模な問題や多数の実験を行うには, どうしても速度に限界があるという壁にぶつかりました. そこで C#への移植を決断しました. (「C++が最適なのでは?」という声もあるかと思いますが, 今回は自身の技術スタックと生産性を考慮して C#を選択しました. この点には目を瞑っていただけると幸いです.)

しかし, C#で実装を進めるにあたり, 実験管理のデファクトスタンダードである MLflow を直接扱うのが困難であるという問題が発生しました.

この問題を解決するため, 最初に C#を含めたどの言語/環境からでも利用できる MLflow ラッパー API を先に開発しました. その開発経緯と詳細はMLflow のラッパー API を作ってみた記事を参照ください.

本連載では, その API を活用し, 拡張性と保守性を重視して設計/実装した C#製の TSP ソルバ実験フレームワークについて解説します.

本記事「プロジェクトの全体像と設計原則」は連載の第 1 回として, その中核となる設計思想, プロジェクト間の依存関係, そして各プロジェクトのディレクトリ構成と言った, 全体の青写真について解説します

連載の構成
本連載「TSP ソルバ実装記」は全 4 回を通して, フレームワークの全てを公開していきます.

ぜひ最後までお付き合いください.

目次

設計思想

本プロジェクトの設計思想は, 長期的な保守性と柔軟な拡張性を確保するために, 明確なルールに基づいています. 特に「TSP ソルバ」という計算ロジックと, 「実験管理」という外部連携ロジックを分離することが重要でした.

レイヤードアーキテクチャの適用

プロジェクト全体を, 責務に応じて垂直に階層化するレイヤードアーキテクチャを採用しました.
これにより, 各層が依存する方向を制御し, コードの変更が他の層に予期せぬ影響を与えることを防いでいます.

レイヤ名 責務の概要
アプリケーション層
(Application/Processor)
実験のワークフロー全体を制御し, ビジネスロジックを実行する.
ビジネスロジック層
(Solver)
TSP アルゴリズムを含む, 純粋な問題解決ロジック.
インフラストラクチャ層
(ExternalInterfaces)
外部 API(MLflow)やシステム監視など, 外部との接続を担う具体的な実装.
共有/ドメイン層
(Common)
レイヤ間で共有されるデータ構造, 設定, インターフェースの定義.

各プロジェクトの役割

レイヤードアーキテクチャに基づき, C#の各プロジェクト(パッケージ)に役割を与えました.

プロジェクト名 レイヤ プロジェクトテンプレート 説明
Application アプリケーション層 console Console で走らせるためのエントリープロジェクト
Processor アプリケーション層 classlib 実験の実行や記録等, 実際の処理
Solver ビジネスロジック層 classlib ソルバの詳細な実装
ExternalInterfaces インフラストラクチャ層 classlib WebAPI とのクライアント
Common 共有/ドメイン層 classlib 設定情報や WebAPI のレコード

この明確な責務分担により, たとえば「新しいソルバを追加したい」場合はSolverプロジェクトのみを修正すればよく, 「MLflowを別のサービスに切り替えたい」場合はExternalInterfacesプロジェクトのみを修正すれば済むようになっています.

プロジェクト間の依存関係

前のセクションで定義したレイヤードアーキテクチャは, C#プロジェクト(パッケージ)間の依存関係として厳密に適用されています.
この依存関係は, 原則として「内側」に向かう一方通行であり, クリーンアーキテクチャ(Clean Architecture)の原則に準拠しています.

依存関係を制御することで, 依存性逆転の原則が成り立ちます. 上位レイヤ(例: Processor)は下位レイヤ(例: ExternalInterfaces)の具体的な実装に一切依存せず, Commonプロジェクトで定義された抽象的なインターフェースにのみ依存する状態を実現しています.

プロジェクト 依存先 依存の理由とクリーンアーキテクチャへの準拠
Common なし 最も内側のドメイン層であり, 誰も依存しない共通の定義のみを提供します.
ExternalInterfaces Common Common で定義された IMLflowClient などのインターフェースを具体的に実装するため.
Solver Common Common で定義されたデータモデル(ProblemModel など)や ISolver インターフェースを利用するため.
Processor Common
ExternalInterfaces
Solver
ライフサイクルを制御するため, 抽象化されたソルバ (ISolver) とクライアント (IMLflowClient) の両方に依存します.
Application Processor エントリポイントとして, ProcessorComponent を生成し, 実行開始を指示するため.

パッケージ図は以下の通りです.
矢印は「依存している」方向, すなわち「抽象度の低い層から高い層へ」向かいます.

package.png

この設計により, 例えばProcessorはExternalInterfacesを直接知らずに, CommonのIMLflowClient経由でMLflowとの通信を抽象的に利用でき, 柔軟な切り替えを可能にしています.

各プロジェクトのディレクトリ構成

このセクションでは, プロジェクトの論理的な責務(前述のレイヤ構造)が, C#の物理的なディレクトリ構造としてどのように具現化されているかを詳細に解説します.

Application プロジェクト

Applicationプロジェクトは, コンソールでアプリケーションを起動するためのエントリーポイントを担います.
Processorプロジェクトのコンポーネントを呼び出すだけのシンプルなプロジェクトであり, 実行ロジックは一切含まれません.

image.png

Common プロジェクト

Common プロジェクトは, すべてのレイヤから依存される最下層のドメイン層です. 全プロジェクトが共有するデータモデル, 設定ファイル構造, そしてインターフェースの定義を一元管理します.

フォルダ名 概要と設計上の意図
DataModels 全てのデータ構造を定義します. 特に CalculatorModels(純粋な計算データ)と
External(外部 API 通信データ)を分離することで, 責務の分離を徹底しています.
Interfaces アプリケーション層とインフラ層の間に立つすべての契約(ISolver, IMLflowClient など)を定義します.
依存性逆転の原則の要となります.

image.png

Common/DataModels/CalculatorModels

CalculatorModels は以下を含みます.
計算とその結果に関連するデータモデルはここに含まれると考えてください.

  • AlgorithmModels
    • アルゴリズムのモデルを表す列挙体
  • CalculationResult
    • 計算結果のデータモデル
  • ProblemModel
    • TSP に用いる問題のデータモデル
  • Coordinate
    • ProblemModel に属する, 座標のデータモデル

image.png

Common/DataModels/External

External は以下を含みます.
実験の開始/終了, パラメータ/メトリクスのロギングはここに含まれると考えてください.

  • 実験開始/終了
    • ExperimentRequest, ExperimentResponse
    • TerminateRunRequest
  • データロギング
    • LogMetricsRequest, LogParamsRequest
    • LogResponse
    • MetricPair
    • PramamPair

image.png

Common/DataModels/Reporter

Reporter は進捗状況のレポート用レコードを含みます.
この名前空間はシンプルで, ファイルが 1 つで完結します.

image.png

Common/DataModels/Settings

Settings は以下を含みます. 実験の設定や固定情報はここに含まれると考えてください.

  • Configurations
    • コンフィグ情報を定義するレコード
  • ExperimentSettings
    • JSON の "experiments" 配下の一つの実験設定に対応するレコード
  • FixedSettings
    • JSON の "fixed" セクションに対応する静的な設定レコード
  • RootSettings
    • Settings ファイルのルート構造を定義するレコード

image.png

Common/Interfaces

Interfaces は以下を含みます.
外部とのやり取りを行うクラスのインターフェースはここに含まれると考えてください.

  • MLflowInterface: MLflow ラッパー API とのやり取りを行うインターフェース
    • IMlflowClient
    • IMLflowClientFactory
  • Solver: 計算機のインターフェース
    • ISolver
  • SystemMonitorInterface: システムモニタリング用のインターフェース
    • ISystemMonitor

image.png

ExternalInterface プロジェクト

ExternalInterfacesプロジェクトは, インフラストラクチャ層の実装を担います.Common.Interfaces の定義に基づき, 外部サービスとの具体的な通信(WebAPI, システム監視)ロジックを格納します.

ExternalInterface/Mlflow

Mlflow フォルダは, Common.Interfaces.MLflowInterface 直下のクライアント, クライアントのファクトリーを実装します.
ストラテジーパターンとファクトリーパターンを採用しています.

image.png

ExternalInterface/SystemMonitor

SystemMonitor フォルダは, Common.Interfaces.SystemMonitorInterface 直下のクライアント, クライアントのファクトリーを実装します.
MLflow と同じく, ストラテジーパターンとファクトリーパターンを採用しています.

image.png

Processor プロジェクト

Processorプロジェクトは, アプリケーション層の核心であり, 実験のワークフロー全体を制御する責務を持ちます.

  • ProcessorComponent.cs: 実験のライフサイクル(設定読み込み, 初期化, 実行, ロギング)を統括する中心的なファイルです
  • ProcessorUnits/Utils: ProcessorComponent の補助的な役割を持つロジックを格納します

image.png

Solver プロジェクト

Solverプロジェクトは, ビジネスロジック層の実装を担います.TSP の計算ロジックに関する全ての要素を含みます.

フォルダ名/ファイル 役割と設計上の意図
AbstractSolver.cs すべての具体的ソルバが継承する基底クラス.共通処理と, protected なメソッドを通じて限定的な機能を公開します.
SolverFactory.cs 実行時に必要なソルバのインスタンスを生成するファクトリー.
Solvers 焼きなまし法 (SA), 遺伝的アルゴリズム (GA)など, ISolver を実装した具体的なアルゴリズムのクラスを格納します.
Helper AbstractSolver が内部的に使用する補助ロジック.Evaluator(評価)や Shuffle アルゴリズムの実装を格納します.

image.png

まとめと次回予告

本記事では, TSPソルバフレームワークの基盤となる設計の全体像を解説しました.

  • 設計思想
    • 計算ロジックと外部連携ロジックを分離するため, 厳密なレイヤードアーキテクチャを採用
  • 依存関係
    • クリーンアーキテクチャの原則に従い, 依存は常に内側(抽象)に向かう一方向である
  • ディレクトリ構成
    • 各C#プロジェクト(Common, Solver, Processorなど)が, そのレイヤの責務を果たすためにどのような役割と構造を持っているかを確認

この設計により, 拡張性」「保守性」「柔軟性」という, 長期的な開発に必要な土台を築くことができました.

しかし, 現時点ではまだ, 実際に実験を動かし, 結果をMLflowに記録するための具体的なコードは見ていません.

次回の記事「連載2: 制御とインフラストラクチャの実装」では, この設計に基づいて, フレームワークの稼働に不可欠な以下の核心部分を深く掘り下げて解説します.

  • データと契約の具現化: Commonプロジェクトで定義したインターフェース(IMLflowClientなど)の詳細
  • インフラストラクチャの実装: ExternalInterfacesプロジェクトにおけるWebAPIクライアントの実装とDI(依存性注入)の仕組み
  • 実験ワークフローの制御: Processorプロジェクトがどのように全体のライフサイクルを統括しているのか
0
0
0

Register as a new user and use Qiita more conveniently

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?