I am trying to saturate my arrays in ArrayFire. I want all values greater than 0.75 to saturate to 1.0 and all less than 0.25 to saturate to 0.0. I am using the following expressions.
a(a > 0.75) = 1.0;
a(a < 0.25) = 0.0;
terminate called after throwing an instance of 'af::exception'
what(): ArrayFire Exception (Invalid input size:203):
In function verifyDims
In file src/api/c/data.cpp:36
Invalid dimension for argument 1
Expected: ndims >= 1
In function af::array af::constant(T, const af::dim4&, af::dtype) [with T = double; af::dtype = af_dtype]
In file src/api/cpp/data.cpp:28
af::print("", a > 0.75);
[10 1 1 1]
af::array bellow = a[levels - 1] < 0.25f;
af::array above = a[levels - 1] > 0.75f;
a[levels - 1](above) = 0.75f;
a[levels - 1](bellow) = 0.25f;
void train(const float* in, const float* expected_out, float learning_rate)
std::unique_ptr<af::array> a(new af::array[levels]),
z(new af::array[levels]), d(new af::array[levels]);
af::array in_array(inputs, in);
af::array y(dims[levels - 1], expected_out);
z = af::matmul(weights, in_array) + biases;
a = sigma(z);
for(size_t i = 1; i < levels; i++)
z[i] = af::matmul(weights[i], a[i - 1]) + biases[i];
a[i] = sigma(z[i]);
a[levels - 1](a[levels - 1] < 0.25f) = 0.0f;
a[levels - 1](a[levels - 1] > 0.75f) = 1.0f;
d[levels - 1] = (y - a[levels - 1]) * sigma_prime(z[levels - 1]);
for(size_t i = levels - 1; i-- > 0;)
d[i] = af::matmul(weights[i + 1].T(), d[i + 1]) * sigma_prime(z[i]);
for(size_t i = 0; i < levels; i++)
biases[i] += learning_rate * d[i];
weights[i] += learning_rate * af::matmul(d[i], (i ? a[i - 1] : in_array).T());
The error you are seeing is because of this open bug about zero length arrays (EDIT: FIxed as of v3.4.0). This is a pervasive issue that we are trying to fix properly for some time now.
Here is the work around for your case. You don't even need indexing to achieve what you are trying to do.
a[levels - 1] = af::min(0.75, af::max(0.25, a[levels - 1]));
EDIT: As of 3.4, you can do the following to achieve the same functionality in arrayfire:
a[levels - 1] = af::clamp(a[levels - 1], 0.25, 0.75);
This method is much much faster than indexing for your case.
That said, there are some cases when you can not use
af::max to replace indexing. In those cases, you could do something like this as a work around:
af::array cond = arr < some_val; arr = arr * (1 - cond) + cond * other_val;
This should also be faster than indexing. However the arithmetic will not work if the arrays have
NAN in them and you are trying to replace them. In which case you could fall back to one of the following functions.
Using select (uses additional memory):
arr = af::select(af::isNaN(arr), arr, other_val));
Using replace (replaces in place, no additional memory used):
af::select(arr, af::isNaN(arr) other_val));
However some benchmarking showed us that
replace can be slower than indexing in certain cases (which we are trying to fix). So you could try using the following work around for indexing if
replace are slow in your algorithm.
af::array idx = af::where(af::isNaN(arr)); if (idx.elements()) arr(idx) = replace_val;
Note that indexing on a boolean
af::where internally. So this is as efficient as the following
arr(arr < some_val) = other_val;
with the added benefit of not failing for zero sized arrays.
EDIT: Added additional workarounds for posterity.