1use thiserror::Error;
2use yscv_autograd::AutogradError;
3use yscv_imgproc::ImgProcError;
4use yscv_kernels::KernelError;
5use yscv_optim::OptimError;
6use yscv_tensor::TensorError;
7
8#[derive(Debug, Clone, PartialEq, Error)]
10pub enum ModelError {
11 #[error("invalid shape for {parameter}: expected {expected:?}, got {got:?}")]
12 InvalidParameterShape {
13 parameter: &'static str,
14 expected: Vec<usize>,
15 got: Vec<usize>,
16 },
17 #[error(
18 "invalid linear input shape: expected rank-2 with last dim {expected_features}, got {got:?}"
19 )]
20 InvalidInputShape {
21 expected_features: usize,
22 got: Vec<usize>,
23 },
24 #[error("invalid leaky-relu negative slope: {negative_slope}; expected finite value >= 0")]
25 InvalidLeakyReluSlope { negative_slope: f32 },
26 #[error("invalid dropout rate: {rate}; expected finite value in [0, 1)")]
27 InvalidDropoutRate { rate: f32 },
28 #[error("prediction/target shape mismatch: prediction={prediction:?}, target={target:?}")]
29 PredictionTargetShapeMismatch {
30 prediction: Vec<usize>,
31 target: Vec<usize>,
32 },
33 #[error("cannot compute mean loss for empty tensor")]
34 EmptyLossTensor,
35 #[error("invalid huber delta: {delta}; expected finite value > 0")]
36 InvalidHuberDelta { delta: f32 },
37 #[error("invalid hinge margin: {margin}; expected finite value > 0")]
38 InvalidHingeMargin { margin: f32 },
39 #[error(
40 "dataset tensors must have rank >= 1, got inputs_rank={inputs_rank}, targets_rank={targets_rank}"
41 )]
42 InvalidDatasetRank {
43 inputs_rank: usize,
44 targets_rank: usize,
45 },
46 #[error("dataset sample mismatch: inputs={inputs:?}, targets={targets:?}")]
47 DatasetShapeMismatch {
48 inputs: Vec<usize>,
49 targets: Vec<usize>,
50 },
51 #[error("dataset is empty")]
52 EmptyDataset,
53 #[error("invalid batch size: {batch_size}; expected batch_size > 0")]
54 InvalidBatchSize { batch_size: usize },
55 #[error("invalid epoch count: {epochs}; expected epochs > 0")]
56 InvalidEpochCount { epochs: usize },
57 #[error(
58 "invalid split ratios: train_ratio={train_ratio}, validation_ratio={validation_ratio}; expected finite values in [0, 1] with train+validation <= 1"
59 )]
60 InvalidSplitRatios {
61 train_ratio: f32,
62 validation_ratio: f32,
63 },
64 #[error(
65 "invalid split counts: train_count={train_count}, validation_count={validation_count}, dataset_len={dataset_len}"
66 )]
67 InvalidSplitCounts {
68 train_count: usize,
69 validation_count: usize,
70 dataset_len: usize,
71 },
72 #[error("invalid sampling weights length: expected {expected} weights, got {got}")]
73 InvalidSamplingWeightsLength { expected: usize, got: usize },
74 #[error("invalid sampling weight at index {index}: {value}; expected finite value >= 0")]
75 InvalidSamplingWeight { index: usize, value: f32 },
76 #[error("invalid sampling distribution: at least one weight must be > 0")]
77 InvalidSamplingDistribution,
78 #[error(
79 "invalid class-balanced sampling target shape: expected scalar class labels ([N,1]) or one-hot labels ([N,C]), got {got:?}"
80 )]
81 InvalidClassSamplingTargetShape { got: Vec<usize> },
82 #[error("invalid class-balanced sampling target at sample {index}: {value}; {reason}")]
83 InvalidClassSamplingTargetValue {
84 index: usize,
85 value: f32,
86 reason: &'static str,
87 },
88 #[error(
89 "invalid augmentation probability for {operation}: {value}; expected finite value in [0, 1]"
90 )]
91 InvalidAugmentationProbability { operation: &'static str, value: f32 },
92 #[error("invalid augmentation argument for {operation}: {message}")]
93 InvalidAugmentationArgument {
94 operation: &'static str,
95 message: String,
96 },
97 #[error("invalid augmentation input shape: expected rank-4 NHWC, got {got:?}")]
98 InvalidAugmentationInputShape { got: Vec<usize> },
99 #[error("invalid mixup argument for {field}: {value}; {message}")]
100 InvalidMixupArgument {
101 field: &'static str,
102 value: f32,
103 message: String,
104 },
105 #[error("invalid cutmix argument for {field}: {value}; {message}")]
106 InvalidCutMixArgument {
107 field: &'static str,
108 value: f32,
109 message: String,
110 },
111 #[error("invalid cutmix input shape: expected rank-4 NHWC, got {got:?}")]
112 InvalidCutMixInputShape { got: Vec<usize> },
113 #[error("invalid dataset-adapter shape for {field}: {shape:?}; {message}")]
114 InvalidDatasetAdapterShape {
115 field: &'static str,
116 shape: Vec<usize>,
117 message: String,
118 },
119 #[error("invalid image-folder extension configuration for {extension}: {message}")]
120 InvalidImageFolderExtension { extension: String, message: String },
121 #[error("invalid CSV delimiter: {delimiter:?}; expected a non-control character")]
122 InvalidCsvDelimiter { delimiter: char },
123 #[error("invalid CSV dataset column count at line {line}: expected {expected}, got {got}")]
124 InvalidDatasetRecordColumns {
125 line: usize,
126 expected: usize,
127 got: usize,
128 },
129 #[error("invalid dataset record path at line {line}: {message}")]
130 InvalidDatasetRecordPath { line: usize, message: String },
131 #[error("failed to parse CSV dataset value at line {line}, column {column}: {message}")]
132 DatasetCsvParse {
133 line: usize,
134 column: usize,
135 message: String,
136 },
137 #[error(
138 "invalid JSONL dataset record length at line {line} for {field}: expected {expected}, got {got}"
139 )]
140 InvalidDatasetRecordLength {
141 line: usize,
142 field: &'static str,
143 expected: usize,
144 got: usize,
145 },
146 #[error("invalid JSONL dataset record value at line {line} for {field}[{index}]: {reason}")]
147 InvalidDatasetRecordValue {
148 line: usize,
149 field: &'static str,
150 index: usize,
151 reason: &'static str,
152 },
153 #[error("failed to parse JSONL dataset record at line {line}: {message}")]
154 DatasetJsonlParse { line: usize, message: String },
155 #[error("failed to read dataset file {path}: {message}")]
156 DatasetLoadIo { path: String, message: String },
157 #[error("failed to decode dataset image {path}: {message}")]
158 DatasetImageDecode { path: String, message: String },
159 #[error("invalid conv2d stride: stride_h={stride_h}, stride_w={stride_w}; both must be > 0")]
160 InvalidConv2dStride { stride_h: usize, stride_w: usize },
161 #[error("invalid batch-norm epsilon: {epsilon}; expected finite value > 0")]
162 InvalidBatchNormEpsilon { epsilon: f32 },
163 #[error("invalid pool kernel: kernel_h={kernel_h}, kernel_w={kernel_w}; both must be > 0")]
164 InvalidPoolKernel { kernel_h: usize, kernel_w: usize },
165 #[error("invalid pool stride: stride_h={stride_h}, stride_w={stride_w}; both must be > 0")]
166 InvalidPoolStride { stride_h: usize, stride_w: usize },
167 #[error("invalid flatten input shape: expected rank >= 2, got {got:?}")]
168 InvalidFlattenShape { got: Vec<usize> },
169 #[error("layer is inference-only and cannot be used in autograd graph forward pass")]
170 InferenceOnlyLayer,
171 #[error("layer {layer} parameters not registered in graph; call register_params first")]
172 ParamsNotRegistered { layer: &'static str },
173 #[error("layer is graph-only and cannot be used in direct tensor inference forward pass")]
174 GraphOnlyLayer,
175 #[error("checkpoint serialization error: {message}")]
176 CheckpointSerialization { message: String },
177 #[error("invalid accumulation steps: {steps}; expected steps > 0")]
178 InvalidAccumulationSteps { steps: usize },
179 #[error("ONNX export error: {0}")]
180 OnnxExport(String),
181 #[error("invalid layer index {index}: model has {count} layers")]
182 InvalidLayerIndex { index: usize, count: usize },
183 #[error("missing weight tensor: {name}")]
184 WeightNotFound { name: String },
185 #[error("safetensors parse error: {message}")]
186 SafeTensorsParse { message: String },
187 #[error("safetensors I/O error for {path}: {message}")]
188 SafeTensorsIo { path: String, message: String },
189 #[error("download failed for {url}: {reason}")]
190 DownloadFailed { url: String, reason: String },
191 #[error("transport error: {0}")]
192 TransportError(String),
193 #[error(transparent)]
194 Tensor(#[from] TensorError),
195 #[error(transparent)]
196 ImgProc(#[from] ImgProcError),
197 #[error(transparent)]
198 Kernel(#[from] KernelError),
199 #[error(transparent)]
200 Autograd(#[from] AutogradError),
201 #[error(transparent)]
202 Optim(#[from] OptimError),
203}