Skip to content

Commit

Permalink
min_finite/1, max_finite/1 completed. fill/3 accepts special values a…
Browse files Browse the repository at this point in the history
…toms.
  • Loading branch information
versilov committed May 23, 2018
1 parent 6b8a2b4 commit ed3804b
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 20 deletions.
49 changes: 45 additions & 4 deletions lib/matrex.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1461,10 +1461,10 @@ defmodule Matrex do
│ 55.0 55.0 55.0 │
└ ┘
"""
@spec fill(index, index, number) :: matrex
@spec fill(index, index, element) :: matrex
def fill(rows, cols, value)
when is_integer(rows) and is_integer(cols) and is_number(value),
do: %Matrex{data: NIFs.fill(rows, cols, value)}
when (is_integer(rows) and is_integer(cols) and is_number(value)) or is_atom(value),
do: %Matrex{data: NIFs.fill(rows, cols, float_to_binary(value))}

@doc """
Create square matrix filled with given value. Inlined.
Expand All @@ -1479,7 +1479,7 @@ defmodule Matrex do
│ 33.0 33.0 33.0 │
└ ┘
"""
@spec fill(index, number) :: matrex
@spec fill(index, element) :: matrex
def fill(size, value), do: fill(size, size, value)

@doc """
Expand Down Expand Up @@ -1674,6 +1674,20 @@ defmodule Matrex do
@spec max(matrex) :: element
def max(%Matrex{data: matrix}), do: NIFs.max(matrix)

@doc """
Returns maximum finite element of a matrex. NIF.
Used on matrices which may contain infinite values.
## Example
iex>Matrex.reshape([1, 2, Inf, 3, NaN, 5], 3, 2) |> Matrex.max_finite()
5.0
"""
@spec max_finite(matrex) :: float
def max_finite(%Matrex{data: matrix}), do: NIFs.max_finite(matrix)

@doc """
Minimum element in a matrix. NIF.
Expand All @@ -1696,6 +1710,20 @@ defmodule Matrex do
@spec min(matrex) :: element
def min(%Matrex{data: matrix}), do: NIFs.min(matrix)

@doc """
Returns minimum finite element of a matrex. NIF.
Used on matrices which may contain infinite values.
## Example
iex>Matrex.reshape([1, 2, NegInf, 3, 4, 5], 3, 2) |> Matrex.min_finite()
1.0
"""
@spec min_finite(matrex) :: float
def min_finite(%Matrex{data: matrix}), do: NIFs.min_finite(matrix)

@doc """
Elementwise multiplication of two matrices or matrix and a scalar. NIF.
Expand Down Expand Up @@ -2391,7 +2419,20 @@ defmodule Matrex do
## Example
iex> m = Matrex.reshape(1..6, 3, 2)
#Matrex[3×2]
┌ ┐
│ 1.0 2.0 │
│ 3.0 4.0 │
│ 5.0 6.0 │
└ ┘
iex> Matrex.set_column(m, 2, Matrex.new("7; 8; 9"))
#Matrex[3×2]
┌ ┐
│ 1.0 7.0 │
│ 3.0 8.0 │
│ 5.0 9.0 │
└ ┘
"""
@spec set_column(matrex, index, matrex) :: matrex
def set_column(
Expand Down
6 changes: 6 additions & 0 deletions lib/matrex/nifs.ex
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,12 @@ defmodule Matrex.NIFs do
@spec min(binary) :: float
def min(_matrix), do: :erlang.nif_error(:nif_library_not_loaded)

@spec max_finite(binary) :: float
def max_finite(_matrix), do: :erlang.nif_error(:nif_library_not_loaded)

@spec min_finite(binary) :: float
def min_finite(_matrix), do: :erlang.nif_error(:nif_library_not_loaded)

@spec multiply(binary, binary) :: binary
def multiply(first, second)
when is_binary(first) and is_binary(second),
Expand Down
6 changes: 6 additions & 0 deletions native/include/matrix.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,12 @@ matrix_max(const Matrix matrix);
float
matrix_min(const Matrix matrix);

float
matrix_max_finite(const Matrix matrix);

float
matrix_min_finite(const Matrix matrix);

void
matrix_multiply(const Matrix first, const Matrix second, Matrix result);

Expand Down
58 changes: 52 additions & 6 deletions native/nifs/matrix_nifs.c
Original file line number Diff line number Diff line change
Expand Up @@ -508,24 +508,26 @@ eye(ErlNifEnv *env, int32_t argc, const ERL_NIF_TERM *argv) {
static ERL_NIF_TERM
fill(ErlNifEnv *env, int32_t argc, const ERL_NIF_TERM *argv) {
ERL_NIF_TERM result;
ErlNifBinary value;
unsigned long rows, cols;
float value;
float *value_data;
float *result_data;
size_t result_size;

(void)(argc);

enif_get_uint64(env, argv[0], &rows);
enif_get_uint64(env, argv[1], &cols);
value = get_scalar(env, argv[2]);
if (!enif_inspect_binary(env, argv[2], &value)) return enif_make_badarg(env);
value_data = (float*)value.data;

result_size = (rows*cols + 2) * sizeof(float);
result_data = (float *) enif_make_new_binary(env, result_size, &result);

MX_SET_ROWS(result_data, rows);
MX_SET_COLS(result_data, cols);

matrix_fill(result_data, value);
matrix_fill(result_data, *value_data);

return result;
}
Expand All @@ -548,7 +550,7 @@ find(ErlNifEnv *env, int argc, const ERL_NIF_TERM *argv) {
if (isnan(*element_data)) {
index = matrix_find_nan(matrix_data);
} else {
index = matrix_find(matrix_data, *element_data);
index = matrix_find(matrix_data, *element_data);
}

if (index >= 0) {
Expand Down Expand Up @@ -578,10 +580,12 @@ static inline ERL_NIF_TERM
make_cell_value(ErlNifEnv* env, const float value) {
if (isfinite(value))
return enif_make_double(env, value);
if (isnan(value))
else if (isnan(value))
return enif_make_atom(env, "Elixir.NaN");
else if (isinf(value))
else if (value == INFINITY)
return enif_make_atom(env, "Elixir.Inf");
else if (value == -INFINITY)
return enif_make_atom(env, "Elixir.NegInf");
else
return enif_make_badarg(env);
}
Expand All @@ -603,6 +607,26 @@ max(ErlNifEnv *env, int32_t argc, const ERL_NIF_TERM *argv) {
return make_cell_value(env, max);
}

static ERL_NIF_TERM
max_finite(ErlNifEnv *env, int32_t argc, const ERL_NIF_TERM *argv) {
ErlNifBinary matrix;
float max;
float *matrix_data;

(void)(argc);

if (!enif_inspect_binary(env, argv[0], &matrix)) return enif_make_badarg(env);

matrix_data = (float *) matrix.data;

max = matrix_max_finite(matrix_data);

if (isnan(max))
return enif_make_atom(env, "nil");
else
return enif_make_double(env, max);
}

static ERL_NIF_TERM
minimum(ErlNifEnv *env, int32_t argc, const ERL_NIF_TERM *argv) {
ErlNifBinary matrix;
Expand All @@ -620,6 +644,26 @@ minimum(ErlNifEnv *env, int32_t argc, const ERL_NIF_TERM *argv) {
return make_cell_value(env, min);
}

static ERL_NIF_TERM
min_finite(ErlNifEnv *env, int32_t argc, const ERL_NIF_TERM *argv) {
ErlNifBinary matrix;
float min;
float *matrix_data;

(void)(argc);

if (!enif_inspect_binary(env, argv[0], &matrix)) return enif_make_badarg(env);

matrix_data = (float *) matrix.data;

min = matrix_min_finite(matrix_data);

if (isnan(min))
return enif_make_atom(env, "nil");
else
return enif_make_double(env, min);
}

static ERL_NIF_TERM
multiply(ErlNifEnv *env, int32_t argc, const ERL_NIF_TERM *argv) {
ErlNifBinary first, second;
Expand Down Expand Up @@ -1097,6 +1141,8 @@ static ErlNifFunc nif_functions[] = {
{"find", 2, find, 0},
{"max", 1, max, 0},
{"min", 1, minimum, 0},
{"max_finite", 1, max_finite, 0},
{"min_finite", 1, min_finite, 0},
{"multiply", 2, multiply, 0},
{"multiply_with_scalar", 2, multiply_with_scalar, 0},
{"neg", 1, neg, 0},
Expand Down
42 changes: 35 additions & 7 deletions native/src/matrix.c
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ matrix_eye(Matrix matrix, const float value) {

void
matrix_fill(Matrix matrix, const float value) {
uint64_t length = MX_LENGTH(matrix);
const uint64_t length = MX_LENGTH(matrix);

for (uint64_t index = 2; index < length; index += 1) {
matrix[index] = value;
Expand Down Expand Up @@ -438,9 +438,23 @@ matrix_inspect_internal(const Matrix matrix, int32_t indentation) {
printf(">");
}

float
matrix_max(const Matrix matrix) {
const uint64_t data_size = MX_LENGTH(matrix);
float max = matrix[2];

for (uint64_t index = 3; index < data_size; index += 1) {
if (max < matrix[index]) {
max = matrix[index];
}
}

return max;
}

float
matrix_min(const Matrix matrix) {
uint64_t data_size = MX_LENGTH(matrix);
const uint64_t data_size = MX_LENGTH(matrix);
float min = matrix[2];

for (uint64_t index = 3; index < data_size; index += 1) {
Expand All @@ -453,19 +467,33 @@ matrix_min(const Matrix matrix) {
}

float
matrix_max(const Matrix matrix) {
uint64_t data_size = MX_LENGTH(matrix);
float max = matrix[2];
matrix_max_finite(const Matrix matrix) {
const uint64_t data_size = MX_LENGTH(matrix);
float max = NAN;

for (uint64_t index = 3; index < data_size; index += 1) {
if (max < matrix[index]) {
for (uint64_t index = 2; index < data_size; index += 1) {
if (isfinite(matrix[index]) && (isnan(max) || max < matrix[index])) {
max = matrix[index];
}
}

return max;
}

float
matrix_min_finite(const Matrix matrix) {
const uint64_t data_size = MX_LENGTH(matrix);
float min = NAN;

for (uint64_t index = 2; index < data_size; index += 1) {
if (isfinite(matrix[index]) && (isnan(min) || min > matrix[index])) {
min = matrix[index];
}
}

return min;
}

void
matrix_multiply(const Matrix first, const Matrix second, Matrix result) {
uint64_t data_size = MX_LENGTH(first);
Expand Down
4 changes: 1 addition & 3 deletions test/algorithms_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ defmodule AlgorithmsTest do

{digit, List.last(fX), sX}
end,
max_concurrency: 1,
max_concurrency: 8,
timeout: 100_000
)
|> Enum.map(fn {:ok, {_d, _l, theta}} -> Matrex.to_list(theta) end)
Expand Down Expand Up @@ -147,8 +147,6 @@ defmodule AlgorithmsTest do
test "#fmincg optimizes neural network" do
end

defp round_enough(num), do: Float.round(num, 5)

# Split data into training and testing set, permute it randomly
@spec split_data(Matrex.t(), Matrex.t()) :: {Matrex.t(), Matrex.t(), Matrex.t(), Matrex.t()}
defp split_data(x, y) do
Expand Down
7 changes: 7 additions & 0 deletions test/creation_test.exs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
defmodule CreationTest do
use ExUnit.Case, async: true

import Matrex

test "#eye creates a diagonal square matirx" do
expected =
Matrex.new([
Expand Down Expand Up @@ -32,6 +34,11 @@ defmodule CreationTest do
assert Matrex.fill(3, 7.53) == expected
end

test "#fill fills matrix with special float value" do
e = new("NegInf NegInf; NegInf NegInf")
assert fill(2, NegInf) == e
end

test "#magic raises error, when too small is requested" do
assert_raise ArgumentError, ~r/Magic square less than 3x3 is not possible./, fn ->
Matrex.magic(2)
Expand Down
35 changes: 35 additions & 0 deletions test/matrex_test.exs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
defmodule MatrexTest do
use ExUnit.Case, async: true
import Matrex

test "#add adds two matrices" do
first = Matrex.new([[1, 2, 3], [4, 5, 6]])
Expand Down Expand Up @@ -337,13 +338,47 @@ defmodule MatrexTest do
assert Matrex.max(matrix) == expected
end

test "#max returns Inf" do
matrix = Matrex.new([[1, Inf, 3], [4, NegInf, 6]])
expected = Inf

assert Matrex.max(matrix) == expected
end

test "#min returns the minimum element from the matrix" do
matrix = Matrex.new([[1, 2, 0.5], [4, 5, 6]])
expected = 0.5

assert Matrex.min(matrix) == expected
end

test "#min returns NegInf" do
matrix = Matrex.new([[1, 2, 0.5], [4, 5, NegInf]])
expected = NegInf

assert Matrex.min(matrix) == expected
end

test "#max_finite returns max finite element" do
m = reshape([Inf, 2, 3, Inf], 2, 2)
assert max_finite(m) == 3.0
end

test "#max_finite returns nil for totally infinite matrix" do
m = fill(3, 3, Inf)
assert max_finite(m) == nil
end

test "#min_finite returns min finite element" do
m = reshape([NegInf, -2, NaN, 5], 2, 2)
assert min_finite(m) == -2.0
end

test "#min_finite returns nil for totally infinite matrix" do
m = fill(3, 3, NegInf)
assert min_finite(m) == nil
end

test "#multiply performs elementwise multiplication of two matrices" do
first = Matrex.new([[1, 2, 3], [4, 5, 6]])
second = Matrex.new([[5, 2, 1], [3, 4, 6]])
Expand Down

0 comments on commit ed3804b

Please sign in to comment.