Skip to main content

yscv_model/
error.rs

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/// Errors returned by model-layer assembly, checkpoints, and training helpers.
9#[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}