Declared functions in tensorfunc which will replace the iterators with a more functional aprroach.

This commit is contained in:
2023-09-15 14:06:13 +02:00
parent 0628955e43
commit 8c44b96913
8 changed files with 257 additions and 387 deletions

View File

@@ -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

197
tensor.c
View File

@@ -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.

View File

@@ -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

View File

@@ -6,7 +6,7 @@
#include <stdlib.h>
#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_

223
tensorfunc.c Normal file
View File

@@ -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
}

30
tensorfunc.h Normal file
View File

@@ -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_

View File

@@ -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));
}

View File

@@ -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_