fix: error handling in opening of external files (neural_network.cpp). (#1044)

* Error Handdling of Files

* exit -> std::exit

* Improved Overall Error handling and reporting

* test commit 

added spaces in include to try to trigger GitHub Actions

* updating DIRECTORY.md

* clang-format and clang-tidy fixes for 51e943d0

Co-authored-by: Krishna Vedala <7001608+kvedala@users.noreply.github.com>
Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
This commit is contained in:
Deep Raval 2020-08-26 23:26:31 +05:30 committed by GitHub
parent c4f586b6ab
commit 44f4f4d6c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 970 additions and 896 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +1,21 @@
/**
* @file vector_ops.hpp
* @author [Deep Raval](https://github.com/imdeep2905)
*
* @brief Various functions for vectors associated with [NeuralNetwork (aka Multilayer Perceptron)]
*
* @brief Various functions for vectors associated with [NeuralNetwork (aka
* Multilayer Perceptron)]
* (https://en.wikipedia.org/wiki/Multilayer_perceptron).
*
*
*/
#ifndef VECTOR_OPS_FOR_NN
#define VECTOR_OPS_FOR_NN
#include <iostream>
#include <algorithm>
#include <vector>
#include <valarray>
#include <chrono>
#include <iostream>
#include <random>
#include <valarray>
#include <vector>
/**
* @namespace machine_learning
@ -31,18 +32,18 @@ template <typename T>
std::ostream &operator<<(std::ostream &out,
std::vector<std::valarray<T>> const &A) {
// Setting output precision to 4 in case of floating point numbers
out.precision(4);
for(const auto &a : A) { // For each row in A
for(const auto &x : a) { // For each element in row
std::cerr << x << ' '; // print element
out.precision(4);
for (const auto &a : A) { // For each row in A
for (const auto &x : a) { // For each element in row
std::cout << x << ' '; // print element
}
std::cerr << std::endl;
std::cout << std::endl;
}
return out;
}
/**
* Overloaded operator "<<" to print a pair
* Overloaded operator "<<" to print a pair
* @tparam T typename of the pair
* @param out std::ostream to output
* @param A Pair to be printed
@ -52,7 +53,7 @@ std::ostream &operator<<(std::ostream &out, const std::pair<T, T> &A) {
// Setting output precision to 4 in case of floating point numbers
out.precision(4);
// printing pair in the form (p, q)
std::cerr << "(" << A.first << ", " << A.second << ")";
std::cout << "(" << A.first << ", " << A.second << ")";
return out;
}
@ -66,10 +67,10 @@ template <typename T>
std::ostream &operator<<(std::ostream &out, const std::valarray<T> &A) {
// Setting output precision to 4 in case of floating point numbers
out.precision(4);
for(const auto &a : A) { // For every element in the vector.
std::cerr << a << ' '; // Print element
for (const auto &a : A) { // For every element in the vector.
std::cout << a << ' '; // Print element
}
std::cerr << std::endl;
std::cout << std::endl;
return out;
}
@ -81,14 +82,14 @@ std::ostream &operator<<(std::ostream &out, const std::valarray<T> &A) {
* @return new resultant vector
*/
template <typename T>
std::valarray<T> insert_element(const std::valarray <T> &A, const T &ele) {
std::valarray <T> B; // New 1D vector to store resultant vector
B.resize(A.size() + 1); // Resizing it accordingly
for(size_t i = 0; i < A.size(); i++) { // For every element in A
B[i] = A[i]; // Copy element in B
std::valarray<T> insert_element(const std::valarray<T> &A, const T &ele) {
std::valarray<T> B; // New 1D vector to store resultant vector
B.resize(A.size() + 1); // Resizing it accordingly
for (size_t i = 0; i < A.size(); i++) { // For every element in A
B[i] = A[i]; // Copy element in B
}
B[B.size() - 1] = ele; // Inserting new element in last position
return B; // Return resultant vector
B[B.size() - 1] = ele; // Inserting new element in last position
return B; // Return resultant vector
}
/**
@ -98,13 +99,14 @@ std::valarray<T> insert_element(const std::valarray <T> &A, const T &ele) {
* @return new resultant vector
*/
template <typename T>
std::valarray <T> pop_front(const std::valarray<T> &A) {
std::valarray <T> B; // New 1D vector to store resultant vector
B.resize(A.size() - 1); // Resizing it accordingly
for(size_t i = 1; i < A.size(); i ++) { // // For every (except first) element in A
B[i - 1] = A[i]; // Copy element in B with left shifted position
std::valarray<T> pop_front(const std::valarray<T> &A) {
std::valarray<T> B; // New 1D vector to store resultant vector
B.resize(A.size() - 1); // Resizing it accordingly
for (size_t i = 1; i < A.size();
i++) { // // For every (except first) element in A
B[i - 1] = A[i]; // Copy element in B with left shifted position
}
return B; // Return resultant vector
return B; // Return resultant vector
}
/**
@ -114,13 +116,14 @@ std::valarray <T> pop_front(const std::valarray<T> &A) {
* @return new resultant vector
*/
template <typename T>
std::valarray <T> pop_back(const std::valarray<T> &A) {
std::valarray <T> B; // New 1D vector to store resultant vector
B.resize(A.size() - 1); // Resizing it accordingly
for(size_t i = 0; i < A.size() - 1; i ++) { // For every (except last) element in A
B[i] = A[i]; // Copy element in B
std::valarray<T> pop_back(const std::valarray<T> &A) {
std::valarray<T> B; // New 1D vector to store resultant vector
B.resize(A.size() - 1); // Resizing it accordingly
for (size_t i = 0; i < A.size() - 1;
i++) { // For every (except last) element in A
B[i] = A[i]; // Copy element in B
}
return B; // Return resultant vector
return B; // Return resultant vector
}
/**
@ -130,16 +133,17 @@ std::valarray <T> pop_back(const std::valarray<T> &A) {
* @param B Second 3D vector
*/
template <typename T>
void equal_shuffle(std::vector < std::vector <std::valarray<T>> > &A,
std::vector < std::vector <std::valarray<T>> > &B) {
void equal_shuffle(std::vector<std::vector<std::valarray<T>>> &A,
std::vector<std::vector<std::valarray<T>>> &B) {
// If two vectors have different sizes
if(A.size() != B.size())
{
std::cerr << "ERROR : Can not equally shuffle two vectors with different sizes: ";
if (A.size() != B.size()) {
std::cerr << "ERROR (" << __func__ << ") : ";
std::cerr
<< "Can not equally shuffle two vectors with different sizes: ";
std::cerr << A.size() << " and " << B.size() << std::endl;
std::exit(EXIT_FAILURE);
}
for(size_t i = 0; i < A.size(); i++) { // For every element in A and B
for (size_t i = 0; i < A.size(); i++) { // For every element in A and B
// Genrating random index < size of A and B
std::srand(std::chrono::system_clock::now().time_since_epoch().count());
size_t random_index = std::rand() % A.size();
@ -159,26 +163,26 @@ void equal_shuffle(std::vector < std::vector <std::valarray<T>> > &A,
* @param high upper limit on value
*/
template <typename T>
void uniform_random_initialization(std::vector<std::valarray<T>> &A,
const std::pair<size_t, size_t> &shape,
const T &low,
const T &high) {
A.clear(); // Making A empty
void uniform_random_initialization(std::vector<std::valarray<T>> &A,
const std::pair<size_t, size_t> &shape,
const T &low, const T &high) {
A.clear(); // Making A empty
// Uniform distribution in range [low, high]
std::default_random_engine generator(std::chrono::system_clock::now().time_since_epoch().count());
std::uniform_real_distribution <T> distribution(low, high);
for(size_t i = 0; i < shape.first; i++) { // For every row
std::valarray <T> row; // Making empty row which will be inserted in vector
std::default_random_engine generator(
std::chrono::system_clock::now().time_since_epoch().count());
std::uniform_real_distribution<T> distribution(low, high);
for (size_t i = 0; i < shape.first; i++) { // For every row
std::valarray<T>
row; // Making empty row which will be inserted in vector
row.resize(shape.second);
for(auto &r : row) { // For every element in row
r = distribution(generator); // copy random number
}
A.push_back(row); // Insert new row in vector
for (auto &r : row) { // For every element in row
r = distribution(generator); // copy random number
}
A.push_back(row); // Insert new row in vector
}
return;
}
/**
* Function to Intialize 2D vector as unit matrix
* @tparam T typename of the vector
@ -186,15 +190,15 @@ void uniform_random_initialization(std::vector<std::valarray<T>> &A,
* @param shape required shape
*/
template <typename T>
void unit_matrix_initialization(std::vector<std::valarray<T>> &A,
const std::pair<size_t, size_t> &shape
) {
A.clear(); // Making A empty
for(size_t i = 0; i < shape.first; i++) {
std::valarray <T> row; // Making empty row which will be inserted in vector
void unit_matrix_initialization(std::vector<std::valarray<T>> &A,
const std::pair<size_t, size_t> &shape) {
A.clear(); // Making A empty
for (size_t i = 0; i < shape.first; i++) {
std::valarray<T>
row; // Making empty row which will be inserted in vector
row.resize(shape.second);
row[i] = T(1); // Insert 1 at ith position
A.push_back(row); // Insert new row in vector
row[i] = T(1); // Insert 1 at ith position
A.push_back(row); // Insert new row in vector
}
return;
}
@ -206,14 +210,14 @@ void unit_matrix_initialization(std::vector<std::valarray<T>> &A,
* @param shape required shape
*/
template <typename T>
void zeroes_initialization(std::vector<std::valarray<T>> &A,
const std::pair<size_t, size_t> &shape
) {
A.clear(); // Making A empty
for(size_t i = 0; i < shape.first; i++) {
std::valarray <T> row; // Making empty row which will be inserted in vector
row.resize(shape.second); // By default all elements are zero
A.push_back(row); // Insert new row in vector
void zeroes_initialization(std::vector<std::valarray<T>> &A,
const std::pair<size_t, size_t> &shape) {
A.clear(); // Making A empty
for (size_t i = 0; i < shape.first; i++) {
std::valarray<T>
row; // Making empty row which will be inserted in vector
row.resize(shape.second); // By default all elements are zero
A.push_back(row); // Insert new row in vector
}
return;
}
@ -226,11 +230,11 @@ void zeroes_initialization(std::vector<std::valarray<T>> &A,
*/
template <typename T>
T sum(const std::vector<std::valarray<T>> &A) {
T cur_sum = 0; // Initially sum is zero
for(const auto &a : A) { // For every row in A
cur_sum += a.sum(); // Add sum of that row to current sum
T cur_sum = 0; // Initially sum is zero
for (const auto &a : A) { // For every row in A
cur_sum += a.sum(); // Add sum of that row to current sum
}
return cur_sum; // Return sum
return cur_sum; // Return sum
}
/**
@ -242,52 +246,59 @@ T sum(const std::vector<std::valarray<T>> &A) {
template <typename T>
std::pair<size_t, size_t> get_shape(const std::vector<std::valarray<T>> &A) {
const size_t sub_size = (*A.begin()).size();
for(const auto &a : A) {
for (const auto &a : A) {
// If supplied vector don't have same shape in all rows
if(a.size() != sub_size) {
std::cerr << "ERROR: (get_shape) Supplied vector is not 2D Matrix" << std::endl;
if (a.size() != sub_size) {
std::cerr << "ERROR (" << __func__ << ") : ";
std::cerr << "Supplied vector is not 2D Matrix" << std::endl;
std::exit(EXIT_FAILURE);
}
}
return std::make_pair(A.size(), sub_size); // Return shape as pair
return std::make_pair(A.size(), sub_size); // Return shape as pair
}
/**
* Function to scale given 3D vector using min-max scaler
* @tparam T typename of the vector
* @param A 3D vector which will be scaled
* @param low new minimum value
* @param low new minimum value
* @param high new maximum value
* @return new scaled 3D vector
*/
template <typename T>
std::vector<std::vector<std::valarray<T>>>
minmax_scaler(const std::vector<std::vector<std::valarray<T>>> &A, const T &low, const T &high) {
std::vector<std::vector<std::valarray<T>>> B = A; // Copying into new vector B
const auto shape = get_shape(B[0]); // Storing shape of B's every element
// As this function is used for scaling training data vector should be of shape (1, X)
if(shape.first != 1) {
std::cerr << "ERROR: (MinMax Scaling) Supplied vector is not supported for minmax scaling, shape: ";
std::vector<std::vector<std::valarray<T>>> minmax_scaler(
const std::vector<std::vector<std::valarray<T>>> &A, const T &low,
const T &high) {
std::vector<std::vector<std::valarray<T>>> B =
A; // Copying into new vector B
const auto shape = get_shape(B[0]); // Storing shape of B's every element
// As this function is used for scaling training data vector should be of
// shape (1, X)
if (shape.first != 1) {
std::cerr << "ERROR (" << __func__ << ") : ";
std::cerr
<< "Supplied vector is not supported for minmax scaling, shape: ";
std::cerr << shape << std::endl;
std::exit(EXIT_FAILURE);
}
for(size_t i = 0; i < shape.second; i++) {
T min = B[0][0][i], max = B[0][0][i];
for(size_t j = 0; j < B.size(); j++) {
for (size_t i = 0; i < shape.second; i++) {
T min = B[0][0][i], max = B[0][0][i];
for (size_t j = 0; j < B.size(); j++) {
// Updating minimum and maximum values
min = std::min(min, B[j][0][i]);
max = std::max(max, B[j][0][i]);
}
for(size_t j = 0; j < B.size(); j++) {
for (size_t j = 0; j < B.size(); j++) {
// Applying min-max scaler formula
B[j][0][i] = ((B[j][0][i] - min) / (max - min)) * (high - low) + low;
B[j][0][i] =
((B[j][0][i] - min) / (max - min)) * (high - low) + low;
}
}
return B; // Return new resultant 3D vector
return B; // Return new resultant 3D vector
}
/**
* Function to get index of maximum element in 2D vector
* Function to get index of maximum element in 2D vector
* @tparam T typename of the vector
* @param A 2D vector for which maximum index is required
* @return index of maximum element
@ -295,13 +306,16 @@ minmax_scaler(const std::vector<std::vector<std::valarray<T>>> &A, const T &low,
template <typename T>
size_t argmax(const std::vector<std::valarray<T>> &A) {
const auto shape = get_shape(A);
// As this function is used on predicted (or target) vector, shape should be (1, X)
if(shape.first != 1) {
std::cerr << "ERROR: (argmax) Supplied vector is ineligible for argmax" << std::endl;
std::exit(EXIT_FAILURE);
// As this function is used on predicted (or target) vector, shape should be
// (1, X)
if (shape.first != 1) {
std::cerr << "ERROR (" << __func__ << ") : ";
std::cerr << "Supplied vector is ineligible for argmax" << std::endl;
std::exit(EXIT_FAILURE);
}
// Return distance of max element from first element (i.e. index)
return std::distance(std::begin(A[0]), std::max_element(std::begin(A[0]), std::end(A[0])));
return std::distance(std::begin(A[0]),
std::max_element(std::begin(A[0]), std::end(A[0])));
}
/**
@ -311,46 +325,51 @@ size_t argmax(const std::vector<std::valarray<T>> &A) {
* @param func Function to be applied
* @return new resultant vector
*/
template <typename T>
std::vector <std::valarray <T>> apply_function(const std::vector <std::valarray <T>> &A,
T (*func) (const T &)) {
std::vector<std::valarray<double>> B = A; // New vector to store resultant vector
for(auto &b : B) { // For every row in vector
b = b.apply(func); // Apply function to that row
template <typename T>
std::vector<std::valarray<T>> apply_function(
const std::vector<std::valarray<T>> &A, T (*func)(const T &)) {
std::vector<std::valarray<double>> B =
A; // New vector to store resultant vector
for (auto &b : B) { // For every row in vector
b = b.apply(func); // Apply function to that row
}
return B; // Return new resultant 2D vector
return B; // Return new resultant 2D vector
}
/**
* Overloaded operator "*" to multiply given 2D vector with scaler
* Overloaded operator "*" to multiply given 2D vector with scaler
* @tparam T typename of both vector and the scaler
* @param A 2D vector to which scaler will be multiplied
* @param val Scaler value which will be multiplied
* @return new resultant vector
*/
template <typename T>
std::vector <std::valarray <T> > operator * (const std::vector<std::valarray<T>> &A, const T& val) {
std::vector<std::valarray<double>> B = A; // New vector to store resultant vector
for(auto &b : B) { // For every row in vector
b = b * val; // Multiply row with scaler
std::vector<std::valarray<T>> operator*(const std::vector<std::valarray<T>> &A,
const T &val) {
std::vector<std::valarray<double>> B =
A; // New vector to store resultant vector
for (auto &b : B) { // For every row in vector
b = b * val; // Multiply row with scaler
}
return B; // Return new resultant 2D vector
return B; // Return new resultant 2D vector
}
/**
* Overloaded operator "/" to divide given 2D vector with scaler
* Overloaded operator "/" to divide given 2D vector with scaler
* @tparam T typename of the vector and the scaler
* @param A 2D vector to which scaler will be divided
* @param val Scaler value which will be divided
* @return new resultant vector
*/
template <typename T>
std::vector <std::valarray <T> > operator / (const std::vector<std::valarray<T>> &A, const T& val) {
std::vector<std::valarray<double>> B = A; // New vector to store resultant vector
for(auto &b : B) { // For every row in vector
b = b / val; // Divide row with scaler
std::vector<std::valarray<T>> operator/(const std::vector<std::valarray<T>> &A,
const T &val) {
std::vector<std::valarray<double>> B =
A; // New vector to store resultant vector
for (auto &b : B) { // For every row in vector
b = b / val; // Divide row with scaler
}
return B; // Return new resultant 2D vector
return B; // Return new resultant 2D vector
}
/**
@ -360,125 +379,136 @@ std::vector <std::valarray <T> > operator / (const std::vector<std::valarray<T>>
* @return new resultant vector
*/
template <typename T>
std::vector <std::valarray <T>> transpose(const std::vector<std::valarray<T>> &A) {
const auto shape = get_shape(A); // Current shape of vector
std::vector <std::valarray <T> > B; // New vector to store result
std::vector<std::valarray<T>> transpose(
const std::vector<std::valarray<T>> &A) {
const auto shape = get_shape(A); // Current shape of vector
std::vector<std::valarray<T>> B; // New vector to store result
// Storing transpose values of A in B
for(size_t j = 0; j < shape.second; j++) {
std::valarray <T> row;
for (size_t j = 0; j < shape.second; j++) {
std::valarray<T> row;
row.resize(shape.first);
for(size_t i = 0; i < shape.first; i++) {
for (size_t i = 0; i < shape.first; i++) {
row[i] = A[i][j];
}
B.push_back(row);
}
return B; // Return new resultant 2D vector
return B; // Return new resultant 2D vector
}
/**
* Overloaded operator "+" to add two 2D vectors
* @tparam T typename of the vector
* @param A First 2D vector
* @param A First 2D vector
* @param B Second 2D vector
* @return new resultant vector
*/
template <typename T>
std::vector <std::valarray <T> > operator + (const std::vector<std::valarray<T>> &A, const std::vector<std::valarray<T>> &B) {
std::vector<std::valarray<T>> operator+(
const std::vector<std::valarray<T>> &A,
const std::vector<std::valarray<T>> &B) {
const auto shape_a = get_shape(A);
const auto shape_b = get_shape(B);
// If vectors don't have equal shape
if(shape_a.first != shape_b.first || shape_a.second != shape_b.second) {
std::cerr << "ERROR: (vector addition) Supplied vectors have different shapes ";
if (shape_a.first != shape_b.first || shape_a.second != shape_b.second) {
std::cerr << "ERROR (" << __func__ << ") : ";
std::cerr << "Supplied vectors have different shapes ";
std::cerr << shape_a << " and " << shape_b << std::endl;
std::exit(EXIT_FAILURE);
}
std::vector<std::valarray <T>> C;
for(size_t i = 0; i < A.size(); i++) { // For every row
C.push_back(A[i] + B[i]); // Elementwise addition
std::vector<std::valarray<T>> C;
for (size_t i = 0; i < A.size(); i++) { // For every row
C.push_back(A[i] + B[i]); // Elementwise addition
}
return C; // Return new resultant 2D vector
return C; // Return new resultant 2D vector
}
/**
* Overloaded operator "-" to add subtract 2D vectors
* @tparam T typename of the vector
* @param A First 2D vector
* @param A First 2D vector
* @param B Second 2D vector
* @return new resultant vector
*/
template <typename T>
std::vector <std::valarray <T>> operator - (const std::vector<std::valarray<T>> &A, const std::vector<std::valarray<T>> &B) {
std::vector<std::valarray<T>> operator-(
const std::vector<std::valarray<T>> &A,
const std::vector<std::valarray<T>> &B) {
const auto shape_a = get_shape(A);
const auto shape_b = get_shape(B);
// If vectors don't have equal shape
if(shape_a.first != shape_b.first || shape_a.second != shape_b.second) {
std::cerr << "ERROR: (vector subtraction) Supplied vectors have different shapes ";
if (shape_a.first != shape_b.first || shape_a.second != shape_b.second) {
std::cerr << "ERROR (" << __func__ << ") : ";
std::cerr << "Supplied vectors have different shapes ";
std::cerr << shape_a << " and " << shape_b << std::endl;
std::exit(EXIT_FAILURE);
}
std::vector<std::valarray<T>> C; // Vector to store result
for(size_t i = 0; i < A.size(); i++) { // For every row
C.push_back(A[i] - B[i]); // Elementwise substraction
std::vector<std::valarray<T>> C; // Vector to store result
for (size_t i = 0; i < A.size(); i++) { // For every row
C.push_back(A[i] - B[i]); // Elementwise substraction
}
return C; // Return new resultant 2D vector
return C; // Return new resultant 2D vector
}
/**
* Function to multiply two 2D vectors
* @tparam T typename of the vector
* @param A First 2D vector
* @param A First 2D vector
* @param B Second 2D vector
* @return new resultant vector
*/
template <typename T>
std::vector <std::valarray <T>> multiply(const std::vector<std::valarray<T>> &A, const std::vector<std::valarray<T>> &B) {
std::vector<std::valarray<T>> multiply(const std::vector<std::valarray<T>> &A,
const std::vector<std::valarray<T>> &B) {
const auto shape_a = get_shape(A);
const auto shape_b = get_shape(B);
// If vectors are not eligible for multiplication
if(shape_a.second != shape_b.first ) {
std::cerr << "ERROR: (multiply) Supplied vectors are not eligible for multiplication ";
if (shape_a.second != shape_b.first) {
std::cerr << "ERROR (" << __func__ << ") : ";
std::cerr << "Vectors are not eligible for multiplication ";
std::cerr << shape_a << " and " << shape_b << std::endl;
std::exit(EXIT_FAILURE);
}
std::vector<std::valarray<T>> C; // Vector to store result
// Normal matrix multiplication
std::vector<std::valarray<T>> C; // Vector to store result
// Normal matrix multiplication
for (size_t i = 0; i < shape_a.first; i++) {
std::valarray<T> row;
row.resize(shape_b.second);
for(size_t j = 0; j < shape_b.second; j++) {
for(size_t k = 0; k < shape_a.second; k++) {
for (size_t j = 0; j < shape_b.second; j++) {
for (size_t k = 0; k < shape_a.second; k++) {
row[j] += A[i][k] * B[k][j];
}
}
C.push_back(row);
}
return C; // Return new resultant 2D vector
return C; // Return new resultant 2D vector
}
/**
* Function to get hadamard product of two 2D vectors
* @tparam T typename of the vector
* @param A First 2D vector
* @param A First 2D vector
* @param B Second 2D vector
* @return new resultant vector
*/
template <typename T>
std::vector <std::valarray <T>> hadamard_product(const std::vector<std::valarray<T>> &A, const std::vector<std::valarray<T>> &B) {
std::vector<std::valarray<T>> hadamard_product(
const std::vector<std::valarray<T>> &A,
const std::vector<std::valarray<T>> &B) {
const auto shape_a = get_shape(A);
const auto shape_b = get_shape(B);
// If vectors are not eligible for hadamard product
if(shape_a.first != shape_b.first || shape_a.second != shape_b.second) {
std::cerr << "ERROR: (hadamard_product) Supplied vectors have different shapes ";
if (shape_a.first != shape_b.first || shape_a.second != shape_b.second) {
std::cerr << "ERROR (" << __func__ << ") : ";
std::cerr << "Vectors have different shapes ";
std::cerr << shape_a << " and " << shape_b << std::endl;
std::exit(EXIT_FAILURE);
}
std::vector<std::valarray<T>> C; // Vector to store result
for(size_t i = 0; i < A.size(); i++) {
C.push_back(A[i] * B[i]); // Elementwise multiplication
std::vector<std::valarray<T>> C; // Vector to store result
for (size_t i = 0; i < A.size(); i++) {
C.push_back(A[i] * B[i]); // Elementwise multiplication
}
return C; // Return new resultant 2D vector
return C; // Return new resultant 2D vector
}
} // namespace machine_learning
#endif