-
Notifications
You must be signed in to change notification settings - Fork 12
test(e2e): Add end-to-end tests simulating neural-network workloads on a Spider cluster. #385
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
56 commits
Select commit
Hold shift + click to select a range
50cc292
Done implementation.
LinZhihao-723 0d73a2f
Merge branch 'main' into test-driver-impl
LinZhihao-723 26d6456
toml lint.
LinZhihao-723 f9bffb7
Merge branch 'test-driver-impl' of https://github.com/LinZhihao-723/s…
LinZhihao-723 efb2d57
Merge branch 'main' into test-driver-impl
sitaowang1998 c233c24
Merge branch 'main' into test-driver-impl
LinZhihao-723 e1aad84
Linter fix.
LinZhihao-723 560c7a1
Merge branch 'test-driver-impl' of github.com:LinZhihao-723/spider in…
sitaowang1998 3567013
Update comment.
LinZhihao-723 33edc61
Merge branch 'test-driver-impl' of github.com:LinZhihao-723/spider in…
sitaowang1998 3e37ef0
Merge branch 'main' into nn-test-driver
sitaowang1998 4eaf570
Add nn network
sitaowang1998 b3312ee
Reorder
sitaowang1998 a8d5cee
Add nn e2e test
sitaowang1998 d386d25
Remove e2e from unit test task
sitaowang1998 37db769
Fix style
sitaowang1998 b9854fe
Merge branch 'main' into nn-test-driver
sitaowang1998 4873ff1
Merge branch 'main' into nn-test-driver
sitaowang1998 22fa3f0
Merge branch 'main' into nn-test-driver
sitaowang1998 e083d12
Merge branch 'main' into nn-test-driver
sitaowang1998 63fdf44
Merge branch 'main' into nn-test-driver
sitaowang1998 4aac6e5
Merge branch 'main' into nn-test-driver
sitaowang1998 93f912a
Merge branch 'main' into nn-test-driver
sitaowang1998 b558c2b
Merge branch 'main' into nn-test-driver
sitaowang1998 9059e59
Merge branch 'main' into nn-test-driver
sitaowang1998 5e98861
Merge branch 'main' into nn-test-driver
sitaowang1998 48c3a7f
Merge branch 'main' into nn-test-driver
sitaowang1998 dfbdd18
Merge branch 'main' into nn-test-driver
sitaowang1998 d3e0d9d
Merge branch 'main' into nn-test-driver
sitaowang1998 5628462
Merge branch 'main' into nn-test-driver
sitaowang1998 76771d2
Merge branch 'main' into nn-test-driver
sitaowang1998 f1b286d
Merge branch 'main' into nn-test-driver
sitaowang1998 0a24773
Merge branch 'main' into nn-test-driver
sitaowang1998 785dfd0
Merge branch 'main' into nn-test-driver
sitaowang1998 2adf570
Merge branch 'main' into nn-test-driver
sitaowang1998 fada347
Merge branch 'main' into nn-test-driver
sitaowang1998 f8b2295
Merge branch 'main' into nn-test-driver
sitaowang1998 45d13b8
Merge branch 'main' into nn-test-driver
sitaowang1998 36de57d
Merge branch 'main' into nn-test-driver
sitaowang1998 d12a733
Merge branch 'main' into nn-test-driver
sitaowang1998 354aa03
Merge branch 'main' into nn-test-driver
sitaowang1998 1a9c140
Merge branch 'main' into nn-test-driver
sitaowang1998 c0a012a
Merge branch 'main' into nn-test-driver
sitaowang1998 c7a4a87
Merge branch 'main' into nn-test-driver
sitaowang1998 5f37b14
Apply suggestions from code review
sitaowang1998 c0b4ead
Remove lifetime
sitaowang1998 2b1d1de
Rename
sitaowang1998 9b4a314
Move private type definition down
sitaowang1998 31e3aae
Remove env test
sitaowang1998 57dabd2
Run multiple jobs
sitaowang1998 0fa8c54
Move type definition
sitaowang1998 8bde76a
Merge branch 'main' into nn-test-driver
sitaowang1998 5645494
Fix where
sitaowang1998 374aa18
build(huntsman): Declare the e2e `nn` integration test as an opt-in C…
LinZhihao-723 6f446d4
Merge branch 'main' into nn-test-driver
LinZhihao-723 e57aefa
Move consts
sitaowang1998 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,10 @@ | ||
| //! End-to-end integration-test harness for the huntsman suites. | ||
|
|
||
| pub mod nn; | ||
| pub mod payload_serde; | ||
| pub mod test_driver; | ||
| mod types; | ||
|
|
||
| pub use payload_serde::*; | ||
| pub use test_driver::SpiderTestDriver; | ||
| pub use types::*; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| //! Self-contained neural-network model for the end-to-end test. | ||
| //! | ||
| //! [`NeuralNetwork`] builds a layered `neuron::dense_*` task graph and reproduces it in-process via | ||
| //! [`NeuralNetwork::simulate`]. | ||
|
|
||
| mod network; | ||
| mod neuron; | ||
| mod wiring; | ||
|
|
||
| pub use network::NeuralNetwork; | ||
| pub use neuron::Neuron; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,179 @@ | ||
| //! The neural-network model: a layered topology of `neuron::dense_*` neurons whose Spider | ||
| //! [`TaskGraph`] and in-process simulation describe the same DAG. | ||
|
|
||
| use huntsman_nn_core::NUM_INPUTS; | ||
| use rand::SeedableRng; | ||
| use rand::rngs::StdRng; | ||
| use spider_core::task::DataTypeDescriptor; | ||
| use spider_core::task::TaskDescriptor; | ||
| use spider_core::task::TaskGraph; | ||
| use spider_core::task::TaskIndex; | ||
| use spider_core::task::TaskInputOutputIndex; | ||
| use spider_core::task::TdlContext; | ||
| use spider_core::task::ValueTypeDescriptor; | ||
|
|
||
| use crate::nn::Neuron; | ||
| use crate::nn::wiring; | ||
|
|
||
| /// A randomly-wired, layered neural network of `neuron::dense_*` neurons. | ||
| pub struct NeuralNetwork { | ||
| /// The layers in layer order. | ||
| layers: Vec<Layer>, | ||
| } | ||
|
|
||
| impl NeuralNetwork { | ||
| /// Factory function. | ||
| /// | ||
| /// Validates `layer_specs` via [`wiring::validate`] and generates the inner-layer fan-in | ||
| /// wiring deterministically from `seed`. | ||
| /// | ||
| /// # Returns | ||
| /// | ||
| /// The newly created [`NeuralNetwork`] on success. | ||
| /// | ||
| /// # Errors | ||
| /// | ||
| /// Returns an error if: | ||
| /// | ||
| /// * Forwards [`wiring::validate`]'s return values on failure. | ||
| pub fn new(layer_specs: Vec<(usize, Neuron)>, seed: u64) -> anyhow::Result<Self> { | ||
| let sizes: Vec<usize> = layer_specs.iter().map(|(size, _)| *size).collect(); | ||
| wiring::validate(&sizes)?; | ||
| let mut rng = StdRng::seed_from_u64(seed); | ||
| let fan_ins = wiring::build_wiring(&sizes, &mut rng); | ||
| let layers = layer_specs | ||
| .into_iter() | ||
| .zip(fan_ins) | ||
| .map(|((neuron_count, activation), fan_in)| Layer { | ||
| neuron_count, | ||
| activation, | ||
| fan_in, | ||
| }) | ||
| .collect(); | ||
| Ok(Self { layers }) | ||
| } | ||
|
|
||
| /// # Returns | ||
| /// | ||
| /// The number of graph inputs. | ||
| #[must_use] | ||
| pub fn num_graph_inputs(&self) -> usize { | ||
| self.layers[0].neuron_count * NUM_INPUTS | ||
| } | ||
|
|
||
| /// Builds the Spider [`TaskGraph`] for this network. | ||
| /// | ||
| /// # Returns | ||
| /// | ||
| /// The [`TaskGraph`] for this network on success. | ||
| /// | ||
| /// # Errors | ||
| /// | ||
| /// Returns an error if: | ||
| /// | ||
| /// * Forwards [`TaskGraph::new`]'s return values on failure. | ||
| /// * Forwards [`TaskGraph::insert_task`]'s return values on failure. | ||
| pub fn to_task_graph(&self) -> anyhow::Result<TaskGraph> { | ||
| let float64 = DataTypeDescriptor::Value(ValueTypeDescriptor::float64()); | ||
| let mut graph = TaskGraph::new(None, None)?; | ||
| let first_layer = &self.layers[0]; | ||
| let mut prev_layer: Vec<TaskIndex> = Vec::with_capacity(first_layer.neuron_count); | ||
|
|
||
| for _ in 0..first_layer.neuron_count { | ||
| let task_idx = graph.insert_task(TaskDescriptor { | ||
| tdl_context: TdlContext { | ||
| package: PACKAGE.to_owned(), | ||
| task_func: first_layer.activation.task_name().to_owned(), | ||
| }, | ||
| execution_policy: None, | ||
| inputs: vec![float64.clone(); NUM_INPUTS], | ||
| outputs: vec![float64.clone()], | ||
| input_sources: None, | ||
| })?; | ||
| prev_layer.push(task_idx); | ||
| } | ||
|
|
||
| for layer in self.layers.iter().skip(1) { | ||
| let mut curr_layer = Vec::with_capacity(layer.neuron_count); | ||
| for j in 0..layer.neuron_count { | ||
| let input_sources: Vec<TaskInputOutputIndex> = layer.fan_in[j] | ||
| .iter() | ||
| .map(|&src| TaskInputOutputIndex { | ||
| task_idx: prev_layer[src], | ||
| position: 0, | ||
| }) | ||
| .collect(); | ||
| let task_idx = graph.insert_task(TaskDescriptor { | ||
| tdl_context: TdlContext { | ||
| package: PACKAGE.to_owned(), | ||
| task_func: layer.activation.task_name().to_owned(), | ||
| }, | ||
| execution_policy: None, | ||
| inputs: vec![float64.clone(); NUM_INPUTS], | ||
| outputs: vec![float64.clone()], | ||
| input_sources: Some(input_sources), | ||
| })?; | ||
| curr_layer.push(task_idx); | ||
| } | ||
| prev_layer = curr_layer; | ||
| } | ||
|
|
||
| Ok(graph) | ||
| } | ||
|
|
||
| /// Computes the network's outputs from graph inputs. | ||
| /// | ||
| /// # Returns | ||
| /// | ||
| /// The network's outputs on success. | ||
| /// | ||
| /// # Errors | ||
| /// | ||
| /// Returns an error if: | ||
| /// | ||
| /// * [`anyhow::Error`] if `inputs` length is not [`Self::num_graph_inputs`]. | ||
| pub fn simulate(&self, inputs: &[f64]) -> anyhow::Result<Vec<f64>> { | ||
| let expected = self.num_graph_inputs(); | ||
| anyhow::ensure!( | ||
| inputs.len() == expected, | ||
| "expected {expected} graph inputs, got {}", | ||
| inputs.len(), | ||
| ); | ||
|
|
||
| let first_layer = &self.layers[0]; | ||
| let mut layer_outputs: Vec<f64> = (0..first_layer.neuron_count) | ||
| .map(|i| { | ||
| let start = i * NUM_INPUTS; | ||
| let mut neuron_inputs = [0.0_f64; NUM_INPUTS]; | ||
| neuron_inputs.copy_from_slice(&inputs[start..start + NUM_INPUTS]); | ||
| first_layer.activation.evaluate_func()(&neuron_inputs) | ||
| }) | ||
| .collect(); | ||
|
|
||
| for layer in self.layers.iter().skip(1) { | ||
| layer_outputs = (0..layer.neuron_count) | ||
| .map(|i| { | ||
| let neuron_inputs: [f64; NUM_INPUTS] = | ||
| std::array::from_fn(|j| layer_outputs[layer.fan_in[i][j]]); | ||
| layer.activation.evaluate_func()(&neuron_inputs) | ||
| }) | ||
| .collect(); | ||
| } | ||
|
|
||
| Ok(layer_outputs) | ||
| } | ||
| } | ||
|
sitaowang1998 marked this conversation as resolved.
|
||
|
|
||
| /// Name of the TDL package supplying the `neuron::dense_*` tasks. | ||
| const PACKAGE: &str = "nn"; | ||
|
|
||
| /// One layer of the network: its neuron count, activation, and per-neuron fan-in. | ||
| struct Layer { | ||
| /// Number of neurons in this layer. | ||
| neuron_count: usize, | ||
| /// Activation applied by every neuron in this layer. | ||
| activation: Neuron, | ||
| /// Per-neuron fan-in, listing previous-layer output indices feeding each neuron. | ||
| /// Empty for layer 0, which reads graph inputs directly. | ||
| fan_in: Vec<Vec<usize>>, | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| //! Activation functions for the end-to-end neural-network test workload. | ||
| //! | ||
| //! Each [`Neuron`] pairs a Spider `neuron::dense_*` task with the in-process | ||
| //! `huntsman_nn_core::dense_*` evaluation function so the task graph and [`super::NeuralNetwork`]'s | ||
| //! simulation share one source of truth. | ||
|
|
||
| use huntsman_nn_core::NUM_INPUTS; | ||
|
|
||
| /// A dense-layer neuron activation. | ||
| #[derive(Clone, Copy, Debug, Eq, PartialEq)] | ||
| pub enum Neuron { | ||
| /// Rectified-linear activation. | ||
| Relu, | ||
|
|
||
| /// Logistic-sigmoid activation. | ||
| Sigmoid, | ||
|
|
||
| /// Identity (no-op) activation. | ||
| Identity, | ||
| } | ||
|
|
||
| impl Neuron { | ||
| /// # Returns | ||
| /// | ||
| /// The `neuron::dense_*` task function name that evaluates this activation. | ||
| #[must_use] | ||
| pub const fn task_name(self) -> &'static str { | ||
| match self { | ||
| Self::Relu => "neuron::dense_relu", | ||
| Self::Sigmoid => "neuron::dense_sigmoid", | ||
| Self::Identity => "neuron::dense_identity", | ||
| } | ||
| } | ||
|
|
||
| /// # Returns | ||
| /// | ||
| /// The `huntsman_nn_core::dense_*` function that evaluates this activation. | ||
| #[must_use] | ||
| pub fn evaluate_func(self) -> fn(&[f64; NUM_INPUTS]) -> f64 { | ||
| match self { | ||
| Self::Relu => huntsman_nn_core::dense_relu, | ||
| Self::Sigmoid => huntsman_nn_core::dense_sigmoid, | ||
| Self::Identity => huntsman_nn_core::dense_identity, | ||
| } | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, this will disable the test by default. There's no need to explicitly disable it in the current task.
To run the nn test case: