From 0a911c71afce9fd77cc35fcb4e03a5e89c5f0991 Mon Sep 17 00:00:00 2001 From: Steven Atkinson Date: Wed, 14 Jan 2026 17:18:40 -0800 Subject: [PATCH 01/10] [FEATURE] Implement grouped convolutions in Conv1x1 - Added support for grouped convolutions in the Conv1x1 class by introducing a groups parameter in the constructor. - Implemented validation checks to ensure in_channels and out_channels are divisible by groups. - Updated weight initialization and processing methods to handle grouped convolution logic. - Enhanced the set_weights_ and process methods to accommodate grouped weight organization and processing. - Added necessary error handling for invalid group configurations. This update enhances the flexibility of the Conv1x1 layer for various convolutional architectures. --- NAM/dsp.cpp | 118 ++++++++++++++++++++++++++++++++++++++++++++++++---- NAM/dsp.h | 3 +- 2 files changed, 112 insertions(+), 9 deletions(-) diff --git a/NAM/dsp.cpp b/NAM/dsp.cpp index 8940314..74a8df1 100644 --- a/NAM/dsp.cpp +++ b/NAM/dsp.cpp @@ -2,6 +2,7 @@ #include // pow, tanh, expf #include #include +#include #include #include #include @@ -206,8 +207,21 @@ std::unique_ptr nam::linear::Factory(const nlohmann::json& config, std // Conv1x1 ==================================================================== -nam::Conv1x1::Conv1x1(const int in_channels, const int out_channels, const bool _bias) +nam::Conv1x1::Conv1x1(const int in_channels, const int out_channels, const bool _bias, const int groups) { + // Validate that channels divide evenly by groups + if (in_channels % groups != 0) + { + throw std::runtime_error("in_channels (" + std::to_string(in_channels) + ") must be divisible by numGroups (" + + std::to_string(groups) + ")"); + } + if (out_channels % groups != 0) + { + throw std::runtime_error("out_channels (" + std::to_string(out_channels) + ") must be divisible by numGroups (" + + std::to_string(groups) + ")"); + } + + this->_num_groups = groups; this->_weight.resize(out_channels, in_channels); this->_do_bias = _bias; if (_bias) @@ -222,9 +236,28 @@ void nam::Conv1x1::SetMaxBufferSize(const int maxBufferSize) void nam::Conv1x1::set_weights_(std::vector::iterator& weights) { - for (int i = 0; i < this->_weight.rows(); i++) - for (int j = 0; j < this->_weight.cols(); j++) - this->_weight(i, j) = *(weights++); + if (this->_weight.size() > 0) + { + const long out_channels = this->_weight.rows(); + const long in_channels = this->_weight.cols(); + const int numGroups = this->_num_groups; + const long out_per_group = out_channels / numGroups; + const long in_per_group = in_channels / numGroups; + + // For grouped convolutions, weights are organized per group + // Weight layout: weights are [group0, group1, ..., groupN-1] + // Each group's weight matrix is (out_channels/numGroups, in_channels/numGroups) + for (int g = 0; g < numGroups; g++) + { + for (auto i = 0; i < out_per_group; i++) + { + for (auto j = 0; j < in_per_group; j++) + { + this->_weight(g * out_per_group + i, g * in_per_group + j) = *(weights++); + } + } + } + } if (this->_do_bias) for (int i = 0; i < this->_bias.size(); i++) this->_bias(i) = *(weights++); @@ -232,16 +265,85 @@ void nam::Conv1x1::set_weights_(std::vector::iterator& weights) Eigen::MatrixXf nam::Conv1x1::process(const Eigen::MatrixXf& input, const int num_frames) const { - if (this->_do_bias) - return (this->_weight * input.leftCols(num_frames)).colwise() + this->_bias; + const int numGroups = this->_num_groups; + const long in_channels = get_in_channels(); + const long out_channels = get_out_channels(); + const long in_per_group = in_channels / numGroups; + const long out_per_group = out_channels / numGroups; + + Eigen::MatrixXf result(out_channels, num_frames); + + if (numGroups == 1) + { + // Standard convolution (no grouping) + if (this->_do_bias) + result = (this->_weight * input.leftCols(num_frames)).colwise() + this->_bias; + else + result = this->_weight * input.leftCols(num_frames); + } else - return this->_weight * input.leftCols(num_frames); + { + // Grouped convolution: process each group separately + result.setZero(); + for (int g = 0; g < numGroups; g++) + { + // Extract input slice for this group + auto input_group = input.leftCols(num_frames).middleRows(g * in_per_group, in_per_group); + + // Extract weight slice for this group + auto weight_group = this->_weight.block(g * out_per_group, g * in_per_group, out_per_group, in_per_group); + + // Extract output slice for this group + auto output_group = result.middleRows(g * out_per_group, out_per_group); + + // Perform grouped convolution: output_group = weight_group * input_group + output_group.noalias() = weight_group * input_group; + } + + // Add bias if present + if (this->_do_bias) + result.colwise() += this->_bias; + } + + return result; } void nam::Conv1x1::process_(const Eigen::MatrixXf& input, const int num_frames) { assert(num_frames <= _output.cols()); - _output.leftCols(num_frames).noalias() = this->_weight * input.leftCols(num_frames); + + const int numGroups = this->_num_groups; + const long in_channels = get_in_channels(); + const long out_channels = get_out_channels(); + const long in_per_group = in_channels / numGroups; + const long out_per_group = out_channels / numGroups; + + if (numGroups == 1) + { + // Standard convolution (no grouping) + _output.leftCols(num_frames).noalias() = this->_weight * input.leftCols(num_frames); + } + else + { + // Grouped convolution: process each group separately + _output.leftCols(num_frames).setZero(); + for (int g = 0; g < numGroups; g++) + { + // Extract input slice for this group + auto input_group = input.leftCols(num_frames).middleRows(g * in_per_group, in_per_group); + + // Extract weight slice for this group + auto weight_group = this->_weight.block(g * out_per_group, g * in_per_group, out_per_group, in_per_group); + + // Extract output slice for this group + auto output_group = _output.leftCols(num_frames).middleRows(g * out_per_group, out_per_group); + + // Perform grouped convolution: output_group = weight_group * input_group + output_group.noalias() = weight_group * input_group; + } + } + + // Add bias if present if (this->_do_bias) { _output.leftCols(num_frames).colwise() += this->_bias; diff --git a/NAM/dsp.h b/NAM/dsp.h index 3f9df92..f359a68 100644 --- a/NAM/dsp.h +++ b/NAM/dsp.h @@ -177,7 +177,7 @@ std::unique_ptr Factory(const nlohmann::json& config, std::vector& w class Conv1x1 { public: - Conv1x1(const int in_channels, const int out_channels, const bool _bias); + Conv1x1(const int in_channels, const int out_channels, const bool _bias, const int groups = 1); // Get the entire internal output buffer. This is intended for internal wiring // between layers/arrays; callers should treat the buffer as pre-allocated // storage and only consider the first `num_frames` columns valid for a given @@ -199,6 +199,7 @@ class Conv1x1 protected: Eigen::MatrixXf _weight; Eigen::VectorXf _bias; + int _num_groups; private: Eigen::MatrixXf _output; From 4c8d66ca7c291ccbef1d07638c76ea6d6f074eba Mon Sep 17 00:00:00 2001 From: Steven Atkinson Date: Wed, 14 Jan 2026 17:24:43 -0800 Subject: [PATCH 02/10] [FEATURE] Add tests for Conv1x1 layer functionality - Included new test cases for the Conv1x1 layer in run_tests.cpp, covering various construction and processing scenarios. - Tests validate functionality such as construction with groups, input/output channel validation, and processing with and without bias. - Enhanced test coverage for grouped convolutions, ensuring comprehensive validation of the Conv1x1 layer's capabilities. --- tools/run_tests.cpp | 16 ++ tools/test/test_conv_1x1.cpp | 445 +++++++++++++++++++++++++++++++++++ 2 files changed, 461 insertions(+) create mode 100644 tools/test/test_conv_1x1.cpp diff --git a/tools/run_tests.cpp b/tools/run_tests.cpp index e879050..aa28629 100644 --- a/tools/run_tests.cpp +++ b/tools/run_tests.cpp @@ -4,6 +4,7 @@ #include #include "test/test_activations.cpp" #include "test/test_conv1d.cpp" +#include "test/test_conv_1x1.cpp" #include "test/test_convnet.cpp" #include "test/test_dsp.cpp" #include "test/test_fast_lut.cpp" @@ -83,6 +84,21 @@ int main() test_conv1d::test_process_grouped_channel_isolation(); test_conv1d::test_get_num_weights_grouped(); + test_conv_1x1::test_construct(); + test_conv_1x1::test_construct_with_groups(); + test_conv_1x1::test_construct_validation_in_channels(); + test_conv_1x1::test_construct_validation_out_channels(); + test_conv_1x1::test_process_basic(); + test_conv_1x1::test_process_with_bias(); + test_conv_1x1::test_process_underscore(); + test_conv_1x1::test_process_grouped_basic(); + test_conv_1x1::test_process_grouped_with_bias(); + test_conv_1x1::test_process_grouped_multiple_groups(); + test_conv_1x1::test_process_grouped_channel_isolation(); + test_conv_1x1::test_process_underscore_grouped(); + test_conv_1x1::test_set_max_buffer_size(); + test_conv_1x1::test_process_multiple_calls(); + test_wavenet::test_layer::test_gated(); test_wavenet::test_layer::test_layer_getters(); test_wavenet::test_layer::test_non_gated_layer(); diff --git a/tools/test/test_conv_1x1.cpp b/tools/test/test_conv_1x1.cpp new file mode 100644 index 0000000..31483a6 --- /dev/null +++ b/tools/test/test_conv_1x1.cpp @@ -0,0 +1,445 @@ +// Tests for Conv1x1 + +#include +#include +#include +#include +#include +#include + +#include "NAM/dsp.h" + +namespace test_conv_1x1 +{ +// Test basic construction +void test_construct() +{ + nam::Conv1x1 conv(2, 3, false); + assert(conv.get_in_channels() == 2); + assert(conv.get_out_channels() == 3); +} + +// Test construction with groups (default should be 1) +void test_construct_with_groups() +{ + nam::Conv1x1 conv(4, 6, false, 2); + assert(conv.get_in_channels() == 4); + assert(conv.get_out_channels() == 6); +} + +// Test construction validation - in_channels not divisible by groups +void test_construct_validation_in_channels() +{ + bool threw = false; + try + { + nam::Conv1x1 conv(5, 6, false, 2); // 5 not divisible by 2 + } + catch (const std::runtime_error&) + { + threw = true; + } + assert(threw); +} + +// Test construction validation - out_channels not divisible by groups +void test_construct_validation_out_channels() +{ + bool threw = false; + try + { + nam::Conv1x1 conv(4, 5, false, 2); // 5 not divisible by 2 + } + catch (const std::runtime_error&) + { + threw = true; + } + assert(threw); +} + +// Test basic process without groups +void test_process_basic() +{ + nam::Conv1x1 conv(2, 3, false); + const int num_frames = 2; + + // Set weights: 3x2 matrix + // [1.0, 2.0] + // [3.0, 4.0] + // [5.0, 6.0] + std::vector weights{1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f}; + auto it = weights.begin(); + conv.set_weights_(it); + + conv.SetMaxBufferSize(64); + + Eigen::MatrixXf input(2, num_frames); + input(0, 0) = 1.0f; + input(1, 0) = 2.0f; + input(0, 1) = 3.0f; + input(1, 1) = 4.0f; + + Eigen::MatrixXf output = conv.process(input, num_frames); + + assert(output.rows() == 3); + assert(output.cols() == num_frames); + // Frame 0: [1.0, 2.0] * [1.0; 2.0] = [5.0, 11.0, 17.0] + assert(std::abs(output(0, 0) - 5.0f) < 0.01f); // 1.0*1.0 + 2.0*2.0 + assert(std::abs(output(1, 0) - 11.0f) < 0.01f); // 3.0*1.0 + 4.0*2.0 + assert(std::abs(output(2, 0) - 17.0f) < 0.01f); // 5.0*1.0 + 6.0*2.0 + // Frame 1: [1.0, 2.0] * [3.0; 4.0] = [11.0, 25.0, 39.0] + assert(std::abs(output(0, 1) - 11.0f) < 0.01f); // 1.0*3.0 + 2.0*4.0 + assert(std::abs(output(1, 1) - 25.0f) < 0.01f); // 3.0*3.0 + 4.0*4.0 + assert(std::abs(output(2, 1) - 39.0f) < 0.01f); // 5.0*3.0 + 6.0*4.0 +} + +// Test process with bias +void test_process_with_bias() +{ + nam::Conv1x1 conv(2, 2, true); + const int num_frames = 1; + + // Set weights: 2x2 identity matrix + // [1.0, 0.0] + // [0.0, 1.0] + // Bias: [10.0, 20.0] + std::vector weights{1.0f, 0.0f, 0.0f, 1.0f, 10.0f, 20.0f}; + auto it = weights.begin(); + conv.set_weights_(it); + + conv.SetMaxBufferSize(64); + + Eigen::MatrixXf input(2, num_frames); + input(0, 0) = 5.0f; + input(1, 0) = 7.0f; + + Eigen::MatrixXf output = conv.process(input, num_frames); + + assert(output.rows() == 2); + assert(output.cols() == num_frames); + // Output should be input + bias (identity weights) + assert(std::abs(output(0, 0) - 15.0f) < 0.01f); // 5.0 + 10.0 + assert(std::abs(output(1, 0) - 27.0f) < 0.01f); // 7.0 + 20.0 +} + +// Test process_ method (stores to internal buffer) +void test_process_underscore() +{ + nam::Conv1x1 conv(2, 2, false); + const int num_frames = 1; + + // Set weights: 2x2 identity matrix + std::vector weights{1.0f, 0.0f, 0.0f, 1.0f}; + auto it = weights.begin(); + conv.set_weights_(it); + + conv.SetMaxBufferSize(64); + + Eigen::MatrixXf input(2, num_frames); + input(0, 0) = 3.0f; + input(1, 0) = 4.0f; + + conv.process_(input, num_frames); + auto output = conv.GetOutput().leftCols(num_frames); + + assert(output.rows() == 2); + assert(output.cols() == num_frames); + assert(std::abs(output(0, 0) - 3.0f) < 0.01f); + assert(std::abs(output(1, 0) - 4.0f) < 0.01f); +} + +// Test basic grouped convolution with 2 groups +void test_process_grouped_basic() +{ + nam::Conv1x1 conv(4, 4, false, 2); + const int num_frames = 2; + + // For grouped convolution with 2 groups: + // Group 0: processes in_channels[0:1] -> out_channels[0:1] + // Group 1: processes in_channels[2:3] -> out_channels[2:3] + // Each group has out_per_group=2, in_per_group=2 + // Weight layout: [group0, group1] + // Group 0: identity matrix (2x2) + // Group 1: scale by 2.0 (2x2) + std::vector weights; + // Group 0: identity + weights.push_back(1.0f); // out[0], in[0] + weights.push_back(0.0f); // out[0], in[1] + weights.push_back(0.0f); // out[1], in[0] + weights.push_back(1.0f); // out[1], in[1] + // Group 1: scale by 2.0 + weights.push_back(2.0f); // out[2], in[2] + weights.push_back(0.0f); // out[2], in[3] + weights.push_back(0.0f); // out[3], in[2] + weights.push_back(2.0f); // out[3], in[3] + + auto it = weights.begin(); + conv.set_weights_(it); + + conv.SetMaxBufferSize(64); + + Eigen::MatrixXf input(4, num_frames); + input(0, 0) = 1.0f; // Group 0, channel 0 + input(1, 0) = 2.0f; // Group 0, channel 1 + input(2, 0) = 3.0f; // Group 1, channel 0 + input(3, 0) = 4.0f; // Group 1, channel 1 + input(0, 1) = 5.0f; + input(1, 1) = 6.0f; + input(2, 1) = 7.0f; + input(3, 1) = 8.0f; + + Eigen::MatrixXf output = conv.process(input, num_frames); + + assert(output.rows() == 4); + assert(output.cols() == num_frames); + // Group 0: identity transformation + assert(std::abs(output(0, 0) - 1.0f) < 0.01f); // out[0] = in[0] + assert(std::abs(output(1, 0) - 2.0f) < 0.01f); // out[1] = in[1] + // Group 1: double transformation + assert(std::abs(output(2, 0) - 6.0f) < 0.01f); // out[2] = 2.0 * in[2] + assert(std::abs(output(3, 0) - 8.0f) < 0.01f); // out[3] = 2.0 * in[3] + // Frame 1 + assert(std::abs(output(0, 1) - 5.0f) < 0.01f); + assert(std::abs(output(1, 1) - 6.0f) < 0.01f); + assert(std::abs(output(2, 1) - 14.0f) < 0.01f); // 2.0 * 7.0 + assert(std::abs(output(3, 1) - 16.0f) < 0.01f); // 2.0 * 8.0 +} + +// Test grouped convolution with bias +void test_process_grouped_with_bias() +{ + nam::Conv1x1 conv(4, 4, true, 2); + const int num_frames = 1; + + std::vector weights; + // Group 0 weights (2x2 identity) + weights.push_back(1.0f); + weights.push_back(0.0f); + weights.push_back(0.0f); + weights.push_back(1.0f); + // Group 1 weights (2x2 identity) + weights.push_back(1.0f); + weights.push_back(0.0f); + weights.push_back(0.0f); + weights.push_back(1.0f); + // Bias: [1.0, 2.0, 3.0, 4.0] + weights.push_back(1.0f); + weights.push_back(2.0f); + weights.push_back(3.0f); + weights.push_back(4.0f); + + auto it = weights.begin(); + conv.set_weights_(it); + + conv.SetMaxBufferSize(64); + + Eigen::MatrixXf input(4, num_frames); + input(0, 0) = 10.0f; + input(1, 0) = 20.0f; + input(2, 0) = 30.0f; + input(3, 0) = 40.0f; + + Eigen::MatrixXf output = conv.process(input, num_frames); + + assert(output.rows() == 4); + assert(output.cols() == num_frames); + // Output should be input + bias (identity weights) + assert(std::abs(output(0, 0) - 11.0f) < 0.01f); // 10.0 + 1.0 + assert(std::abs(output(1, 0) - 22.0f) < 0.01f); // 20.0 + 2.0 + assert(std::abs(output(2, 0) - 33.0f) < 0.01f); // 30.0 + 3.0 + assert(std::abs(output(3, 0) - 44.0f) < 0.01f); // 40.0 + 4.0 +} + +// Test grouped convolution with 4 groups +void test_process_grouped_multiple_groups() +{ + nam::Conv1x1 conv(8, 8, false, 4); + const int num_frames = 1; + + // Each group processes 2 input channels -> 2 output channels + std::vector weights; + // Group 0: scale by 1.0 + weights.push_back(1.0f); + weights.push_back(0.0f); + weights.push_back(0.0f); + weights.push_back(1.0f); + // Group 1: scale by 2.0 + weights.push_back(2.0f); + weights.push_back(0.0f); + weights.push_back(0.0f); + weights.push_back(2.0f); + // Group 2: scale by 3.0 + weights.push_back(3.0f); + weights.push_back(0.0f); + weights.push_back(0.0f); + weights.push_back(3.0f); + // Group 3: scale by 4.0 + weights.push_back(4.0f); + weights.push_back(0.0f); + weights.push_back(0.0f); + weights.push_back(4.0f); + + auto it = weights.begin(); + conv.set_weights_(it); + + conv.SetMaxBufferSize(64); + + Eigen::MatrixXf input(8, num_frames); + for (int i = 0; i < 8; i++) + { + input(i, 0) = static_cast(i + 1); + } + + Eigen::MatrixXf output = conv.process(input, num_frames); + + assert(output.rows() == 8); + assert(output.cols() == num_frames); + // Group 0: channels 0-1 scaled by 1.0 + assert(std::abs(output(0, 0) - 1.0f) < 0.01f); + assert(std::abs(output(1, 0) - 2.0f) < 0.01f); + // Group 1: channels 2-3 scaled by 2.0 + assert(std::abs(output(2, 0) - 6.0f) < 0.01f); // 3.0 * 2.0 + assert(std::abs(output(3, 0) - 8.0f) < 0.01f); // 4.0 * 2.0 + // Group 2: channels 4-5 scaled by 3.0 + assert(std::abs(output(4, 0) - 15.0f) < 0.01f); // 5.0 * 3.0 + assert(std::abs(output(5, 0) - 18.0f) < 0.01f); // 6.0 * 3.0 + // Group 3: channels 6-7 scaled by 4.0 + assert(std::abs(output(6, 0) - 28.0f) < 0.01f); // 7.0 * 4.0 + assert(std::abs(output(7, 0) - 32.0f) < 0.01f); // 8.0 * 4.0 +} + +// Test that groups don't mix channels (channel isolation) +void test_process_grouped_channel_isolation() +{ + nam::Conv1x1 conv(6, 6, false, 3); + const int num_frames = 1; + + // 3 groups, each processes 2 channels + // Group 0: channels 0-1, set to zero (zero matrix) + // Group 1: channels 2-3, identity + // Group 2: channels 4-5, identity + std::vector weights; + // Group 0: zero matrix + weights.push_back(0.0f); + weights.push_back(0.0f); + weights.push_back(0.0f); + weights.push_back(0.0f); + // Group 1: identity + weights.push_back(1.0f); + weights.push_back(0.0f); + weights.push_back(0.0f); + weights.push_back(1.0f); + // Group 2: identity + weights.push_back(1.0f); + weights.push_back(0.0f); + weights.push_back(0.0f); + weights.push_back(1.0f); + + auto it = weights.begin(); + conv.set_weights_(it); + + conv.SetMaxBufferSize(64); + + Eigen::MatrixXf input(6, num_frames); + input(0, 0) = 10.0f; // Should be zeroed by group 0 + input(1, 0) = 20.0f; // Should be zeroed by group 0 + input(2, 0) = 30.0f; // Should pass through group 1 + input(3, 0) = 40.0f; // Should pass through group 1 + input(4, 0) = 50.0f; // Should pass through group 2 + input(5, 0) = 60.0f; // Should pass through group 2 + + Eigen::MatrixXf output = conv.process(input, num_frames); + + assert(output.rows() == 6); + assert(output.cols() == num_frames); + // Group 0: should be zero + assert(std::abs(output(0, 0)) < 0.01f); + assert(std::abs(output(1, 0)) < 0.01f); + // Group 1: should pass through + assert(std::abs(output(2, 0) - 30.0f) < 0.01f); + assert(std::abs(output(3, 0) - 40.0f) < 0.01f); + // Group 2: should pass through + assert(std::abs(output(4, 0) - 50.0f) < 0.01f); + assert(std::abs(output(5, 0) - 60.0f) < 0.01f); +} + +// Test process_ with groups +void test_process_underscore_grouped() +{ + nam::Conv1x1 conv(4, 4, false, 2); + const int num_frames = 1; + + // Group 0: identity, Group 1: scale by 2.0 + std::vector weights; + // Group 0: identity + weights.push_back(1.0f); + weights.push_back(0.0f); + weights.push_back(0.0f); + weights.push_back(1.0f); + // Group 1: scale by 2.0 + weights.push_back(2.0f); + weights.push_back(0.0f); + weights.push_back(0.0f); + weights.push_back(2.0f); + + auto it = weights.begin(); + conv.set_weights_(it); + + conv.SetMaxBufferSize(64); + + Eigen::MatrixXf input(4, num_frames); + input(0, 0) = 1.0f; + input(1, 0) = 2.0f; + input(2, 0) = 3.0f; + input(3, 0) = 4.0f; + + conv.process_(input, num_frames); + auto output = conv.GetOutput().leftCols(num_frames); + + assert(output.rows() == 4); + assert(output.cols() == num_frames); + assert(std::abs(output(0, 0) - 1.0f) < 0.01f); + assert(std::abs(output(1, 0) - 2.0f) < 0.01f); + assert(std::abs(output(2, 0) - 6.0f) < 0.01f); // 2.0 * 3.0 + assert(std::abs(output(3, 0) - 8.0f) < 0.01f); // 2.0 * 4.0 +} + +// Test SetMaxBufferSize +void test_set_max_buffer_size() +{ + nam::Conv1x1 conv(2, 3, false); + const int maxBufferSize = 128; + + conv.SetMaxBufferSize(maxBufferSize); + auto output = conv.GetOutput(); + assert(output.rows() == 3); + assert(output.cols() == maxBufferSize); +} + +// Test multiple calls to process +void test_process_multiple_calls() +{ + nam::Conv1x1 conv(2, 2, false); + // Identity matrix + std::vector weights{1.0f, 0.0f, 0.0f, 1.0f}; + auto it = weights.begin(); + conv.set_weights_(it); + conv.SetMaxBufferSize(64); + + Eigen::MatrixXf input1(2, 1); + input1(0, 0) = 1.0f; + input1(1, 0) = 2.0f; + + Eigen::MatrixXf output1 = conv.process(input1, 1); + assert(std::abs(output1(0, 0) - 1.0f) < 0.01f); + assert(std::abs(output1(1, 0) - 2.0f) < 0.01f); + + Eigen::MatrixXf input2(2, 1); + input2(0, 0) = 3.0f; + input2(1, 0) = 4.0f; + + Eigen::MatrixXf output2 = conv.process(input2, 1); + assert(std::abs(output2(0, 0) - 3.0f) < 0.01f); + assert(std::abs(output2(1, 0) - 4.0f) < 0.01f); +} +} // namespace test_conv_1x1 From 99213b36de645b919f9b1ad633c7e070758cb1c4 Mon Sep 17 00:00:00 2001 From: Steven Atkinson Date: Wed, 14 Jan 2026 17:27:28 -0800 Subject: [PATCH 03/10] [REFINE] Update Conv1x1 tests for clarity and consistency - Refactored test cases in test_conv_1x1.cpp to use named constants for input/output channels and bias parameters, improving readability. - Ensured all assertions for output dimensions are based on the defined constants, enhancing maintainability. - Updated tests to validate behavior for both basic and grouped convolution scenarios, ensuring comprehensive coverage of the Conv1x1 functionality. --- tools/test/test_conv_1x1.cpp | 126 ++++++++++++++++++++++++----------- 1 file changed, 88 insertions(+), 38 deletions(-) diff --git a/tools/test/test_conv_1x1.cpp b/tools/test/test_conv_1x1.cpp index 31483a6..cb3e234 100644 --- a/tools/test/test_conv_1x1.cpp +++ b/tools/test/test_conv_1x1.cpp @@ -14,26 +14,37 @@ namespace test_conv_1x1 // Test basic construction void test_construct() { - nam::Conv1x1 conv(2, 3, false); - assert(conv.get_in_channels() == 2); - assert(conv.get_out_channels() == 3); + const int in_channels = 2; + const int out_channels = 3; + const bool do_bias = false; + nam::Conv1x1 conv(in_channels, out_channels, do_bias); + assert(conv.get_in_channels() == in_channels); + assert(conv.get_out_channels() == out_channels); } // Test construction with groups (default should be 1) void test_construct_with_groups() { - nam::Conv1x1 conv(4, 6, false, 2); - assert(conv.get_in_channels() == 4); - assert(conv.get_out_channels() == 6); + const int in_channels = 4; + const int out_channels = 6; + const bool do_bias = false; + const int groups = 2; + nam::Conv1x1 conv(in_channels, out_channels, do_bias, groups); + assert(conv.get_in_channels() == in_channels); + assert(conv.get_out_channels() == out_channels); } // Test construction validation - in_channels not divisible by groups void test_construct_validation_in_channels() { + const int in_channels = 5; + const int out_channels = 6; + const bool do_bias = false; + const int groups = 2; // 5 not divisible by 2 bool threw = false; try { - nam::Conv1x1 conv(5, 6, false, 2); // 5 not divisible by 2 + nam::Conv1x1 conv(in_channels, out_channels, do_bias, groups); } catch (const std::runtime_error&) { @@ -45,10 +56,14 @@ void test_construct_validation_in_channels() // Test construction validation - out_channels not divisible by groups void test_construct_validation_out_channels() { + const int in_channels = 4; + const int out_channels = 5; + const bool do_bias = false; + const int groups = 2; // 5 not divisible by 2 bool threw = false; try { - nam::Conv1x1 conv(4, 5, false, 2); // 5 not divisible by 2 + nam::Conv1x1 conv(in_channels, out_channels, do_bias, groups); } catch (const std::runtime_error&) { @@ -60,7 +75,10 @@ void test_construct_validation_out_channels() // Test basic process without groups void test_process_basic() { - nam::Conv1x1 conv(2, 3, false); + const int in_channels = 2; + const int out_channels = 3; + const bool do_bias = false; + nam::Conv1x1 conv(in_channels, out_channels, do_bias); const int num_frames = 2; // Set weights: 3x2 matrix @@ -73,7 +91,7 @@ void test_process_basic() conv.SetMaxBufferSize(64); - Eigen::MatrixXf input(2, num_frames); + Eigen::MatrixXf input(in_channels, num_frames); input(0, 0) = 1.0f; input(1, 0) = 2.0f; input(0, 1) = 3.0f; @@ -81,7 +99,7 @@ void test_process_basic() Eigen::MatrixXf output = conv.process(input, num_frames); - assert(output.rows() == 3); + assert(output.rows() == out_channels); assert(output.cols() == num_frames); // Frame 0: [1.0, 2.0] * [1.0; 2.0] = [5.0, 11.0, 17.0] assert(std::abs(output(0, 0) - 5.0f) < 0.01f); // 1.0*1.0 + 2.0*2.0 @@ -96,7 +114,10 @@ void test_process_basic() // Test process with bias void test_process_with_bias() { - nam::Conv1x1 conv(2, 2, true); + const int in_channels = 2; + const int out_channels = 2; + const bool do_bias = true; + nam::Conv1x1 conv(in_channels, out_channels, do_bias); const int num_frames = 1; // Set weights: 2x2 identity matrix @@ -109,13 +130,13 @@ void test_process_with_bias() conv.SetMaxBufferSize(64); - Eigen::MatrixXf input(2, num_frames); + Eigen::MatrixXf input(in_channels, num_frames); input(0, 0) = 5.0f; input(1, 0) = 7.0f; Eigen::MatrixXf output = conv.process(input, num_frames); - assert(output.rows() == 2); + assert(output.rows() == out_channels); assert(output.cols() == num_frames); // Output should be input + bias (identity weights) assert(std::abs(output(0, 0) - 15.0f) < 0.01f); // 5.0 + 10.0 @@ -125,7 +146,10 @@ void test_process_with_bias() // Test process_ method (stores to internal buffer) void test_process_underscore() { - nam::Conv1x1 conv(2, 2, false); + const int in_channels = 2; + const int out_channels = 2; + const bool do_bias = false; + nam::Conv1x1 conv(in_channels, out_channels, do_bias); const int num_frames = 1; // Set weights: 2x2 identity matrix @@ -135,14 +159,14 @@ void test_process_underscore() conv.SetMaxBufferSize(64); - Eigen::MatrixXf input(2, num_frames); + Eigen::MatrixXf input(in_channels, num_frames); input(0, 0) = 3.0f; input(1, 0) = 4.0f; conv.process_(input, num_frames); auto output = conv.GetOutput().leftCols(num_frames); - assert(output.rows() == 2); + assert(output.rows() == out_channels); assert(output.cols() == num_frames); assert(std::abs(output(0, 0) - 3.0f) < 0.01f); assert(std::abs(output(1, 0) - 4.0f) < 0.01f); @@ -151,7 +175,11 @@ void test_process_underscore() // Test basic grouped convolution with 2 groups void test_process_grouped_basic() { - nam::Conv1x1 conv(4, 4, false, 2); + const int in_channels = 4; + const int out_channels = 4; + const bool do_bias = false; + const int groups = 2; + nam::Conv1x1 conv(in_channels, out_channels, do_bias, groups); const int num_frames = 2; // For grouped convolution with 2 groups: @@ -178,7 +206,7 @@ void test_process_grouped_basic() conv.SetMaxBufferSize(64); - Eigen::MatrixXf input(4, num_frames); + Eigen::MatrixXf input(in_channels, num_frames); input(0, 0) = 1.0f; // Group 0, channel 0 input(1, 0) = 2.0f; // Group 0, channel 1 input(2, 0) = 3.0f; // Group 1, channel 0 @@ -190,7 +218,7 @@ void test_process_grouped_basic() Eigen::MatrixXf output = conv.process(input, num_frames); - assert(output.rows() == 4); + assert(output.rows() == out_channels); assert(output.cols() == num_frames); // Group 0: identity transformation assert(std::abs(output(0, 0) - 1.0f) < 0.01f); // out[0] = in[0] @@ -208,7 +236,11 @@ void test_process_grouped_basic() // Test grouped convolution with bias void test_process_grouped_with_bias() { - nam::Conv1x1 conv(4, 4, true, 2); + const int in_channels = 4; + const int out_channels = 4; + const bool do_bias = true; + const int groups = 2; + nam::Conv1x1 conv(in_channels, out_channels, do_bias, groups); const int num_frames = 1; std::vector weights; @@ -233,7 +265,7 @@ void test_process_grouped_with_bias() conv.SetMaxBufferSize(64); - Eigen::MatrixXf input(4, num_frames); + Eigen::MatrixXf input(in_channels, num_frames); input(0, 0) = 10.0f; input(1, 0) = 20.0f; input(2, 0) = 30.0f; @@ -241,7 +273,7 @@ void test_process_grouped_with_bias() Eigen::MatrixXf output = conv.process(input, num_frames); - assert(output.rows() == 4); + assert(output.rows() == out_channels); assert(output.cols() == num_frames); // Output should be input + bias (identity weights) assert(std::abs(output(0, 0) - 11.0f) < 0.01f); // 10.0 + 1.0 @@ -253,7 +285,11 @@ void test_process_grouped_with_bias() // Test grouped convolution with 4 groups void test_process_grouped_multiple_groups() { - nam::Conv1x1 conv(8, 8, false, 4); + const int in_channels = 8; + const int out_channels = 8; + const bool do_bias = false; + const int groups = 4; + nam::Conv1x1 conv(in_channels, out_channels, do_bias, groups); const int num_frames = 1; // Each group processes 2 input channels -> 2 output channels @@ -284,15 +320,15 @@ void test_process_grouped_multiple_groups() conv.SetMaxBufferSize(64); - Eigen::MatrixXf input(8, num_frames); - for (int i = 0; i < 8; i++) + Eigen::MatrixXf input(in_channels, num_frames); + for (int i = 0; i < in_channels; i++) { input(i, 0) = static_cast(i + 1); } Eigen::MatrixXf output = conv.process(input, num_frames); - assert(output.rows() == 8); + assert(output.rows() == out_channels); assert(output.cols() == num_frames); // Group 0: channels 0-1 scaled by 1.0 assert(std::abs(output(0, 0) - 1.0f) < 0.01f); @@ -311,7 +347,11 @@ void test_process_grouped_multiple_groups() // Test that groups don't mix channels (channel isolation) void test_process_grouped_channel_isolation() { - nam::Conv1x1 conv(6, 6, false, 3); + const int in_channels = 6; + const int out_channels = 6; + const bool do_bias = false; + const int groups = 3; + nam::Conv1x1 conv(in_channels, out_channels, do_bias, groups); const int num_frames = 1; // 3 groups, each processes 2 channels @@ -340,7 +380,7 @@ void test_process_grouped_channel_isolation() conv.SetMaxBufferSize(64); - Eigen::MatrixXf input(6, num_frames); + Eigen::MatrixXf input(in_channels, num_frames); input(0, 0) = 10.0f; // Should be zeroed by group 0 input(1, 0) = 20.0f; // Should be zeroed by group 0 input(2, 0) = 30.0f; // Should pass through group 1 @@ -350,7 +390,7 @@ void test_process_grouped_channel_isolation() Eigen::MatrixXf output = conv.process(input, num_frames); - assert(output.rows() == 6); + assert(output.rows() == out_channels); assert(output.cols() == num_frames); // Group 0: should be zero assert(std::abs(output(0, 0)) < 0.01f); @@ -366,7 +406,11 @@ void test_process_grouped_channel_isolation() // Test process_ with groups void test_process_underscore_grouped() { - nam::Conv1x1 conv(4, 4, false, 2); + const int in_channels = 4; + const int out_channels = 4; + const bool do_bias = false; + const int groups = 2; + nam::Conv1x1 conv(in_channels, out_channels, do_bias, groups); const int num_frames = 1; // Group 0: identity, Group 1: scale by 2.0 @@ -387,7 +431,7 @@ void test_process_underscore_grouped() conv.SetMaxBufferSize(64); - Eigen::MatrixXf input(4, num_frames); + Eigen::MatrixXf input(in_channels, num_frames); input(0, 0) = 1.0f; input(1, 0) = 2.0f; input(2, 0) = 3.0f; @@ -396,7 +440,7 @@ void test_process_underscore_grouped() conv.process_(input, num_frames); auto output = conv.GetOutput().leftCols(num_frames); - assert(output.rows() == 4); + assert(output.rows() == out_channels); assert(output.cols() == num_frames); assert(std::abs(output(0, 0) - 1.0f) < 0.01f); assert(std::abs(output(1, 0) - 2.0f) < 0.01f); @@ -407,26 +451,32 @@ void test_process_underscore_grouped() // Test SetMaxBufferSize void test_set_max_buffer_size() { - nam::Conv1x1 conv(2, 3, false); + const int in_channels = 2; + const int out_channels = 3; + const bool do_bias = false; + nam::Conv1x1 conv(in_channels, out_channels, do_bias); const int maxBufferSize = 128; conv.SetMaxBufferSize(maxBufferSize); auto output = conv.GetOutput(); - assert(output.rows() == 3); + assert(output.rows() == out_channels); assert(output.cols() == maxBufferSize); } // Test multiple calls to process void test_process_multiple_calls() { - nam::Conv1x1 conv(2, 2, false); + const int in_channels = 2; + const int out_channels = 2; + const bool do_bias = false; + nam::Conv1x1 conv(in_channels, out_channels, do_bias); // Identity matrix std::vector weights{1.0f, 0.0f, 0.0f, 1.0f}; auto it = weights.begin(); conv.set_weights_(it); conv.SetMaxBufferSize(64); - Eigen::MatrixXf input1(2, 1); + Eigen::MatrixXf input1(in_channels, 1); input1(0, 0) = 1.0f; input1(1, 0) = 2.0f; @@ -434,7 +484,7 @@ void test_process_multiple_calls() assert(std::abs(output1(0, 0) - 1.0f) < 0.01f); assert(std::abs(output1(1, 0) - 2.0f) < 0.01f); - Eigen::MatrixXf input2(2, 1); + Eigen::MatrixXf input2(in_channels, 1); input2(0, 0) = 3.0f; input2(1, 0) = 4.0f; From 9045ca3f018e5ac66c6a317dc68ab89a4a6dbcc3 Mon Sep 17 00:00:00 2001 From: Steven Atkinson Date: Wed, 14 Jan 2026 17:33:56 -0800 Subject: [PATCH 04/10] [FEATURE] Extend WaveNet Layer and LayerArray to support additional grouping parameters - Updated the WaveNet _Layer and _LayerArray constructors to accept new parameters: groups_condition and groups_1x1, enhancing flexibility for grouped convolutions. - Modified relevant test cases in test_layer, test_layer_array, and test_full to incorporate the new parameters, ensuring comprehensive testing of the updated functionality. - Ensured backward compatibility by maintaining existing parameter structures while adding new ones for improved configurability. --- NAM/wavenet.cpp | 11 ++++++--- tools/test/test_wavenet/test_full.cpp | 26 +++++++++++++++----- tools/test/test_wavenet/test_layer.cpp | 25 +++++++++++++++---- tools/test/test_wavenet/test_layer_array.cpp | 18 +++++++++----- 4 files changed, 59 insertions(+), 21 deletions(-) diff --git a/NAM/wavenet.cpp b/NAM/wavenet.cpp index a1075ae..4c45319 100644 --- a/NAM/wavenet.cpp +++ b/NAM/wavenet.cpp @@ -74,13 +74,14 @@ void nam::wavenet::_Layer::Process(const Eigen::MatrixXf& input, const Eigen::Ma nam::wavenet::_LayerArray::_LayerArray(const int input_size, const int condition_size, const int head_size, const int channels, const int kernel_size, const std::vector& dilations, const std::string activation, const bool gated, const bool head_bias, - const int groups_input) + const int groups_input, const int groups_condition, const int groups_1x1) : _rechannel(input_size, channels, false) , _head_rechannel(channels, head_size, head_bias) { for (size_t i = 0; i < dilations.size(); i++) this->_layers.push_back( - _Layer(condition_size, channels, kernel_size, dilations[i], activation, gated, groups_input)); + _Layer(condition_size, channels, kernel_size, dilations[i], activation, gated, groups_input, groups_condition, + groups_1x1)); } void nam::wavenet::_LayerArray::SetMaxBufferSize(const int maxBufferSize) @@ -201,7 +202,7 @@ nam::wavenet::WaveNet::WaveNet(const std::vector layer_array_params[i].input_size, layer_array_params[i].condition_size, layer_array_params[i].head_size, layer_array_params[i].channels, layer_array_params[i].kernel_size, layer_array_params[i].dilations, layer_array_params[i].activation, layer_array_params[i].gated, layer_array_params[i].head_bias, - layer_array_params[i].groups_input)); + layer_array_params[i].groups_input, layer_array_params[i].groups_condition, layer_array_params[i].groups_1x1)); if (i > 0) if (layer_array_params[i].channels != layer_array_params[i - 1].head_size) { @@ -299,10 +300,12 @@ std::unique_ptr nam::wavenet::Factory(const nlohmann::json& config, st { nlohmann::json layer_config = config["layers"][i]; const int groups = layer_config.value("groups", 1); // defaults to 1 + const int groups_condition = layer_config.value("groups_condition", 1); // defaults to 1 + const int groups_1x1 = layer_config.value("groups_1x1", 1); // defaults to 1 layer_array_params.push_back(nam::wavenet::LayerArrayParams( layer_config["input_size"], layer_config["condition_size"], layer_config["head_size"], layer_config["channels"], layer_config["kernel_size"], layer_config["dilations"], layer_config["activation"], layer_config["gated"], - layer_config["head_bias"], groups)); + layer_config["head_bias"], groups, groups_condition, groups_1x1)); } const bool with_head = !config["head"].is_null(); const float head_scale = config["head_scale"]; diff --git a/tools/test/test_wavenet/test_full.cpp b/tools/test/test_wavenet/test_full.cpp index 2fc20c9..b90419c 100644 --- a/tools/test/test_wavenet/test_full.cpp +++ b/tools/test/test_wavenet/test_full.cpp @@ -27,9 +27,12 @@ void test_wavenet_model() const float head_scale = 1.0f; const bool with_head = false; const int groups = 1; + const int groups_condition = 1; + const int groups_1x1 = 1; nam::wavenet::LayerArrayParams params(input_size, condition_size, head_size, channels, kernel_size, - std::move(dilations), activation, gated, head_bias, groups); + std::move(dilations), activation, gated, head_bias, groups, groups_condition, + groups_1x1); std::vector layer_array_params; layer_array_params.push_back(std::move(params)); @@ -80,18 +83,20 @@ void test_wavenet_multiple_arrays() const float head_scale = 0.5f; const bool with_head = false; const int groups = 1; + const int groups_condition = 1; + const int groups_1x1 = 1; std::vector layer_array_params; // First array std::vector dilations1{1}; layer_array_params.push_back(nam::wavenet::LayerArrayParams(input_size, condition_size, head_size, channels, kernel_size, std::move(dilations1), activation, gated, - head_bias, groups)); + head_bias, groups, groups_condition, groups_1x1)); // Second array (head_size of first must match channels of second) std::vector dilations2{1}; layer_array_params.push_back(nam::wavenet::LayerArrayParams(head_size, condition_size, head_size, channels, kernel_size, std::move(dilations2), activation, gated, - head_bias, groups)); + head_bias, groups, groups_condition, groups_1x1)); std::vector weights; // Array 0: rechannel, layer, head_rechannel @@ -133,9 +138,12 @@ void test_wavenet_zero_input() const float head_scale = 1.0f; const bool with_head = false; const int groups = 1; + const int groups_condition = 1; + const int groups_1x1 = 1; nam::wavenet::LayerArrayParams params(input_size, condition_size, head_size, channels, kernel_size, - std::move(dilations), activation, gated, head_bias, groups); + std::move(dilations), activation, gated, head_bias, groups, groups_condition, + groups_1x1); std::vector layer_array_params; layer_array_params.push_back(std::move(params)); @@ -173,9 +181,12 @@ void test_wavenet_different_buffer_sizes() const float head_scale = 1.0f; const bool with_head = false; const int groups = 1; + const int groups_condition = 1; + const int groups_1x1 = 1; nam::wavenet::LayerArrayParams params(input_size, condition_size, head_size, channels, kernel_size, - std::move(dilations), activation, gated, head_bias, groups); + std::move(dilations), activation, gated, head_bias, groups, groups_condition, + groups_1x1); std::vector layer_array_params; layer_array_params.push_back(std::move(params)); @@ -214,9 +225,12 @@ void test_wavenet_prewarm() const float head_scale = 1.0f; const bool with_head = false; const int groups = 1; + const int groups_condition = 1; + const int groups_1x1 = 1; nam::wavenet::LayerArrayParams params(input_size, condition_size, head_size, channels, kernel_size, - std::move(dilations), activation, gated, head_bias, groups); + std::move(dilations), activation, gated, head_bias, groups, groups_condition, + groups_1x1); std::vector layer_array_params; layer_array_params.push_back(std::move(params)); diff --git a/tools/test/test_wavenet/test_layer.cpp b/tools/test/test_wavenet/test_layer.cpp index 1a27bea..ab814a5 100644 --- a/tools/test/test_wavenet/test_layer.cpp +++ b/tools/test/test_wavenet/test_layer.cpp @@ -23,7 +23,10 @@ void test_gated() const std::string activation = "ReLU"; const bool gated = true; const int groups_input = 1; - auto layer = nam::wavenet::_Layer(conditionSize, channels, kernelSize, dilation, activation, gated, groups_input); + const int groups_condition = 1; + const int groups_1x1 = 1; + auto layer = nam::wavenet::_Layer(conditionSize, channels, kernelSize, dilation, activation, gated, groups_input, + groups_condition, groups_1x1); // Conv, input mixin, 1x1 std::vector weights{ @@ -96,7 +99,10 @@ void test_layer_getters() const bool gated = false; const int groups_input = 1; - auto layer = nam::wavenet::_Layer(conditionSize, channels, kernelSize, dilation, activation, gated, groups_input); + const int groups_condition = 1; + const int groups_1x1 = 1; + auto layer = nam::wavenet::_Layer(conditionSize, channels, kernelSize, dilation, activation, gated, groups_input, + groups_condition, groups_1x1); assert(layer.get_channels() == channels); assert(layer.get_kernel_size() == kernelSize); @@ -114,7 +120,10 @@ void test_non_gated_layer() const bool gated = false; const int groups_input = 1; - auto layer = nam::wavenet::_Layer(conditionSize, channels, kernelSize, dilation, activation, gated, groups_input); + const int groups_condition = 1; + const int groups_1x1 = 1; + auto layer = nam::wavenet::_Layer(conditionSize, channels, kernelSize, dilation, activation, gated, groups_input, + groups_condition, groups_1x1); // For non-gated: conv outputs 1 channel, input_mixin outputs 1 channel, 1x1 outputs 1 channel // Conv: (1,1,1) weight + (1,) bias @@ -178,7 +187,10 @@ void test_layer_activations() // Test Tanh activation { const int groups_input = 1; - auto layer = nam::wavenet::_Layer(conditionSize, channels, kernelSize, dilation, "Tanh", gated, groups_input); + const int groups_condition = 1; + const int groups_1x1 = 1; + auto layer = nam::wavenet::_Layer(conditionSize, channels, kernelSize, dilation, "Tanh", gated, groups_input, + groups_condition, groups_1x1); std::vector weights{1.0f, 0.0f, 1.0f, 1.0f, 0.0f}; auto it = weights.begin(); layer.set_weights_(it); @@ -211,7 +223,10 @@ void test_layer_multichannel() const bool gated = false; const int groups_input = 1; - auto layer = nam::wavenet::_Layer(conditionSize, channels, kernelSize, dilation, activation, gated, groups_input); + const int groups_condition = 1; + const int groups_1x1 = 1; + auto layer = nam::wavenet::_Layer(conditionSize, channels, kernelSize, dilation, activation, gated, groups_input, + groups_condition, groups_1x1); assert(layer.get_channels() == channels); diff --git a/tools/test/test_wavenet/test_layer_array.cpp b/tools/test/test_wavenet/test_layer_array.cpp index 7614562..a7acd29 100644 --- a/tools/test/test_wavenet/test_layer_array.cpp +++ b/tools/test/test_wavenet/test_layer_array.cpp @@ -26,8 +26,10 @@ void test_layer_array_basic() const bool head_bias = false; const int groups = 1; - auto layer_array = nam::wavenet::_LayerArray( - input_size, condition_size, head_size, channels, kernel_size, dilations, activation, gated, head_bias, groups); + const int groups_condition = 1; + const int groups_1x1 = 1; + auto layer_array = nam::wavenet::_LayerArray(input_size, condition_size, head_size, channels, kernel_size, dilations, + activation, gated, head_bias, groups, groups_condition, groups_1x1); const int numFrames = 4; layer_array.SetMaxBufferSize(numFrames); @@ -81,8 +83,10 @@ void test_layer_array_receptive_field() const bool head_bias = false; const int groups = 1; - auto layer_array = nam::wavenet::_LayerArray( - input_size, condition_size, head_size, channels, kernel_size, dilations, activation, gated, head_bias, groups); + const int groups_condition = 1; + const int groups_1x1 = 1; + auto layer_array = nam::wavenet::_LayerArray(input_size, condition_size, head_size, channels, kernel_size, dilations, + activation, gated, head_bias, groups, groups_condition, groups_1x1); long rf = layer_array.get_receptive_field(); // Expected: sum of dilation * (kernel_size - 1) for each layer @@ -108,8 +112,10 @@ void test_layer_array_with_head_input() const bool head_bias = false; const int groups = 1; - auto layer_array = nam::wavenet::_LayerArray( - input_size, condition_size, head_size, channels, kernel_size, dilations, activation, gated, head_bias, groups); + const int groups_condition = 1; + const int groups_1x1 = 1; + auto layer_array = nam::wavenet::_LayerArray(input_size, condition_size, head_size, channels, kernel_size, dilations, + activation, gated, head_bias, groups, groups_condition, groups_1x1); const int numFrames = 2; layer_array.SetMaxBufferSize(numFrames); From 6657a6531b0477495ceb6ef7168848b90b90fac3 Mon Sep 17 00:00:00 2001 From: Steven Atkinson Date: Wed, 14 Jan 2026 17:34:30 -0800 Subject: [PATCH 05/10] Formatting --- NAM/dsp.cpp | 2 +- NAM/wavenet.cpp | 5 ++--- NAM/wavenet.h | 16 +++++++++----- tools/test/test_wavenet/test_layer.cpp | 20 ++++++++--------- .../test/test_wavenet/test_real_time_safe.cpp | 22 ++++++++++++++----- 5 files changed, 40 insertions(+), 25 deletions(-) diff --git a/NAM/dsp.cpp b/NAM/dsp.cpp index 74a8df1..dc46891 100644 --- a/NAM/dsp.cpp +++ b/NAM/dsp.cpp @@ -311,7 +311,7 @@ Eigen::MatrixXf nam::Conv1x1::process(const Eigen::MatrixXf& input, const int nu void nam::Conv1x1::process_(const Eigen::MatrixXf& input, const int num_frames) { assert(num_frames <= _output.cols()); - + const int numGroups = this->_num_groups; const long in_channels = get_in_channels(); const long out_channels = get_out_channels(); diff --git a/NAM/wavenet.cpp b/NAM/wavenet.cpp index 4c45319..55c8de5 100644 --- a/NAM/wavenet.cpp +++ b/NAM/wavenet.cpp @@ -79,9 +79,8 @@ nam::wavenet::_LayerArray::_LayerArray(const int input_size, const int condition , _head_rechannel(channels, head_size, head_bias) { for (size_t i = 0; i < dilations.size(); i++) - this->_layers.push_back( - _Layer(condition_size, channels, kernel_size, dilations[i], activation, gated, groups_input, groups_condition, - groups_1x1)); + this->_layers.push_back(_Layer(condition_size, channels, kernel_size, dilations[i], activation, gated, groups_input, + groups_condition, groups_1x1)); } void nam::wavenet::_LayerArray::SetMaxBufferSize(const int maxBufferSize) diff --git a/NAM/wavenet.h b/NAM/wavenet.h index b460ef5..55a5c77 100644 --- a/NAM/wavenet.h +++ b/NAM/wavenet.h @@ -17,10 +17,11 @@ class _Layer { public: _Layer(const int condition_size, const int channels, const int kernel_size, const int dilation, - const std::string activation, const bool gated, const int groups_input) + const std::string activation, const bool gated, const int groups_input, const int groups_condition, + const int groups_1x1) : _conv(channels, gated ? 2 * channels : channels, kernel_size, true, dilation, groups_input) - , _input_mixin(condition_size, gated ? 2 * channels : channels, false) - , _1x1(channels, channels, true) + , _input_mixin(condition_size, gated ? 2 * channels : channels, false, groups_condition) + , _1x1(channels, channels, true, groups_1x1) , _activation(activations::Activation::get_activation(activation)) // needs to support activations with parameters , _gated(gated) {}; // Resize all arrays to be able to process `maxBufferSize` frames. @@ -78,7 +79,8 @@ class LayerArrayParams public: LayerArrayParams(const int input_size_, const int condition_size_, const int head_size_, const int channels_, const int kernel_size_, const std::vector&& dilations_, const std::string activation_, - const bool gated_, const bool head_bias_, const int groups_input) + const bool gated_, const bool head_bias_, const int groups_input, const int groups_condition_, + const int groups_1x1_) : input_size(input_size_) , condition_size(condition_size_) , head_size(head_size_) @@ -89,6 +91,8 @@ class LayerArrayParams , gated(gated_) , head_bias(head_bias_) , groups_input(groups_input) + , groups_condition(groups_condition_) + , groups_1x1(groups_1x1_) { } @@ -102,6 +106,8 @@ class LayerArrayParams const bool gated; const bool head_bias; const int groups_input; + const int groups_condition; + const int groups_1x1; }; // An array of layers with the same channels, kernel sizes, activations. @@ -110,7 +116,7 @@ class _LayerArray public: _LayerArray(const int input_size, const int condition_size, const int head_size, const int channels, const int kernel_size, const std::vector& dilations, const std::string activation, const bool gated, - const bool head_bias, const int groups_input); + const bool head_bias, const int groups_input, const int groups_condition, const int groups_1x1); void SetMaxBufferSize(const int maxBufferSize); diff --git a/tools/test/test_wavenet/test_layer.cpp b/tools/test/test_wavenet/test_layer.cpp index ab814a5..d76c7b4 100644 --- a/tools/test/test_wavenet/test_layer.cpp +++ b/tools/test/test_wavenet/test_layer.cpp @@ -25,8 +25,8 @@ void test_gated() const int groups_input = 1; const int groups_condition = 1; const int groups_1x1 = 1; - auto layer = nam::wavenet::_Layer(conditionSize, channels, kernelSize, dilation, activation, gated, groups_input, - groups_condition, groups_1x1); + auto layer = nam::wavenet::_Layer( + conditionSize, channels, kernelSize, dilation, activation, gated, groups_input, groups_condition, groups_1x1); // Conv, input mixin, 1x1 std::vector weights{ @@ -101,8 +101,8 @@ void test_layer_getters() const int groups_condition = 1; const int groups_1x1 = 1; - auto layer = nam::wavenet::_Layer(conditionSize, channels, kernelSize, dilation, activation, gated, groups_input, - groups_condition, groups_1x1); + auto layer = nam::wavenet::_Layer( + conditionSize, channels, kernelSize, dilation, activation, gated, groups_input, groups_condition, groups_1x1); assert(layer.get_channels() == channels); assert(layer.get_kernel_size() == kernelSize); @@ -122,8 +122,8 @@ void test_non_gated_layer() const int groups_condition = 1; const int groups_1x1 = 1; - auto layer = nam::wavenet::_Layer(conditionSize, channels, kernelSize, dilation, activation, gated, groups_input, - groups_condition, groups_1x1); + auto layer = nam::wavenet::_Layer( + conditionSize, channels, kernelSize, dilation, activation, gated, groups_input, groups_condition, groups_1x1); // For non-gated: conv outputs 1 channel, input_mixin outputs 1 channel, 1x1 outputs 1 channel // Conv: (1,1,1) weight + (1,) bias @@ -189,8 +189,8 @@ void test_layer_activations() const int groups_input = 1; const int groups_condition = 1; const int groups_1x1 = 1; - auto layer = nam::wavenet::_Layer(conditionSize, channels, kernelSize, dilation, "Tanh", gated, groups_input, - groups_condition, groups_1x1); + auto layer = nam::wavenet::_Layer( + conditionSize, channels, kernelSize, dilation, "Tanh", gated, groups_input, groups_condition, groups_1x1); std::vector weights{1.0f, 0.0f, 1.0f, 1.0f, 0.0f}; auto it = weights.begin(); layer.set_weights_(it); @@ -225,8 +225,8 @@ void test_layer_multichannel() const int groups_condition = 1; const int groups_1x1 = 1; - auto layer = nam::wavenet::_Layer(conditionSize, channels, kernelSize, dilation, activation, gated, groups_input, - groups_condition, groups_1x1); + auto layer = nam::wavenet::_Layer( + conditionSize, channels, kernelSize, dilation, activation, gated, groups_input, groups_condition, groups_1x1); assert(layer.get_channels() == channels); diff --git a/tools/test/test_wavenet/test_real_time_safe.cpp b/tools/test/test_wavenet/test_real_time_safe.cpp index 3991164..0307a63 100644 --- a/tools/test/test_wavenet/test_real_time_safe.cpp +++ b/tools/test/test_wavenet/test_real_time_safe.cpp @@ -434,8 +434,11 @@ void test_layer_process_realtime_safe() const std::string activation = "ReLU"; const bool gated = false; const int groups_input = 1; + const int groups_condition = 1; + const int groups_1x1 = 1; - auto layer = nam::wavenet::_Layer(condition_size, channels, kernel_size, dilation, activation, gated, groups_input); + auto layer = nam::wavenet::_Layer( + condition_size, channels, kernel_size, dilation, activation, gated, groups_input, groups_condition, groups_1x1); // Set weights std::vector weights{1.0f, 0.0f, // Conv (weight, bias) @@ -486,8 +489,11 @@ void test_layer_grouped_process_realtime_safe() const std::string activation = "ReLU"; const bool gated = false; const int groups_input = 2; // groups_input > 1 + const int groups_condition = 1; + const int groups_1x1 = 1; - auto layer = nam::wavenet::_Layer(condition_size, channels, kernel_size, dilation, activation, gated, groups_input); + auto layer = nam::wavenet::_Layer( + condition_size, channels, kernel_size, dilation, activation, gated, groups_input, groups_condition, groups_1x1); // Set weights for grouped convolution // With groups_input=2, channels=4: each group has 2 in_channels and 2 out_channels @@ -590,9 +596,11 @@ void test_layer_array_process_realtime_safe() const bool gated = false; const bool head_bias = false; const int groups = 1; + const int groups_condition = 1; + const int groups_1x1 = 1; - auto layer_array = nam::wavenet::_LayerArray( - input_size, condition_size, head_size, channels, kernel_size, dilations, activation, gated, head_bias, groups); + auto layer_array = nam::wavenet::_LayerArray(input_size, condition_size, head_size, channels, kernel_size, dilations, + activation, gated, head_bias, groups, groups_condition, groups_1x1); // Set weights: rechannel(1), layer(conv:1+1, input_mixin:1, 1x1:1+1), head_rechannel(1) std::vector weights{1.0f, // Rechannel @@ -657,14 +665,16 @@ void test_process_realtime_safe() std::vector layer_array_params; // First layer array std::vector dilations1{1}; + const int groups_condition = 1; + const int groups_1x1 = 1; layer_array_params.push_back(nam::wavenet::LayerArrayParams(input_size, condition_size, head_size, channels, kernel_size, std::move(dilations1), activation, gated, - head_bias, groups)); + head_bias, groups, groups_condition, groups_1x1)); // Second layer array (head_size of first must match channels of second) std::vector dilations2{1}; layer_array_params.push_back(nam::wavenet::LayerArrayParams(head_size, condition_size, head_size, channels, kernel_size, std::move(dilations2), activation, gated, - head_bias, groups)); + head_bias, groups, groups_condition, groups_1x1)); // Weights: Array 0: rechannel(1), layer(conv:1+1, input_mixin:1, 1x1:1+1), head_rechannel(1) // Array 1: same structure From 1346a0c1c07580fe26400efa8b6776256e7a5060 Mon Sep 17 00:00:00 2001 From: Steven Atkinson Date: Wed, 14 Jan 2026 17:41:42 -0800 Subject: [PATCH 06/10] [REFINE] Update WaveNet Layer and LayerArray constructors to simplify grouping parameters - Removed the groups_condition parameter from the constructors of _Layer and _LayerArray, streamlining the interface for grouped convolutions. - Updated relevant method calls and test cases to reflect this change, ensuring consistency and clarity in the codebase. - Maintained backward compatibility by defaulting groups_1x1 to 1 where applicable. --- NAM/dsp.cpp | 2 +- NAM/wavenet.cpp | 11 +++++------ NAM/wavenet.h | 12 ++++-------- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/NAM/dsp.cpp b/NAM/dsp.cpp index dc46891..74a8df1 100644 --- a/NAM/dsp.cpp +++ b/NAM/dsp.cpp @@ -311,7 +311,7 @@ Eigen::MatrixXf nam::Conv1x1::process(const Eigen::MatrixXf& input, const int nu void nam::Conv1x1::process_(const Eigen::MatrixXf& input, const int num_frames) { assert(num_frames <= _output.cols()); - + const int numGroups = this->_num_groups; const long in_channels = get_in_channels(); const long out_channels = get_out_channels(); diff --git a/NAM/wavenet.cpp b/NAM/wavenet.cpp index 55c8de5..2a1aba7 100644 --- a/NAM/wavenet.cpp +++ b/NAM/wavenet.cpp @@ -74,13 +74,13 @@ void nam::wavenet::_Layer::Process(const Eigen::MatrixXf& input, const Eigen::Ma nam::wavenet::_LayerArray::_LayerArray(const int input_size, const int condition_size, const int head_size, const int channels, const int kernel_size, const std::vector& dilations, const std::string activation, const bool gated, const bool head_bias, - const int groups_input, const int groups_condition, const int groups_1x1) + const int groups_input, const int groups_1x1) : _rechannel(input_size, channels, false) , _head_rechannel(channels, head_size, head_bias) { for (size_t i = 0; i < dilations.size(); i++) - this->_layers.push_back(_Layer(condition_size, channels, kernel_size, dilations[i], activation, gated, groups_input, - groups_condition, groups_1x1)); + this->_layers.push_back( + _Layer(condition_size, channels, kernel_size, dilations[i], activation, gated, groups_input, groups_1x1)); } void nam::wavenet::_LayerArray::SetMaxBufferSize(const int maxBufferSize) @@ -201,7 +201,7 @@ nam::wavenet::WaveNet::WaveNet(const std::vector layer_array_params[i].input_size, layer_array_params[i].condition_size, layer_array_params[i].head_size, layer_array_params[i].channels, layer_array_params[i].kernel_size, layer_array_params[i].dilations, layer_array_params[i].activation, layer_array_params[i].gated, layer_array_params[i].head_bias, - layer_array_params[i].groups_input, layer_array_params[i].groups_condition, layer_array_params[i].groups_1x1)); + layer_array_params[i].groups_input, layer_array_params[i].groups_1x1)); if (i > 0) if (layer_array_params[i].channels != layer_array_params[i - 1].head_size) { @@ -299,12 +299,11 @@ std::unique_ptr nam::wavenet::Factory(const nlohmann::json& config, st { nlohmann::json layer_config = config["layers"][i]; const int groups = layer_config.value("groups", 1); // defaults to 1 - const int groups_condition = layer_config.value("groups_condition", 1); // defaults to 1 const int groups_1x1 = layer_config.value("groups_1x1", 1); // defaults to 1 layer_array_params.push_back(nam::wavenet::LayerArrayParams( layer_config["input_size"], layer_config["condition_size"], layer_config["head_size"], layer_config["channels"], layer_config["kernel_size"], layer_config["dilations"], layer_config["activation"], layer_config["gated"], - layer_config["head_bias"], groups, groups_condition, groups_1x1)); + layer_config["head_bias"], groups, groups_1x1)); } const bool with_head = !config["head"].is_null(); const float head_scale = config["head_scale"]; diff --git a/NAM/wavenet.h b/NAM/wavenet.h index 55a5c77..976f7e4 100644 --- a/NAM/wavenet.h +++ b/NAM/wavenet.h @@ -17,10 +17,9 @@ class _Layer { public: _Layer(const int condition_size, const int channels, const int kernel_size, const int dilation, - const std::string activation, const bool gated, const int groups_input, const int groups_condition, - const int groups_1x1) + const std::string activation, const bool gated, const int groups_input, const int groups_1x1 = 1) : _conv(channels, gated ? 2 * channels : channels, kernel_size, true, dilation, groups_input) - , _input_mixin(condition_size, gated ? 2 * channels : channels, false, groups_condition) + , _input_mixin(condition_size, gated ? 2 * channels : channels, false) , _1x1(channels, channels, true, groups_1x1) , _activation(activations::Activation::get_activation(activation)) // needs to support activations with parameters , _gated(gated) {}; @@ -79,8 +78,7 @@ class LayerArrayParams public: LayerArrayParams(const int input_size_, const int condition_size_, const int head_size_, const int channels_, const int kernel_size_, const std::vector&& dilations_, const std::string activation_, - const bool gated_, const bool head_bias_, const int groups_input, const int groups_condition_, - const int groups_1x1_) + const bool gated_, const bool head_bias_, const int groups_input, const int groups_1x1_ = 1) : input_size(input_size_) , condition_size(condition_size_) , head_size(head_size_) @@ -91,7 +89,6 @@ class LayerArrayParams , gated(gated_) , head_bias(head_bias_) , groups_input(groups_input) - , groups_condition(groups_condition_) , groups_1x1(groups_1x1_) { } @@ -106,7 +103,6 @@ class LayerArrayParams const bool gated; const bool head_bias; const int groups_input; - const int groups_condition; const int groups_1x1; }; @@ -116,7 +112,7 @@ class _LayerArray public: _LayerArray(const int input_size, const int condition_size, const int head_size, const int channels, const int kernel_size, const std::vector& dilations, const std::string activation, const bool gated, - const bool head_bias, const int groups_input, const int groups_condition, const int groups_1x1); + const bool head_bias, const int groups_input, const int groups_1x1 = 1); void SetMaxBufferSize(const int maxBufferSize); From 19e2c3e41bd87b25a21a4e1e6f573154025eb8c8 Mon Sep 17 00:00:00 2001 From: Steven Atkinson Date: Wed, 14 Jan 2026 17:44:43 -0800 Subject: [PATCH 07/10] [REFINE] Simplify WaveNet Layer and LayerArray tests by removing groups_condition parameter - Removed the groups_condition parameter from various test cases in test_layer, test_layer_array, test_full, and test_real_time_safe. - Updated constructor calls for _Layer and _LayerArray to reflect this change, ensuring consistency across tests. - Enhanced code clarity by streamlining the parameter list in relevant test functions. --- NAM/wavenet.h | 6 ++--- tools/test/test_wavenet/test_full.cpp | 23 +++++----------- tools/test/test_wavenet/test_layer.cpp | 26 ++++++------------- tools/test/test_wavenet/test_layer_array.cpp | 21 +++++++-------- .../test/test_wavenet/test_real_time_safe.cpp | 18 +++++-------- 5 files changed, 34 insertions(+), 60 deletions(-) diff --git a/NAM/wavenet.h b/NAM/wavenet.h index 976f7e4..71d2eff 100644 --- a/NAM/wavenet.h +++ b/NAM/wavenet.h @@ -17,7 +17,7 @@ class _Layer { public: _Layer(const int condition_size, const int channels, const int kernel_size, const int dilation, - const std::string activation, const bool gated, const int groups_input, const int groups_1x1 = 1) + const std::string activation, const bool gated, const int groups_input, const int groups_1x1) : _conv(channels, gated ? 2 * channels : channels, kernel_size, true, dilation, groups_input) , _input_mixin(condition_size, gated ? 2 * channels : channels, false) , _1x1(channels, channels, true, groups_1x1) @@ -78,7 +78,7 @@ class LayerArrayParams public: LayerArrayParams(const int input_size_, const int condition_size_, const int head_size_, const int channels_, const int kernel_size_, const std::vector&& dilations_, const std::string activation_, - const bool gated_, const bool head_bias_, const int groups_input, const int groups_1x1_ = 1) + const bool gated_, const bool head_bias_, const int groups_input, const int groups_1x1_) : input_size(input_size_) , condition_size(condition_size_) , head_size(head_size_) @@ -112,7 +112,7 @@ class _LayerArray public: _LayerArray(const int input_size, const int condition_size, const int head_size, const int channels, const int kernel_size, const std::vector& dilations, const std::string activation, const bool gated, - const bool head_bias, const int groups_input, const int groups_1x1 = 1); + const bool head_bias, const int groups_input, const int groups_1x1); void SetMaxBufferSize(const int maxBufferSize); diff --git a/tools/test/test_wavenet/test_full.cpp b/tools/test/test_wavenet/test_full.cpp index b90419c..a99de2c 100644 --- a/tools/test/test_wavenet/test_full.cpp +++ b/tools/test/test_wavenet/test_full.cpp @@ -27,12 +27,10 @@ void test_wavenet_model() const float head_scale = 1.0f; const bool with_head = false; const int groups = 1; - const int groups_condition = 1; const int groups_1x1 = 1; nam::wavenet::LayerArrayParams params(input_size, condition_size, head_size, channels, kernel_size, - std::move(dilations), activation, gated, head_bias, groups, groups_condition, - groups_1x1); + std::move(dilations), activation, gated, head_bias, groups, groups_1x1); std::vector layer_array_params; layer_array_params.push_back(std::move(params)); @@ -83,20 +81,19 @@ void test_wavenet_multiple_arrays() const float head_scale = 0.5f; const bool with_head = false; const int groups = 1; - const int groups_condition = 1; - const int groups_1x1 = 1; std::vector layer_array_params; // First array std::vector dilations1{1}; + const int groups_1x1 = 1; layer_array_params.push_back(nam::wavenet::LayerArrayParams(input_size, condition_size, head_size, channels, kernel_size, std::move(dilations1), activation, gated, - head_bias, groups, groups_condition, groups_1x1)); + head_bias, groups, groups_1x1)); // Second array (head_size of first must match channels of second) std::vector dilations2{1}; layer_array_params.push_back(nam::wavenet::LayerArrayParams(head_size, condition_size, head_size, channels, kernel_size, std::move(dilations2), activation, gated, - head_bias, groups, groups_condition, groups_1x1)); + head_bias, groups, groups_1x1)); std::vector weights; // Array 0: rechannel, layer, head_rechannel @@ -138,12 +135,10 @@ void test_wavenet_zero_input() const float head_scale = 1.0f; const bool with_head = false; const int groups = 1; - const int groups_condition = 1; const int groups_1x1 = 1; nam::wavenet::LayerArrayParams params(input_size, condition_size, head_size, channels, kernel_size, - std::move(dilations), activation, gated, head_bias, groups, groups_condition, - groups_1x1); + std::move(dilations), activation, gated, head_bias, groups, groups_1x1); std::vector layer_array_params; layer_array_params.push_back(std::move(params)); @@ -181,12 +176,10 @@ void test_wavenet_different_buffer_sizes() const float head_scale = 1.0f; const bool with_head = false; const int groups = 1; - const int groups_condition = 1; const int groups_1x1 = 1; nam::wavenet::LayerArrayParams params(input_size, condition_size, head_size, channels, kernel_size, - std::move(dilations), activation, gated, head_bias, groups, groups_condition, - groups_1x1); + std::move(dilations), activation, gated, head_bias, groups, groups_1x1); std::vector layer_array_params; layer_array_params.push_back(std::move(params)); @@ -225,12 +218,10 @@ void test_wavenet_prewarm() const float head_scale = 1.0f; const bool with_head = false; const int groups = 1; - const int groups_condition = 1; const int groups_1x1 = 1; nam::wavenet::LayerArrayParams params(input_size, condition_size, head_size, channels, kernel_size, - std::move(dilations), activation, gated, head_bias, groups, groups_condition, - groups_1x1); + std::move(dilations), activation, gated, head_bias, groups, groups_1x1); std::vector layer_array_params; layer_array_params.push_back(std::move(params)); diff --git a/tools/test/test_wavenet/test_layer.cpp b/tools/test/test_wavenet/test_layer.cpp index d76c7b4..3a1e071 100644 --- a/tools/test/test_wavenet/test_layer.cpp +++ b/tools/test/test_wavenet/test_layer.cpp @@ -23,10 +23,8 @@ void test_gated() const std::string activation = "ReLU"; const bool gated = true; const int groups_input = 1; - const int groups_condition = 1; const int groups_1x1 = 1; - auto layer = nam::wavenet::_Layer( - conditionSize, channels, kernelSize, dilation, activation, gated, groups_input, groups_condition, groups_1x1); + auto layer = nam::wavenet::_Layer(conditionSize, channels, kernelSize, dilation, activation, gated, groups_input, groups_1x1); // Conv, input mixin, 1x1 std::vector weights{ @@ -98,11 +96,9 @@ void test_layer_getters() const std::string activation = "Tanh"; const bool gated = false; const int groups_input = 1; - - const int groups_condition = 1; const int groups_1x1 = 1; - auto layer = nam::wavenet::_Layer( - conditionSize, channels, kernelSize, dilation, activation, gated, groups_input, groups_condition, groups_1x1); + + auto layer = nam::wavenet::_Layer(conditionSize, channels, kernelSize, dilation, activation, gated, groups_input, groups_1x1); assert(layer.get_channels() == channels); assert(layer.get_kernel_size() == kernelSize); @@ -119,11 +115,9 @@ void test_non_gated_layer() const std::string activation = "ReLU"; const bool gated = false; const int groups_input = 1; - - const int groups_condition = 1; const int groups_1x1 = 1; - auto layer = nam::wavenet::_Layer( - conditionSize, channels, kernelSize, dilation, activation, gated, groups_input, groups_condition, groups_1x1); + + auto layer = nam::wavenet::_Layer(conditionSize, channels, kernelSize, dilation, activation, gated, groups_input, groups_1x1); // For non-gated: conv outputs 1 channel, input_mixin outputs 1 channel, 1x1 outputs 1 channel // Conv: (1,1,1) weight + (1,) bias @@ -187,10 +181,8 @@ void test_layer_activations() // Test Tanh activation { const int groups_input = 1; - const int groups_condition = 1; const int groups_1x1 = 1; - auto layer = nam::wavenet::_Layer( - conditionSize, channels, kernelSize, dilation, "Tanh", gated, groups_input, groups_condition, groups_1x1); + auto layer = nam::wavenet::_Layer(conditionSize, channels, kernelSize, dilation, "Tanh", gated, groups_input, groups_1x1); std::vector weights{1.0f, 0.0f, 1.0f, 1.0f, 0.0f}; auto it = weights.begin(); layer.set_weights_(it); @@ -222,11 +214,9 @@ void test_layer_multichannel() const std::string activation = "ReLU"; const bool gated = false; const int groups_input = 1; - - const int groups_condition = 1; const int groups_1x1 = 1; - auto layer = nam::wavenet::_Layer( - conditionSize, channels, kernelSize, dilation, activation, gated, groups_input, groups_condition, groups_1x1); + + auto layer = nam::wavenet::_Layer(conditionSize, channels, kernelSize, dilation, activation, gated, groups_input, groups_1x1); assert(layer.get_channels() == channels); diff --git a/tools/test/test_wavenet/test_layer_array.cpp b/tools/test/test_wavenet/test_layer_array.cpp index a7acd29..c84a51d 100644 --- a/tools/test/test_wavenet/test_layer_array.cpp +++ b/tools/test/test_wavenet/test_layer_array.cpp @@ -25,11 +25,10 @@ void test_layer_array_basic() const bool gated = false; const bool head_bias = false; const int groups = 1; - - const int groups_condition = 1; const int groups_1x1 = 1; - auto layer_array = nam::wavenet::_LayerArray(input_size, condition_size, head_size, channels, kernel_size, dilations, - activation, gated, head_bias, groups, groups_condition, groups_1x1); + + auto layer_array = nam::wavenet::_LayerArray( + input_size, condition_size, head_size, channels, kernel_size, dilations, activation, gated, head_bias, groups, groups_1x1); const int numFrames = 4; layer_array.SetMaxBufferSize(numFrames); @@ -82,11 +81,10 @@ void test_layer_array_receptive_field() const bool gated = false; const bool head_bias = false; const int groups = 1; - - const int groups_condition = 1; const int groups_1x1 = 1; - auto layer_array = nam::wavenet::_LayerArray(input_size, condition_size, head_size, channels, kernel_size, dilations, - activation, gated, head_bias, groups, groups_condition, groups_1x1); + + auto layer_array = nam::wavenet::_LayerArray( + input_size, condition_size, head_size, channels, kernel_size, dilations, activation, gated, head_bias, groups, groups_1x1); long rf = layer_array.get_receptive_field(); // Expected: sum of dilation * (kernel_size - 1) for each layer @@ -111,11 +109,10 @@ void test_layer_array_with_head_input() const bool gated = false; const bool head_bias = false; const int groups = 1; - - const int groups_condition = 1; const int groups_1x1 = 1; - auto layer_array = nam::wavenet::_LayerArray(input_size, condition_size, head_size, channels, kernel_size, dilations, - activation, gated, head_bias, groups, groups_condition, groups_1x1); + + auto layer_array = nam::wavenet::_LayerArray( + input_size, condition_size, head_size, channels, kernel_size, dilations, activation, gated, head_bias, groups, groups_1x1); const int numFrames = 2; layer_array.SetMaxBufferSize(numFrames); diff --git a/tools/test/test_wavenet/test_real_time_safe.cpp b/tools/test/test_wavenet/test_real_time_safe.cpp index 0307a63..7083c76 100644 --- a/tools/test/test_wavenet/test_real_time_safe.cpp +++ b/tools/test/test_wavenet/test_real_time_safe.cpp @@ -434,11 +434,10 @@ void test_layer_process_realtime_safe() const std::string activation = "ReLU"; const bool gated = false; const int groups_input = 1; - const int groups_condition = 1; const int groups_1x1 = 1; - auto layer = nam::wavenet::_Layer( - condition_size, channels, kernel_size, dilation, activation, gated, groups_input, groups_condition, groups_1x1); + auto layer = + nam::wavenet::_Layer(condition_size, channels, kernel_size, dilation, activation, gated, groups_input, groups_1x1); // Set weights std::vector weights{1.0f, 0.0f, // Conv (weight, bias) @@ -489,11 +488,10 @@ void test_layer_grouped_process_realtime_safe() const std::string activation = "ReLU"; const bool gated = false; const int groups_input = 2; // groups_input > 1 - const int groups_condition = 1; const int groups_1x1 = 1; - auto layer = nam::wavenet::_Layer( - condition_size, channels, kernel_size, dilation, activation, gated, groups_input, groups_condition, groups_1x1); + auto layer = + nam::wavenet::_Layer(condition_size, channels, kernel_size, dilation, activation, gated, groups_input, groups_1x1); // Set weights for grouped convolution // With groups_input=2, channels=4: each group has 2 in_channels and 2 out_channels @@ -596,11 +594,10 @@ void test_layer_array_process_realtime_safe() const bool gated = false; const bool head_bias = false; const int groups = 1; - const int groups_condition = 1; const int groups_1x1 = 1; auto layer_array = nam::wavenet::_LayerArray(input_size, condition_size, head_size, channels, kernel_size, dilations, - activation, gated, head_bias, groups, groups_condition, groups_1x1); + activation, gated, head_bias, groups, groups_1x1); // Set weights: rechannel(1), layer(conv:1+1, input_mixin:1, 1x1:1+1), head_rechannel(1) std::vector weights{1.0f, // Rechannel @@ -665,16 +662,15 @@ void test_process_realtime_safe() std::vector layer_array_params; // First layer array std::vector dilations1{1}; - const int groups_condition = 1; const int groups_1x1 = 1; layer_array_params.push_back(nam::wavenet::LayerArrayParams(input_size, condition_size, head_size, channels, kernel_size, std::move(dilations1), activation, gated, - head_bias, groups, groups_condition, groups_1x1)); + head_bias, groups, groups_1x1)); // Second layer array (head_size of first must match channels of second) std::vector dilations2{1}; layer_array_params.push_back(nam::wavenet::LayerArrayParams(head_size, condition_size, head_size, channels, kernel_size, std::move(dilations2), activation, gated, - head_bias, groups, groups_condition, groups_1x1)); + head_bias, groups, groups_1x1)); // Weights: Array 0: rechannel(1), layer(conv:1+1, input_mixin:1, 1x1:1+1), head_rechannel(1) // Array 1: same structure From 3ce2ab9cd424b69e3034d2c023235faacfcdf24d Mon Sep 17 00:00:00 2001 From: Steven Atkinson Date: Wed, 14 Jan 2026 17:45:07 -0800 Subject: [PATCH 08/10] Formatting --- NAM/dsp.cpp | 2 +- tools/test/test_wavenet/test_full.cpp | 8 ++++---- tools/test/test_wavenet/test_layer.cpp | 15 ++++++++++----- tools/test/test_wavenet/test_layer_array.cpp | 12 ++++++------ 4 files changed, 21 insertions(+), 16 deletions(-) diff --git a/NAM/dsp.cpp b/NAM/dsp.cpp index 74a8df1..dc46891 100644 --- a/NAM/dsp.cpp +++ b/NAM/dsp.cpp @@ -311,7 +311,7 @@ Eigen::MatrixXf nam::Conv1x1::process(const Eigen::MatrixXf& input, const int nu void nam::Conv1x1::process_(const Eigen::MatrixXf& input, const int num_frames) { assert(num_frames <= _output.cols()); - + const int numGroups = this->_num_groups; const long in_channels = get_in_channels(); const long out_channels = get_out_channels(); diff --git a/tools/test/test_wavenet/test_full.cpp b/tools/test/test_wavenet/test_full.cpp index a99de2c..3d20679 100644 --- a/tools/test/test_wavenet/test_full.cpp +++ b/tools/test/test_wavenet/test_full.cpp @@ -30,7 +30,7 @@ void test_wavenet_model() const int groups_1x1 = 1; nam::wavenet::LayerArrayParams params(input_size, condition_size, head_size, channels, kernel_size, - std::move(dilations), activation, gated, head_bias, groups, groups_1x1); + std::move(dilations), activation, gated, head_bias, groups, groups_1x1); std::vector layer_array_params; layer_array_params.push_back(std::move(params)); @@ -138,7 +138,7 @@ void test_wavenet_zero_input() const int groups_1x1 = 1; nam::wavenet::LayerArrayParams params(input_size, condition_size, head_size, channels, kernel_size, - std::move(dilations), activation, gated, head_bias, groups, groups_1x1); + std::move(dilations), activation, gated, head_bias, groups, groups_1x1); std::vector layer_array_params; layer_array_params.push_back(std::move(params)); @@ -179,7 +179,7 @@ void test_wavenet_different_buffer_sizes() const int groups_1x1 = 1; nam::wavenet::LayerArrayParams params(input_size, condition_size, head_size, channels, kernel_size, - std::move(dilations), activation, gated, head_bias, groups, groups_1x1); + std::move(dilations), activation, gated, head_bias, groups, groups_1x1); std::vector layer_array_params; layer_array_params.push_back(std::move(params)); @@ -221,7 +221,7 @@ void test_wavenet_prewarm() const int groups_1x1 = 1; nam::wavenet::LayerArrayParams params(input_size, condition_size, head_size, channels, kernel_size, - std::move(dilations), activation, gated, head_bias, groups, groups_1x1); + std::move(dilations), activation, gated, head_bias, groups, groups_1x1); std::vector layer_array_params; layer_array_params.push_back(std::move(params)); diff --git a/tools/test/test_wavenet/test_layer.cpp b/tools/test/test_wavenet/test_layer.cpp index 3a1e071..10eccf4 100644 --- a/tools/test/test_wavenet/test_layer.cpp +++ b/tools/test/test_wavenet/test_layer.cpp @@ -24,7 +24,8 @@ void test_gated() const bool gated = true; const int groups_input = 1; const int groups_1x1 = 1; - auto layer = nam::wavenet::_Layer(conditionSize, channels, kernelSize, dilation, activation, gated, groups_input, groups_1x1); + auto layer = + nam::wavenet::_Layer(conditionSize, channels, kernelSize, dilation, activation, gated, groups_input, groups_1x1); // Conv, input mixin, 1x1 std::vector weights{ @@ -98,7 +99,8 @@ void test_layer_getters() const int groups_input = 1; const int groups_1x1 = 1; - auto layer = nam::wavenet::_Layer(conditionSize, channels, kernelSize, dilation, activation, gated, groups_input, groups_1x1); + auto layer = + nam::wavenet::_Layer(conditionSize, channels, kernelSize, dilation, activation, gated, groups_input, groups_1x1); assert(layer.get_channels() == channels); assert(layer.get_kernel_size() == kernelSize); @@ -117,7 +119,8 @@ void test_non_gated_layer() const int groups_input = 1; const int groups_1x1 = 1; - auto layer = nam::wavenet::_Layer(conditionSize, channels, kernelSize, dilation, activation, gated, groups_input, groups_1x1); + auto layer = + nam::wavenet::_Layer(conditionSize, channels, kernelSize, dilation, activation, gated, groups_input, groups_1x1); // For non-gated: conv outputs 1 channel, input_mixin outputs 1 channel, 1x1 outputs 1 channel // Conv: (1,1,1) weight + (1,) bias @@ -182,7 +185,8 @@ void test_layer_activations() { const int groups_input = 1; const int groups_1x1 = 1; - auto layer = nam::wavenet::_Layer(conditionSize, channels, kernelSize, dilation, "Tanh", gated, groups_input, groups_1x1); + auto layer = + nam::wavenet::_Layer(conditionSize, channels, kernelSize, dilation, "Tanh", gated, groups_input, groups_1x1); std::vector weights{1.0f, 0.0f, 1.0f, 1.0f, 0.0f}; auto it = weights.begin(); layer.set_weights_(it); @@ -216,7 +220,8 @@ void test_layer_multichannel() const int groups_input = 1; const int groups_1x1 = 1; - auto layer = nam::wavenet::_Layer(conditionSize, channels, kernelSize, dilation, activation, gated, groups_input, groups_1x1); + auto layer = + nam::wavenet::_Layer(conditionSize, channels, kernelSize, dilation, activation, gated, groups_input, groups_1x1); assert(layer.get_channels() == channels); diff --git a/tools/test/test_wavenet/test_layer_array.cpp b/tools/test/test_wavenet/test_layer_array.cpp index c84a51d..41c435a 100644 --- a/tools/test/test_wavenet/test_layer_array.cpp +++ b/tools/test/test_wavenet/test_layer_array.cpp @@ -27,8 +27,8 @@ void test_layer_array_basic() const int groups = 1; const int groups_1x1 = 1; - auto layer_array = nam::wavenet::_LayerArray( - input_size, condition_size, head_size, channels, kernel_size, dilations, activation, gated, head_bias, groups, groups_1x1); + auto layer_array = nam::wavenet::_LayerArray(input_size, condition_size, head_size, channels, kernel_size, dilations, + activation, gated, head_bias, groups, groups_1x1); const int numFrames = 4; layer_array.SetMaxBufferSize(numFrames); @@ -83,8 +83,8 @@ void test_layer_array_receptive_field() const int groups = 1; const int groups_1x1 = 1; - auto layer_array = nam::wavenet::_LayerArray( - input_size, condition_size, head_size, channels, kernel_size, dilations, activation, gated, head_bias, groups, groups_1x1); + auto layer_array = nam::wavenet::_LayerArray(input_size, condition_size, head_size, channels, kernel_size, dilations, + activation, gated, head_bias, groups, groups_1x1); long rf = layer_array.get_receptive_field(); // Expected: sum of dilation * (kernel_size - 1) for each layer @@ -111,8 +111,8 @@ void test_layer_array_with_head_input() const int groups = 1; const int groups_1x1 = 1; - auto layer_array = nam::wavenet::_LayerArray( - input_size, condition_size, head_size, channels, kernel_size, dilations, activation, gated, head_bias, groups, groups_1x1); + auto layer_array = nam::wavenet::_LayerArray(input_size, condition_size, head_size, channels, kernel_size, dilations, + activation, gated, head_bias, groups, groups_1x1); const int numFrames = 2; layer_array.SetMaxBufferSize(numFrames); From 94457ae5545f73ae847490353047a42d099d2bee Mon Sep 17 00:00:00 2001 From: Steven Atkinson Date: Wed, 14 Jan 2026 17:47:11 -0800 Subject: [PATCH 09/10] Update grouped convolution parameters in WaveNet tests - Increased groups_1x1 from 1 to 2 in test_real_time_safe.cpp to support grouped processing. - Enhanced comments to clarify weight initialization for grouped convolutions, detailing the identity matrix setup for each group. - Ensured consistency in the test setup for grouped convolution scenarios, improving clarity and maintainability. --- .../test/test_wavenet/test_real_time_safe.cpp | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/tools/test/test_wavenet/test_real_time_safe.cpp b/tools/test/test_wavenet/test_real_time_safe.cpp index 7083c76..843d0e4 100644 --- a/tools/test/test_wavenet/test_real_time_safe.cpp +++ b/tools/test/test_wavenet/test_real_time_safe.cpp @@ -488,7 +488,7 @@ void test_layer_grouped_process_realtime_safe() const std::string activation = "ReLU"; const bool gated = false; const int groups_input = 2; // groups_input > 1 - const int groups_1x1 = 1; + const int groups_1x1 = 2; // 1x1 is also grouped auto layer = nam::wavenet::_Layer(condition_size, channels, kernel_size, dilation, activation, gated, groups_input, groups_1x1); @@ -529,16 +529,20 @@ void test_layer_grouped_process_realtime_safe() weights.push_back(1.0f); weights.push_back(1.0f); weights.push_back(1.0f); - // 1x1: (channels, channels) = (4, 4) weights + (4,) bias - // Identity matrix - for (int i = 0; i < 4; i++) - { - for (int j = 0; j < 4; j++) - { - weights.push_back((i == j) ? 1.0f : 0.0f); - } - } - // 1x1 bias: zeros + // 1x1: grouped with groups_1x1=2, channels=4 + // Each group processes 2 channels: Group 0 (channels 0-1), Group 1 (channels 2-3) + // Weight layout: for each group g, for each (out_ch, in_ch) in that group + // Group 0: identity matrix for channels 0-1 (2x2) + weights.push_back(1.0f); // out_ch=0, in_ch=0 + weights.push_back(0.0f); // out_ch=0, in_ch=1 + weights.push_back(0.0f); // out_ch=1, in_ch=0 + weights.push_back(1.0f); // out_ch=1, in_ch=1 + // Group 1: identity matrix for channels 2-3 (2x2) + weights.push_back(1.0f); // out_ch=2, in_ch=2 + weights.push_back(0.0f); // out_ch=2, in_ch=3 + weights.push_back(0.0f); // out_ch=3, in_ch=2 + weights.push_back(1.0f); // out_ch=3, in_ch=3 + // 1x1 bias: 4 values (one per output channel) weights.push_back(0.0f); weights.push_back(0.0f); weights.push_back(0.0f); From 47c37574a5ff0f94ae05627aa74c327e828e8380 Mon Sep 17 00:00:00 2001 From: Steven Atkinson Date: Wed, 14 Jan 2026 17:48:14 -0800 Subject: [PATCH 10/10] Formatting --- tools/test/test_wavenet/test_real_time_safe.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/test/test_wavenet/test_real_time_safe.cpp b/tools/test/test_wavenet/test_real_time_safe.cpp index 843d0e4..a7a5e8f 100644 --- a/tools/test/test_wavenet/test_real_time_safe.cpp +++ b/tools/test/test_wavenet/test_real_time_safe.cpp @@ -488,7 +488,7 @@ void test_layer_grouped_process_realtime_safe() const std::string activation = "ReLU"; const bool gated = false; const int groups_input = 2; // groups_input > 1 - const int groups_1x1 = 2; // 1x1 is also grouped + const int groups_1x1 = 2; // 1x1 is also grouped auto layer = nam::wavenet::_Layer(condition_size, channels, kernel_size, dilation, activation, gated, groups_input, groups_1x1);