Skip to content

Added tensor_to_matrix/1 (with tests/docs) #37

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions c_src/Tensorflex.c
Original file line number Diff line number Diff line change
Expand Up @@ -1023,6 +1023,55 @@ static ERL_NIF_TERM subtract_matrices(ErlNifEnv* env, int argc, const ERL_NIF_TE
return ret;
}

static ERL_NIF_TERM tensor_to_matrix(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
unsigned i,j;
ERL_NIF_TERM ret;
TF_Tensor **tensor;
mx_t mx;
mx.p = NULL;
enif_get_resource(env, argv[0], tensor_resource, (void *) &tensor);
TF_DataType type = TF_TensorType(*tensor);
if(TF_NumDims(*tensor) == 2) {
mx.p = alloc_matrix(env, (unsigned)(TF_Dim(*tensor,(TF_NumDims(*tensor)-2))), (unsigned)(TF_Dim(*tensor,(TF_NumDims(*tensor)-1))));

if(type == TF_FLOAT){
float* float_tensor_data = (float*)TF_TensorData(*tensor);
for(j=0; j<(unsigned)(TF_Dim(*tensor,(TF_NumDims(*tensor)-2))); j++) {
for(i=0; i<(unsigned)(TF_Dim(*tensor,(TF_NumDims(*tensor)-1))); i++)
{
POS(mx.p,j,i) = (double)*float_tensor_data++;

}
}
}

else if(type == TF_INT32){
int32_t* int32_tensor_data = (int32_t*)TF_TensorData(*tensor);
for(j=0; j<(unsigned)(TF_Dim(*tensor,(TF_NumDims(*tensor)-2))); j++) {
for(i=0; i<(unsigned)(TF_Dim(*tensor,(TF_NumDims(*tensor)-1))); i++)
{
POS(mx.p,j,i) = (double)*int32_tensor_data++;
}
}
}

else if(type == TF_DOUBLE){
double* double_tensor_data = (double*)TF_TensorData(*tensor);
for(j=0; j<(unsigned)(TF_Dim(*tensor,(TF_NumDims(*tensor)-2))); j++) {
for(i=0; i<(unsigned)(TF_Dim(*tensor,(TF_NumDims(*tensor)-1))); i++)
{
POS(mx.p,j,i) = *double_tensor_data++;
}
}
}
} else return enif_make_badarg(env);

ret = enif_make_resource(env, mx.p);
enif_release_resource(mx.p);
return ret;
}

static ErlNifFunc nif_funcs[] =
{
{"create_matrix", 3, create_matrix},
Expand Down Expand Up @@ -1053,6 +1102,7 @@ static ErlNifFunc nif_funcs[] =
{ "divide_matrix_by_scalar", 2, divide_matrix_by_scalar },
{ "add_matrices", 2, add_matrices },
{ "subtract_matrices", 2, subtract_matrices },
{ "tensor_to_matrix", 1, tensor_to_matrix },
};

ERL_NIF_INIT(Elixir.Tensorflex.NIFs, nif_funcs, res_loader, NULL, NULL, NULL)
Expand Down
4 changes: 4 additions & 0 deletions lib/nifs.ex
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,8 @@ defmodule Tensorflex.NIFs do
raise "NIF subtract_matrices/2 not implemented"
end

def tensor_to_matrix(_tensor) do
raise "NIF tensor_to_matrix/1 not implemented"
end

end
118 changes: 118 additions & 0 deletions lib/tensorflex.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1407,5 +1407,123 @@ defmodule Tensorflex do
new_ref = NIFs.subtract_matrices(ref1, ref2)
%Matrix{nrows: nrows1, ncols: ncols2, data: new_ref}
end

@doc """
Converts the data stored in a 2-D tensor back to a 2-D matrix.

Takes in a single argument as a `%Tensor` tensor (any `TF_Datatype`).

Returns a `%Matrix` 2-D matrix.

__NOTE__: Tensorflex doesn't currently support 3-D matrices, and therefore
tensors that are 3-D (such as created using the `load_image_as_tensor/1`
function) cannot be converted back to a matrix, yet. Support for 3-D matrices
will be added soon.

## Examples
`tensor_to_matrix/1` converts any 2-D `%Tensor` tensor back to matrix form.
Consider `sample1.csv` back from the examples of `load_csv_as_matrix/2`:

```elixir
iex(1)> vals = Tensorflex.load_csv_as_matrix("sample1.csv", header: :false)
%Tensorflex.Matrix{
data: #Reference<0.124471106.2360737795.170799>,
ncols: 5,
nrows: 3
}

iex(2)> dims = Tensorflex.create_matrix(1,2,[[3,5]])
%Tensorflex.Matrix{
data: #Reference<0.124471106.2360737795.170827>,
ncols: 2,
nrows: 1
}

iex(3)> {:ok, float64_tensor} = Tensorflex.float64_tensor vals,dims
{:ok,
%Tensorflex.Tensor{
datatype: :tf_double,
tensor: #Reference<0.124471106.2360737794.171586>
}}

iex(4)> m_float64 = Tensorflex.tensor_to_matrix float64_tensor
%Tensorflex.Matrix{
data: #Reference<0.124471106.2360737794.171596>,
ncols: 5,
nrows: 3
}

iex(5)> Tensorflex.matrix_to_lists m_float64
[
[1.0, 2.0, 3.0, 4.0, 5.0],
[6.0, 7.0, 8.0, 9.0, 10.0],
[11.0, 12.0, 13.0, 14.0, 15.0]
]

iex(6)> {:ok, float32_tensor} = Tensorflex.float32_tensor vals,dims
{:ok,
%Tensorflex.Tensor{
datatype: :tf_float,
tensor: #Reference<0.124471106.2360737794.172555>
}}

iex(7)> m_float32 = Tensorflex.tensor_to_matrix float32_tensor
%Tensorflex.Matrix{
data: #Reference<0.124471106.2360737794.172563>,
ncols: 5,
nrows: 3
}

iex(8)> Tensorflex.matrix_to_lists m_float32
[
[1.0, 2.0, 3.0, 4.0, 5.0],
[6.0, 7.0, 8.0, 9.0, 10.0],
[11.0, 12.0, 13.0, 14.0, 15.0]
]

iex(9)> {:ok, int32_tensor} = Tensorflex.int32_tensor vals,dims
{:ok,
%Tensorflex.Tensor{
datatype: :tf_int32,
tensor: #Reference<0.124471106.2360737794.172578>
}}

iex(10)> m_int32 = Tensorflex.tensor_to_matrix int32_tensor
%Tensorflex.Matrix{
data: #Reference<0.124471106.2360737794.172586>,
ncols: 5,
nrows: 3
}

iex(11)> Tensorflex.matrix_to_lists m_int32
[
[1.0, 2.0, 3.0, 4.0, 5.0],
[6.0, 7.0, 8.0, 9.0, 10.0],
[11.0, 12.0, 13.0, 14.0, 15.0]
]

```

The matrix values obtained in the conversions, `m_int32`, `m_float32`,
`m_float64` are identical to the `vals` matrix we had generated from the
`sample1.csv` file:

```elixir

iex(12)> Tensorflex.matrix_to_lists vals
[
[1.0, 2.0, 3.0, 4.0, 5.0],
[6.0, 7.0, 8.0, 9.0, 10.0],
[11.0, 12.0, 13.0, 14.0, 15.0]
]

```
"""

def tensor_to_matrix(%Tensor{datatype: _datatype, tensor: ref}) do
matrix_ref = NIFs.tensor_to_matrix(ref)
{nrows, ncols} = NIFs.size_of_matrix matrix_ref
%Matrix{nrows: nrows, ncols: ncols, data: matrix_ref}
end

end
70 changes: 70 additions & 0 deletions test/tensorflex_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,45 @@ defmodule TensorflexTest do
assert {5,4} = Tensorflex.size_of_matrix mat
assert 7.0 = Tensorflex.matrix_pos(mat,5,3)
end

test "add scalar to matrix check" do
m = Tensorflex.create_matrix(2,3,[[1,2,3],[4,5,6]])
m = Tensorflex.add_scalar_to_matrix(m, 5)
assert [[6.0, 7.0, 8.0], [9.0, 10.0, 11.0]] = Tensorflex.matrix_to_lists m
end

test "subtract scalar from matrix check" do
m = Tensorflex.create_matrix(2,3,[[1,2,3],[4,5,6]])
m = Tensorflex.subtract_scalar_from_matrix m,3
assert [[-2.0, -1.0, 0.0], [1.0, 2.0, 3.0]] = Tensorflex.matrix_to_lists m
end

test "multiply matrix with scalar check" do
m = Tensorflex.create_matrix(2,3,[[1,2,3],[4,5,6]])
m = Tensorflex.multiply_matrix_with_scalar m,5
assert [[5.0, 10.0, 15.0], [20.0, 25.0, 30.0]] = Tensorflex.matrix_to_lists m
end

test "divide matrix with scalar check" do
m = Tensorflex.create_matrix(2,3,[[1,2,3],[4,5,6]])
m = Tensorflex.divide_matrix_by_scalar m,3
assert [[0.3333333333333333, 0.6666666666666666, 1.0], [1.3333333333333333, 1.6666666666666667, 2.0]] = Tensorflex.matrix_to_lists m
end

test "add two matrices check" do
m1 = Tensorflex.create_matrix(2,3,[[1,2,3],[4,5,6]])
m2 = Tensorflex.create_matrix(2,3,[[4,5,6],[1,2,3]])
m_added = Tensorflex.add_matrices m1,m2
assert [[5.0, 7.0, 9.0], [5.0, 7.0, 9.0]] = Tensorflex.matrix_to_lists m_added
end

test "subtract two matrices check" do
m1 = Tensorflex.create_matrix(2,3,[[1,2,3],[4,5,6]])
m2 = Tensorflex.create_matrix(2,3,[[4,5,6],[1,2,3]])
m_subtracted = Tensorflex.subtract_matrices m1,m2
assert [[-3.0, -3.0, -3.0], [3.0, 3.0, 3.0]] = Tensorflex.matrix_to_lists m_subtracted
end

end

describe "float32 tensor functionalities" do
Expand Down Expand Up @@ -199,6 +238,37 @@ defmodule TensorflexTest do
{:ok, _tensor} = Tensorflex.load_image_as_tensor("./test/sample1.csv")
end
end

test "float64_tensor to matrix conversion check" do
vals = Tensorflex.load_csv_as_matrix("./test/sample1.csv", header: :false)
dims = Tensorflex.create_matrix(1,2,[[3,5]])
{:ok, float64_tensor} = Tensorflex.float64_tensor vals,dims
m_float64 = Tensorflex.tensor_to_matrix float64_tensor
assert [[1.0, 2.0, 3.0, 4.0, 5.0], [6.0, 7.0, 8.0, 9.0, 10.0], [11.0, 12.0, 13.0, 14.0, 15.0]] = Tensorflex.matrix_to_lists m_float64
end

test "int32_tensor to matrix conversion check" do
vals = Tensorflex.load_csv_as_matrix("./test/sample1.csv", header: :false)
dims = Tensorflex.create_matrix(1,2,[[3,5]])
{:ok, int32_tensor} = Tensorflex.int32_tensor vals,dims
m_int32 = Tensorflex.tensor_to_matrix int32_tensor
assert [[1.0, 2.0, 3.0, 4.0, 5.0], [6.0, 7.0, 8.0, 9.0, 10.0], [11.0, 12.0, 13.0, 14.0, 15.0]] = Tensorflex.matrix_to_lists m_int32
end

test "float32_tensor to matrix conversion check" do
vals = Tensorflex.load_csv_as_matrix("./test/sample1.csv", header: :false)
dims = Tensorflex.create_matrix(1,2,[[3,5]])
{:ok, float32_tensor} = Tensorflex.float32_tensor vals,dims
m_float32 = Tensorflex.tensor_to_matrix float32_tensor
assert [[1.0, 2.0, 3.0, 4.0, 5.0], [6.0, 7.0, 8.0, 9.0, 10.0], [11.0, 12.0, 13.0, 14.0, 15.0]] = Tensorflex.matrix_to_lists m_float32
end

test "incorrect usage tensor-to-matrix check" do
{:ok, tensor} = Tensorflex.load_image_as_tensor("./test/cropped_panda.jpg")
assert_raise ArgumentError, fn ->
_m = Tensorflex.tensor_to_matrix tensor
end
end
end

end