From 8c44b969139f6dee9eb8fc29e2781c7cfc8635f2 Mon Sep 17 00:00:00 2001 From: Elias Kohout Date: Fri, 15 Sep 2023 14:06:13 +0200 Subject: [PATCH] Declared functions in tensorfunc which will replace the iterators with a more functional aprroach. --- README.md | 2 + tensor.c | 197 -------------------------------------------- tensor.h | 10 --- tensorarray.h | 4 +- tensorfunc.c | 223 ++++++++++++++++++++++++++++++++++++++++++++++++++ tensorfunc.h | 30 +++++++ tensoriter.c | 153 ---------------------------------- tensoriter.h | 25 ------ 8 files changed, 257 insertions(+), 387 deletions(-) create mode 100644 tensorfunc.c create mode 100644 tensorfunc.h delete mode 100644 tensoriter.c delete mode 100644 tensoriter.h diff --git a/README.md b/README.md index 116134a..84ad0ef 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,8 @@ A simple C library implementing tensors. Still in development. Currently it only ## TODOs +- [ ] Adding DTYPE_COPY macro +- [ ] Make elementwise operations run in parallel - [ ] Making all functions work with random strides - [ ] Adding a function for normalising strides - [ ] Implementing dot product diff --git a/tensor.c b/tensor.c index 1172fe3..6bcc0d0 100644 --- a/tensor.c +++ b/tensor.c @@ -265,203 +265,6 @@ bool tensor_cpy(tensor t1, const tensor t2) return true; } -bool tensor_add_inplace(tensor t1, const tensor t2) -{ - /* Adds the values of t2 onto the values of t1. t1 and t2 need to have the - * same shape. - * - * @param t1 The tensor on which the values of t2 are added - * @param t2 The tensor whose values are added - * - * @return true when successful, false otherwise - */ - assert(!tensor_is_empty(t1)); - assert(!tensor_is_empty(t2)); - - uint32_t i; - - if(t1->rank != t2->rank) return false; - if(!tarray_uint32_equals(t1->size, t2->size, t1->rank)) return false; - for(i = 0; i < t1->num_elem; i++) { - t1->elements[i] = DTYPE_ADD(t1->elements[i], t2->elements[i]); - } - return true; -} - -bool tensor_sub_inplace(tensor t1, const tensor t2) -{ - /* Subtracts the values of t2 from the values of t1. t1 and t2 need to have - * the same shape. - * - * @param t1 The tensor from which the values of t2 are subtracted - * @param t2 The tensor whose values are subtracted - * - * @return true when successful, false otherwise - */ - assert(!tensor_is_empty(t1)); - assert(!tensor_is_empty(t2)); - - uint32_t i; - - if(t1->rank != t2->rank) return false; - if(!tarray_uint32_equals(t1->size, t2->size, t1->rank)) return false; - for(i = 0; i < t1->num_elem; i++) { - t1->elements[i] = DTYPE_SUB(t1->elements[i], t2->elements[i]); - } - return true; -} - -bool tensor_mul_inplace(tensor t1, const tensor t2) -{ - /* Multiplies the values of t2 onto t1 element wise. t1 and t2 need to - * have the same shape. - * - * @param t1 The tensor to multiply onto - * @param t2 The tensor to multiply with - * - * @return true when successful, false otherwise - */ - assert(!tensor_is_empty(t1)); - assert(!tensor_is_empty(t2)); - - uint32_t i; - - if(t1->rank != t2->rank) return false; - if(!tarray_uint32_equals(t1->size, t2->size, t1->rank)) return false; - for(i = 0; i < t1->num_elem; i++) { - t1->elements[i] = DTYPE_MUL(t1->elements[i], t2->elements[i]); - } - return true; -} - -bool tensor_div_inplace(tensor t1, const tensor t2) -{ - /* Divides the values of t2 by the values of t1 element wise. t1 and t2 - * need to have the same shape. - * - * @param t1 The tensor to devide - * @param t2 The tensor to devide by - * - * @return true when successful, false otherwise - */ - assert(!tensor_is_empty(t1)); - assert(!tensor_is_empty(t2)); - - uint32_t i; - - if(t1->rank != t2->rank) return false; - if(!tarray_uint32_equals(t1->size, t2->size, t1->rank)) return false; - for(i = 0; i < t1->num_elem; i++) { - t1->elements[i] = DTYPE_DIV(t1->elements[i], t2->elements[i]); - } - return true; -} - -tensor tensor_add(const tensor t1, const tensor t2) -{ - /* Adds two tensors returning the result as a tensor. t1 and t2 need to - * have the same shape. - * - * @param t1 The first tensor to add - * @param t2 The second tensor to add - * - * @return The result of the operation, NULL if an error occurs - */ - assert(!tensor_is_empty(t1)); - assert(!tensor_is_empty(t2)); - - tensor t3 = tensor_new(); - if(t3 == NULL) return NULL; - if (!tensor_cpy(t3, t1)) { - tensor_destroy(t3); - return NULL; - } - if (!tensor_add_inplace(t3, t2)) { - tensor_destroy(t3); - return NULL; - } - return t3; -} - -tensor tensor_sub(const tensor t1, const tensor t2) -{ - /* Subtracts two tensors returning the result as a tensor. t1 and t2 need - * to have the same shape. - * - * @param t1 The tensor to subtract from - * @param t2 The tensor that is subtracted - * - * @return The result of the operation, NULL if an error occurs - */ - assert(!tensor_is_empty(t1)); - assert(!tensor_is_empty(t2)); - - tensor t3 = tensor_new(); - if(t3 == NULL) return NULL; - if (!tensor_cpy(t3, t1)) { - tensor_destroy(t3); - return NULL; - } - if (!tensor_sub_inplace(t3, t2)) { - tensor_destroy(t3); - return NULL; - } - return t3; -} - - -tensor tensor_mul(const tensor t1, const tensor t2) -{ - /* Multiplies two tensors element wise returning the result as a tensor. - * t1 and t2 need to have the same shape. - * - * @param t1 The first tensor to multipy - * @param t2 The second tensor to multipy - * - * @return The result of the operation, NULL if an error occurs - */ - assert(!tensor_is_empty(t1)); - assert(!tensor_is_empty(t2)); - - tensor t3 = tensor_new(); - if(t3 == NULL) return NULL; - if (!tensor_cpy(t3, t1)) { - tensor_destroy(t3); - return NULL; - } - if (!tensor_mul_inplace(t3, t2)) { - tensor_destroy(t3); - return NULL; - } - return t3; -} - -tensor tensor_div(const tensor t1, const tensor t2) -{ - /* Divides two tensors element wise returning the result as a tensor. t1 - * and t2 need to have the same shape. - * - * @param t1 The dividend - * @param t2 The divisor - * - * @return The result of the operation, NULL if an error occurs - */ - assert(!tensor_is_empty(t1)); - assert(!tensor_is_empty(t2)); - - tensor t3 = tensor_new(); - if(t3 == NULL) return NULL; - if (!tensor_cpy(t3, t1)) { - tensor_destroy(t3); - return NULL; - } - if (!tensor_div_inplace(t3, t2)) { - tensor_destroy(t3); - return NULL; - } - return t3; -} - void tensor_print(const tensor t) { /* Prints a tensor to stdout. diff --git a/tensor.h b/tensor.h index e5a1737..73c3cb0 100644 --- a/tensor.h +++ b/tensor.h @@ -21,7 +21,6 @@ typedef struct _tensor { uint32_t num_elem; } *tensor; - tensor tensor_new(void); void tensor_destroy(tensor t); @@ -39,15 +38,6 @@ bool tensor_init_zero(tensor t, const uint32_t *size, uint8_t rank); bool tensor_init_rand(tensor t, const uint32_t *size, uint8_t rank, dtype max); bool tensor_cpy(tensor t1, const tensor t2); -bool tensor_add_inplace(tensor t1, const tensor t2); -bool tensor_sub_inplace(tensor t1, const tensor t2); -bool tensor_mul_inplace(tensor t1, const tensor t2); -bool tensor_div_inplace(tensor t1, const tensor t2); -tensor tensor_add(const tensor t1, const tensor t2); -tensor tensor_sub(const tensor t1, const tensor t2); -tensor tensor_mul(const tensor t1, const tensor t2); -tensor tensor_div(const tensor t1, const tensor t2); - void tensor_print(const tensor t); #endif diff --git a/tensorarray.h b/tensorarray.h index a71dad4..c26f35a 100644 --- a/tensorarray.h +++ b/tensorarray.h @@ -6,7 +6,7 @@ #include #include "dtype.h" -inline bool tarray_equals(const dtype* a1, const dtype* a2, uint32_t len); -inline bool tarray_uint32_equals(const uint32_t* a1, const uint32_t* a2, uint32_t len); +bool tarray_equals(const dtype* a1, const dtype* a2, uint32_t len); +bool tarray_uint32_equals(const uint32_t* a1, const uint32_t* a2, uint32_t len); #endif // _TENSORARRAY_H_INCLUDED_ diff --git a/tensorfunc.c b/tensorfunc.c new file mode 100644 index 0000000..2190bee --- /dev/null +++ b/tensorfunc.c @@ -0,0 +1,223 @@ +#include "tensorfunc.h" + +void tensor_fill(tensor t, dtype (*func)(void)) +{ + /* Filles a tensor with the values provided by a function. + * + * @param t The tensor to fill + * @param func The function providing the values, it is expected to have + * no sidel effects. + */ + assert(!tensor_is_empty(t)); + // TODO +} + +void tensor_inspect(const tensor t, void (*func)(dtype)) +{ + /* Goes over every element in a tensor and calls a function on it. + * + * @param t The tensor that provides the values + * @param func The function that is called with the values + */ + assert(!tensor_is_empty(t)); + // TODO +} + +tensor tensor_map(const tensor t, dtype (*func)(dtype)) +{ + /* Creates a new tensor in which the result of the given function + * with the values of the given tensor as parameters is stored. + * + * @param t The tensor that provides the values + * @param func The map function that is called, it is expected to have no + * side effects + * + * @return The newly created tensor + */ + assert(!tensor_is_empty(t)); + // TODO +} + +void tensor_map_inplace(tensor t, dtype (*func)(dtype)) +{ + /* Replaces every value in a tensor with the result of the given function + * with the old value as a parameter. + * + * @param t The tensor to operate on + * @param func The map function that is called, it is expected to have no + * side effects + */ + assert(!tensor_is_empty(t)); + // TODO +} + +tensor tensor_combine(const tensor t1, const tensor t2, dtype (*func)(dtype, dtype)) +{ + /* Combines every value of two tensors and stores the result in a third + * tensor. t1 and t2 need to have the same shape. + * + * @param t1 The first tensor + * @param t2 The second tensor + * @param func The function that takes in the values of t1 and t2 and + * returns the result that is stored in the created tensor, it is + * expected to have no side effects + * + * @return The tensor with the result of the combination of t1 and t2 + */ + assert(!tensor_is_empty(t1)); + assert(!tensor_is_empty(t2)); + // TODO +} + +void tensor_combine_inplace(tensor t1, const tensor t2, dtype (*func)(dtype, dtype)) +{ + /* Combines every value of two tensor and stores the result in the first of + * the tensors. t1 and t2 need to have the same shape. + * + * @param t1 The tensor in which to store the result + * @param t2 The second tensor of the operation + * @param func The function that takes in the values of t1 and t2 and + * returns the result that is stored in t1, it is expected to have + * no side effects + */ + assert(!tensor_is_empty(t1)); + assert(!tensor_is_empty(t2)); + // TODO + +} + +void tensor_add_scalar(tensor t, dtype scalar) +{ + /* Adds a fixed scalar value to all the values of a tensor. + * + * @param t The tensor to operate on + * @param scalar The value to add + */ + // TODO +} + +void tensor_sub_scalar(tensor t, dtype scalar) +{ + /* Subtracts a fixed scalar value from all the values of a tensor. + * + * @param t The tensor to operate on + * @param scalar The value to subtract + */ + // TODO +} + +void tensor_mul_scalar(tensor t, dtype scalar) +{ + /* Multiplies a fixed scalar value with all the values of a tensor. + * + * @param t The tensor to operate on + * @param scalar The value to multiply + */ + // TODO +} + +void tensor_div_scalar(tensor t, dtype scalar) +{ + /* Divides all the values of a tensor by a fixed scalar value. + * + * @param t The tensor to operate on + * @param scalar The value to divide by + */ + // TODO +} + +void tensor_add_inplace(tensor t1, const tensor t2) +{ + /* Adds the values of t2 onto the values of t1. t1 and t2 need to have the + * same shape. + * + * @param t1 The tensor on which the values of t2 are added + * @param t2 The tensor whose values are added + */ + // TODO +} + +void tensor_sub_inplace(tensor t1, const tensor t2) +{ + /* Subtracts the values of t2 from the values of t1. t1 and t2 need to have + * the same shape. + * + * @param t1 The tensor from which the values of t2 are subtracted + * @param t2 The tensor whose values are subtracted + */ + // TODO +} + +void tensor_mul_inplace(tensor t1, const tensor t2) +{ + /* Multiplies the values of t2 onto t1 element wise. t1 and t2 need to + * have the same shape. + * + * @param t1 The tensor to multiply onto + * @param t2 The tensor to multiply with + */ + // TODO +} + +void tensor_div_inplace(tensor t1, const tensor t2) +{ + /* Divides the values of t2 by the values of t1 element wise. t1 and t2 + * need to have the same shape. + * + * @param t1 The tensor to devide + * @param t2 The tensor to devide by + */ + // TODO +} + +tensor tensor_add(const tensor t1, const tensor t2) +{ + /* Adds two tensors returning the result as a tensor. t1 and t2 need to + * have the same shape. + * + * @param t1 The first tensor to add + * @param t2 The second tensor to add + * + * @return The result of the operation, NULL if an error occurs + */ + // TODO +} + +tensor tensor_sub(const tensor t1, const tensor t2) +{ + /* Subtracts two tensors returning the result as a tensor. t1 and t2 need + * to have the same shape. + * + * @param t1 The tensor to subtract from + * @param t2 The tensor that is subtracted + * + * @return The result of the operation, NULL if an error occurs + */ + // TODO +} + +tensor tensor_mul(const tensor t1, const tensor t2) +{ + /* Multiplies two tensors element wise returning the result as a tensor. + * t1 and t2 need to have the same shape. + * + * @param t1 The first tensor to multipy + * @param t2 The second tensor to multipy + * + * @return The result of the operation, NULL if an error occurs + */ + // TODO +} + +tensor tensor_div(const tensor t1, const tensor t2) +{ + /* Divides two tensors element wise returning the result as a tensor. t1 + * and t2 need to have the same shape. + * + * @param t1 The dividend + * @param t2 The divisor + * + * @return The result of the operation, NULL if an error occurs + */ + // TODO +} diff --git a/tensorfunc.h b/tensorfunc.h new file mode 100644 index 0000000..6bb1a07 --- /dev/null +++ b/tensorfunc.h @@ -0,0 +1,30 @@ +#ifndef _TENSORFUNC_H_ +#define _TENSORFUNC_H_ + +#include "tensor.h" + +void tensor_fill(tensor t, dtype (*func)(void)); +void tensor_inspect(const tensor t, void (*func)(dtype)); + +tensor tensor_map(const tensor t, dtype (*func)(dtype)); +void tensor_map_inplace(tensor t, dtype (*func)(dtype)); + +tensor tensor_combine(const tensor t1, const tensor t2, dtype (*func)(dtype, dtype)); +void tensor_combine_inplace(tensor t1, const tensor t2, dtype (*func)(dtype, dtype)); + +void tensor_add_scalar(tensor t, dtype scalar); +void tensor_sub_scalar(tensor t, dtype scalar); +void tensor_mul_scalar(tensor t, dtype scalar); +void tensor_div_scalar(tensor t, dtype scalar); + +void tensor_add_inplace(tensor t1, const tensor t2); +void tensor_sub_inplace(tensor t1, const tensor t2); +void tensor_mul_inplace(tensor t1, const tensor t2); +void tensor_div_inplace(tensor t1, const tensor t2); + +tensor tensor_add(const tensor t1, const tensor t2); +tensor tensor_sub(const tensor t1, const tensor t2); +tensor tensor_mul(const tensor t1, const tensor t2); +tensor tensor_div(const tensor t1, const tensor t2); + +#endif // _TENSORFUNC_H_ diff --git a/tensoriter.c b/tensoriter.c deleted file mode 100644 index a782b76..0000000 --- a/tensoriter.c +++ /dev/null @@ -1,153 +0,0 @@ -#include "tensoriter.h" - -tensoriter_scalar tensoriter_scalar_create(tensor t) -{ - /* Creates an iterator over the values of a tensor. If two tensors have the - * same size the iterator will always iterate over them in the same order. - * - * @param t The tensor to iterate over - * - * @return The iterator, NULL in case of an error - */ - assert(!tensor_is_empty(t)); - - tensoriter_scalar it = malloc(sizeof(struct _tensor_scalar_iterator)); - if (it == NULL) return NULL; - - it->t = t; - it->index = calloc(sizeof(uint32_t), t->rank); - - return it; -} - -void tensoriter_scalar_destroy(tensoriter_scalar it) -{ - /* Destroys an iterator. - * - * @param it The iterator to destroy - */ - free(it->index); - free(it); -} - -bool tensoriter_scalar_next(tensoriter_scalar it) -{ - /* Checks whether the given iterator has a next value and sets this value - * as the current value if available. If there is not next value the - * iterator is destroyed and false is returned. - * - * @param it The iterator to evaluate - * - * @return true if there is a next value, false otherwise - */ - bool end = true; - - for (uint8_t i = 0; i < it->t->rank; i++) { - if (it->index[i] < it->t->size[i] - 1) { - (it->index[i])++; - end = false; - break; - } else { - it->index[i] = 0; - } - } - - if (end) { - tensoriter_scalar_destroy(it); - return false; - } - - return true; -} - -dtype tensoriter_scalar_get(tensoriter_scalar it, bool *success) -{ - /* Gets the current value of the iterator. - * - * @param it The iterator to operate on - * @param success Is set if not NULL and defines whether the get operation - * was successful - * - * @return The current value of the iterator - */ - return tensor_get(it->t, it->index, success); -} - -bool tensoriter_scalar_set(tensoriter_scalar it, dtype value) -{ - /* Sets the value of the tensor which the iterator is pointing to at the - * moment. - * - * @param it The iterator to operate on - * @param value The value to insert - * - */ - return tensor_set(it->t, it->index, value); -} - -void tensoriter_scalar_map(tensoriter_scalar it, dtype (*func)(dtype)) -{ - /* Replaces every value in an iterator with the result of given function - * with the old value as a parameter. - * - * @param it The iterator to operate on - * @param func The map function that is called - */ - do { - dtype x = tensoriter_scalar_get(it, NULL); - tensoriter_scalar_set(it, func(x)); - } while(tensoriter_scalar_next(it)); -} - -void tensoriter_scalar_map_add(tensoriter_scalar it, dtype scalar) -{ - /* Adds a fixed scalar value to all the values of the iterator. - * - * @param it The iterator to operate on - * @param scalar The value to add - */ - do { - dtype x = tensoriter_scalar_get(it, NULL); - tensoriter_scalar_set(it, DTYPE_ADD(x, scalar)); - } while(tensoriter_scalar_next(it)); -} - -void tensoriter_scalar_map_sub(tensoriter_scalar it, dtype scalar) -{ - /* Subtracts a fixed scalar value from all the values of the iterator. - * - * @param it The iterator to operate on - * @param scalar The value to subtract - */ - do { - dtype x = tensoriter_scalar_get(it, NULL); - tensoriter_scalar_set(it, DTYPE_SUB(x, scalar)); - } while(tensoriter_scalar_next(it)); -} - -void tensoriter_scalar_map_mul(tensoriter_scalar it, dtype scalar) -{ - /* Multiplies a fixed scalar value with all the values of the iterator. - * - * @param it The iterator to operate on - * @param scalar The value to multiply - */ - do { - dtype x = tensoriter_scalar_get(it, NULL); - tensoriter_scalar_set(it, DTYPE_MUL(x, scalar)); - } while(tensoriter_scalar_next(it)); -} - -void tensoriter_scalar_map_div(tensoriter_scalar it, dtype scalar) -{ - /* Divides all the values of the iterator by a fixed scalar value. - * - * @param it The iterator to operate on - * @param scalar The value to divide by - */ - do { - dtype x = tensoriter_scalar_get(it, NULL); - tensoriter_scalar_set(it, DTYPE_DIV(x, scalar)); - } while(tensoriter_scalar_next(it)); -} - diff --git a/tensoriter.h b/tensoriter.h deleted file mode 100644 index 5b4852e..0000000 --- a/tensoriter.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef _TENSORITERATOR_H_ -#define _TENSORITERATOR_H_ - -#include "tensor.h" - -typedef struct _tensor_scalar_iterator { - tensor t; - uint32_t *index; -} * tensoriter_scalar; - -tensoriter_scalar tensoriter_scalar_create(tensor t); -void tensoriter_scalar_destroy(tensoriter_scalar it); - -bool tensoriter_scalar_next(tensoriter_scalar it); -dtype tensoriter_scalar_get(tensoriter_scalar it, bool *success); -bool tensoriter_scalar_set(tensoriter_scalar it, dtype value); - -void tensoriter_scalar_map(tensoriter_scalar it, dtype (*func)(dtype)); -void tensoriter_scalar_map_add(tensoriter_scalar it, dtype scalar); -void tensoriter_scalar_map_sub(tensoriter_scalar it, dtype scalar); -void tensoriter_scalar_map_mul(tensoriter_scalar it, dtype scalar); -void tensoriter_scalar_map_div(tensoriter_scalar it, dtype scalar); - -#endif // _TENSORITERATOR_H_ -