Skip to main content

otspot_core/
error.rs

1/// MPSファイルのパース中に発生するエラー
2#[non_exhaustive]
3#[derive(Debug)]
4pub enum MpsError {
5    /// ファイル読み込み時のI/Oエラー
6    IoError(std::io::Error),
7    /// 指定行のパースエラー(行番号とメッセージを含む)
8    ParseError { line: usize, message: String },
9    /// 必須セクションが欠落している
10    MissingSection(String),
11    /// 同じセクションが複数回出現した
12    DuplicateSection(String),
13    /// 無効な行タイプ文字が指定された
14    InvalidRowType(char),
15    /// 無効な上下限タイプ文字列が指定された
16    InvalidBoundType(String),
17    /// 未定義の行名または列名が参照された
18    UndefinedReference { kind: String, name: String },
19    /// INTORG マーカーが対応する INTEND で閉じられないまま COLUMNS を抜けた。
20    /// 残り全列が無警告で整数化されるのを防ぐため明示エラーとする。
21    UnclosedIntegerMarker,
22}
23
24impl std::fmt::Display for MpsError {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        match self {
27            MpsError::IoError(e) => write!(f, "I/O error: {}", e),
28            MpsError::ParseError { line, message } => {
29                write!(f, "Parse error at line {}: {}", line, message)
30            }
31            MpsError::MissingSection(s) => write!(f, "Missing required section: {}", s),
32            MpsError::DuplicateSection(s) => write!(f, "Duplicate section: {}", s),
33            MpsError::InvalidRowType(c) => write!(f, "Invalid row type: {}", c),
34            MpsError::InvalidBoundType(s) => write!(f, "Invalid bound type: {}", s),
35            MpsError::UndefinedReference { kind, name } => {
36                write!(f, "Undefined {} reference: {}", kind, name)
37            }
38            MpsError::UnclosedIntegerMarker => {
39                write!(
40                    f,
41                    "INTORG marker not closed by a matching INTEND in COLUMNS"
42                )
43            }
44        }
45    }
46}
47
48impl std::error::Error for MpsError {}
49
50impl From<std::io::Error> for MpsError {
51    fn from(err: std::io::Error) -> Self {
52        MpsError::IoError(err)
53    }
54}
55
56/// ソルバー全体の統一エラー型
57///
58/// 入力検証・数値計算など、ソルバー操作中に発生しうるエラーを統一的に表現する。
59///
60/// 注意: Infeasible/Unbounded/MaxIterations は数学的結果であり、
61/// エラーではないため [`SolveStatus`](crate::problem::SolveStatus) で表現する。
62#[non_exhaustive]
63#[derive(Debug)]
64pub enum SolverError {
65    /// 次元不一致(配列長・行列サイズの不整合)
66    ///
67    /// 例: `c.len() != a.ncols`, トリプレット配列の長さ不一致
68    DimensionMismatch {
69        /// どのフィールド/配列が不一致か(例: "c", "b", "triplet_arrays")
70        field: &'static str,
71        /// 期待されるサイズ
72        expected: usize,
73        /// 実際のサイズ
74        got: usize,
75    },
76
77    /// インデックスが有効範囲外
78    ///
79    /// 例: 行列の列インデックス、基底列インデックス
80    IndexOutOfBounds {
81        /// どのインデックスか(例: "column", "row", "basis_column")
82        context: &'static str,
83        /// 範囲外のインデックス値
84        index: usize,
85        /// 上限値(0..bound が有効範囲)
86        bound: usize,
87    },
88
89    /// 基底行列が特異(LU分解で数値的に特異なピボットを検出)
90    SingularBasis {
91        /// 特異性が検出されたガウス消去のステップ番号
92        step: usize,
93    },
94
95    /// 空の入力が渡された
96    EmptyInput {
97        /// どの入力が空か(例: "basis")
98        context: &'static str,
99    },
100
101    /// Deadline を超過した(タイムアウト)
102    DeadlineExceeded,
103
104    /// 非有限係数(NaN または ±∞)が入力された
105    ///
106    /// 例: `c[i]` が NaN、`b[j]` が Inf、行列要素が NaN
107    NonFiniteCoefficient {
108        /// どのフィールドか("c", "b", "A" など)
109        field: &'static str,
110        /// 最初に非有限値が検出されたインデックス
111        index: usize,
112    },
113
114    /// 変数境界が無効(NaN または lb > ub)
115    InvalidBounds {
116        /// 無効な境界を持つ変数のインデックス
117        index: usize,
118        /// 下限値
119        lb: f64,
120        /// 上限値
121        ub: f64,
122    },
123}
124
125impl std::fmt::Display for SolverError {
126    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127        match self {
128            SolverError::DimensionMismatch {
129                field,
130                expected,
131                got,
132            } => {
133                write!(
134                    f,
135                    "Dimension mismatch: {} expected {} but got {}",
136                    field, expected, got
137                )
138            }
139            SolverError::IndexOutOfBounds {
140                context,
141                index,
142                bound,
143            } => {
144                write!(
145                    f,
146                    "{} index {} out of bounds (size={})",
147                    context, index, bound
148                )
149            }
150            SolverError::SingularBasis { step } => {
151                write!(f, "Singular matrix detected at step {}", step)
152            }
153            SolverError::EmptyInput { context } => {
154                write!(f, "Empty input: {}", context)
155            }
156            SolverError::DeadlineExceeded => {
157                write!(f, "Deadline exceeded during computation")
158            }
159            SolverError::NonFiniteCoefficient { field, index } => {
160                write!(f, "Non-finite coefficient in {}: index {}", field, index)
161            }
162            SolverError::InvalidBounds { index, lb, ub } => {
163                write!(
164                    f,
165                    "Invalid bounds at index {}: lb={} > ub={} or NaN",
166                    index, lb, ub
167                )
168            }
169        }
170    }
171}
172
173impl std::error::Error for SolverError {}