1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
use smile::{
autocxx::c_int,
ffi::{
DSL_CANT_SOLVE_EQUATION, DSL_CONFLICTING_EVIDENCE, DSL_CYCLE_DETECTED, DSL_DUPLICATED_ID,
DSL_END_OF_FILE, DSL_FIELD_NOT_FOUND, DSL_FILE_READ, DSL_FILE_WRITE, DSL_GENERAL_ERROR,
DSL_ILLEGAL_ID, DSL_INTERRUPTED, DSL_INVALID_VALUE, DSL_LEXICAL_ERROR, DSL_NO_ITEM,
DSL_NO_MORE_TOKENS, DSL_NO_USEFUL_SAMPLES, DSL_OKAY, DSL_OUT_OF_MEMORY, DSL_OUT_OF_RANGE,
DSL_SYNTAX_ERROR, DSL_UNEXPECTED_EOF, DSL_WRONG_ELEMENT_TYPE, DSL_WRONG_FILE,
DSL_WRONG_NODE_TYPE, DSL_WRONG_NUM_STATES, DSL_ZERO_POTENTIAL,
},
};
use thiserror::Error;
/// SMILE error enumeration
#[derive(Error, Debug)]
pub enum Error {
#[error("An error occurred.")]
/// SMILE documentation does not specify when this error happens.
GeneralError,
#[error("Value is out of range.")]
/// SMILE documentation does not specify when this error happens.
OutOfRange,
#[error("Node has no outcome intervals")]
/// An error occurred when a node has no outcome intervals.
///
/// Documented use is only in [smile::ffi::DSL_discDef] if the node has no outcome intervals.
NoItem,
#[error("Value is invalid.")]
/// An error occurred when a value is invalid.
///
/// Happens when „some of the discretization samples fall outside of the node's domain (defined by the lower and the upper bounds set earlier by [smile::ffi::DSL_equation::SetBounds])”
/// TODO: The SMILE documentation says that it is a „warning”. This should be checked.
///
/// Returned by [smile::ffi::DSL_network::SetTarget] when `nodeHandle` was valid, but the value of `target` is the same as the value of the current target flag of the node (i.e., when trying to set the target flag of a node that is already a target or when trying to clear the target flag of a node that is not a target).
InvalidValue,
#[error("No useful samples.")]
/// SMILE documentation does not specify when this error happens.
NoUsefulSamples,
#[error("Can't solve equation.")]
/// SMILE documentation does not specify when this error happens.
CantSolveEquation,
#[error("Cycle detected.")]
/// An error occurred when a cycle is detected.
///
/// This error occurs when calling [smile::ffi::DSL_network::AddArc] would result in a cycle
/// in the grpah.
CycleDetected,
#[error("Discretization intervals for the Damper Control Signal node are missing.")]
/// An error occurred when the discretization intervals for the Damper Control Signal node are missing.
///
/// When occured, SMILE uses two intervals dividing the node's domain into two equal halves. Two intervals are adequate in this case, as the equation for this node uses Bernoulli distribution.
/// TODO: The SMILE documentation says that it is a „warning”. This should be checked.
WrongNumStates,
#[error("Conflicting evidence.")]
/// SMILE documentation does not specify when this error happens.
ConflictingEvidence,
#[error("Invalid ID.")]
/// SMILE documentation does not specify when this error happens.
IllegalId,
#[error("Duplicated ID.")]
/// SMILE documentation does not specify when this error happens.
DuplicatedId,
#[error("Out of memory.")]
/// Returned by [smile::ffi::DSL_network::UpdateBeliefs] when the temporary data structures required to complete the inference require more memory than that available.
///
/// In such case, or if the inference takes too long, consider taking advantage of SMILE's relevance reasoning layer.
/// TODO: Move this to revelance reasoning docs:
/// Relevance reasoning runs as a pre-processing step, which can lessen the complexity of later stages of inference algorithms. Relevance reasoning takes the target node set into account, therefore, to reduce the workload you should reduce the number of nodes set as targets if possible. Note that by default all nodes are targets (this is the case when no nodes were marked as such). If your network has 1,000 nodes and you only need the probabilities of 20 nodes, by all means call DSL_network::SetTarget on these nodes.
OutOfMemory,
#[error("Zero potential.")]
/// SMILE documentation does not specify when this error happens.
ZeroPotential,
#[error("Wrong node type or operation not supported on this node type")]
/// This error indicates that operation is not supported on current node type.
WrongNodeType,
#[error("Wrong element type.")]
/// SMILE documentation does not specify when this error happens.
WrongElementType,
#[error("Interrupted.")]
/// Some methods can receive `progress` argument that allows them to be interrupted.
/// This error happens when such iterrupt occurs.
/// For more information consult [smile::ffi::DSL_progress] docs.
///
/// TODO: There should be an abstraction over this error type:
/// ```no_run
/// enum ProgressResult<T> {
/// Ok(T),
/// Interrupted(String)
/// }
///
/// /// Trait for checking progress of learning alghorithms [smile::ffi::DSL_progress].
/// trait Progress {
/// /// Check if the progress should be interrupted.
/// ///
/// /// The `progress` argument is a number from 0 to 100.
/// /// The `message` argument is a message returned by a learning algorithm on every tick.
/// ///
/// /// This function is called on every iteration of learning algorithms and is equivalent to
/// /// [smile::ffi::DSL_progress::Tick].
/// fn should_interrupt(&mut self, progress: u8, message: &str) -> bool {
/// true
/// }
/// }
/// ```
Interrupted,
#[error("File read error.")]
/// SMILE documentation does not specify when this error happens.
FileRead,
#[error("File read write.")]
/// SMILE documentation does not specify when this error happens.
FileWrite,
#[error("Unexpected EOF.")]
/// SMILE documentation does not specify when this error happens.
EndOfFile,
#[error("Wrong file error.")]
/// SMILE documentation does not specify when this error happens.
WrongFile,
#[error("No more tokens left.")]
/// SMILE documentation does not specify when this error happens.
NoMoreTokens,
#[error("Lexical error.")]
/// SMILE documentation does not specify when this error happens.
LexicalError,
#[error("Syntax error.")]
/// SMILE documentation does not specify when this error happens.
SyntaxError,
#[error("Unexpected EOF.")]
/// SMILE documentation does not specify when this error happens.
UnexpectedEof,
#[error("Field not found.")]
/// SMILE documentation does not specify when this error happens.
FieldNotFound,
#[error("Io error: {0}")]
/// An error occurred while performing an I/O operation.
IoError(#[from] std::io::Error),
#[error("Failed to convert C string to Rust string.")]
/// An error occurred while converting a string to a C string.
NulError(#[from] std::ffi::NulError),
}
impl Error {
fn from_error_code(error_code: c_int) -> Self {
match error_code.0 {
code if code == DSL_GENERAL_ERROR => Error::GeneralError,
code if code == DSL_OUT_OF_RANGE => Error::OutOfRange,
code if code == DSL_NO_ITEM => Error::NoItem,
code if code == DSL_INVALID_VALUE => Error::InvalidValue,
code if code == DSL_NO_USEFUL_SAMPLES => Error::NoUsefulSamples,
code if code == DSL_CANT_SOLVE_EQUATION => Error::CantSolveEquation,
code if code == DSL_CYCLE_DETECTED => Error::CycleDetected,
code if code == DSL_WRONG_NUM_STATES => Error::WrongNumStates,
code if code == DSL_CONFLICTING_EVIDENCE => Error::ConflictingEvidence,
code if code == DSL_ILLEGAL_ID => Error::IllegalId,
code if code == DSL_DUPLICATED_ID => Error::DuplicatedId,
code if code == DSL_OUT_OF_MEMORY => Error::OutOfMemory,
code if code == DSL_ZERO_POTENTIAL => Error::ZeroPotential,
code if code == DSL_WRONG_NODE_TYPE => Error::WrongNodeType,
code if code == DSL_WRONG_ELEMENT_TYPE => Error::WrongElementType,
code if code == DSL_INTERRUPTED => Error::Interrupted,
code if code == DSL_FILE_READ => Error::FileRead,
code if code == DSL_FILE_WRITE => Error::FileWrite,
code if code == DSL_END_OF_FILE => Error::EndOfFile,
code if code == DSL_WRONG_FILE => Error::WrongFile,
code if code == DSL_NO_MORE_TOKENS => Error::NoMoreTokens,
code if code == DSL_LEXICAL_ERROR => Error::LexicalError,
code if code == DSL_SYNTAX_ERROR => Error::SyntaxError,
code if code == DSL_UNEXPECTED_EOF => Error::UnexpectedEof,
code if code == DSL_FIELD_NOT_FOUND => Error::FieldNotFound,
_ => panic!("Unknown error code: {:?}", error_code),
}
}
}
/// Result type for SMILE operations.
pub type Result<T> = std::result::Result<T, Error>;
/// Extension trait for converting a [smile::autocxx::c_int] into [Result].
pub trait CIntExt {
/// Convert a [smile::autocxx::c_int] into [Result].
fn into_result(self) -> Result<()>;
}
impl CIntExt for c_int {
fn into_result(self) -> Result<()> {
if self.0 as u32 == DSL_OKAY {
Ok(())
} else {
Err(Error::from_error_code(self))
}
}
}