I have a struct, Node, of which one of the fields (node_type) is an enum LayerType (which is either LayerType::ReLU, LayerType::Sigmoid, LayerType::Tanh or LayerType::Linear). There is a function on each Node: non_linear_function(&self, x: f64) -> f64 which should select the correct function to use based on the variant of the LayerType stored in node_type. The code I currently have is:
pub fn non_linear_function(&self, x: f64) -> f64 {
match self.node_type {
LayerType::ReLU => todo!(),
LayerType::Sigmoid => todo!(),
LayerType::Tanh => todo!(),
LayerType::Linear => todo!(),
}
}
However, I am not sure whether or not this is efficient. Will it perform the check every time the function is called? If so, is there a way to prevent this happening?
All of the relevant code can be found below.
struct Layer {
layer_type: LayerType,
layer_width: usize,
node_vec: Vec<Node>
}
#[derive(Debug)]
struct Node {
prev_layer_size: usize,
node_type: LayerType,
weights: Vec<f64>,
bias: f64
}
impl Node {
pub fn new(prev_layer_size: usize, layer_pointer: &Layer) -> Node {
// Instantiates a new node based on the size of the previous layer
let mut rng = rand::thread_rng();
return Node {
prev_layer_size,
node_type: (*layer_pointer).layer_type,
weights: (0..prev_layer_size).map(|x| rng.gen()).collect(),
bias: rng.gen()
};
}
pub fn eval(&self, prev_activations: &Vec<f64>) -> f64 {
// Evaluate the activation of the node
return self.non_linear_function(
self.weights.iter().zip(prev_activations.iter()).map(
|(&x, &y)| x*y
).sum::<f64>() + self.bias
)
}
pub fn update(&mut self, target: f64) -> Vec<f64> {
// Changes weights and biases based on target,
// which indicates how much a given node should change and
// returns a vector for how it thinks the nodes in the layer
// behind it should change
todo!()
}
// The code in question is here
pub fn non_linear_function(&self, x: f64) -> f64 {
match self.node_type {
LayerType::ReLU => todo!(),
LayerType::Sigmoid => todo!(),
LayerType::Tanh => todo!(),
LayerType::Linear => todo!(),
}
}
}
#[derive(Debug, Clone, Copy)]
enum LayerType {
ReLU,
Sigmoid,
Tanh,
Linear
}
Is there an idiomatic way to implement this without match? Or is the match already optimised away by the compiler?
Additionally, I can see that it is also an option to implement several different versions of Node, for each different LayerType, however this does not sound like a good idea.
I also understand that this is probably a miniscule time save, but I am writing this code to learn how to write fast Rust code.