arrayfire / arrayfire-python

Python bindings for ArrayFire: A general purpose GPU library.

Home Page:https://arrayfire.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Conversion from af:Array to scalar

shyams2 opened this issue · comments

Is there a Python command to convert an ArrayFire array to a scalar?

Suppose I define a = af.Array([1,2,3]). Then, a[1] gives me an af.Array of dimensions [1 1 1 1] containing the value of 2. But how do I get the scalar value of it directly? I'm currently using af.algorithm.sum(a[1])

Is there a more direct way to do this?

@ShyamSS-95 I just realized that is the fastest way for now. The C++ API has array.scalar<T>() but that is copying the entire array to the host and then returning one element.

Created an issue upstream to add the necessry functionality: arrayfire/arrayfire#1725

@ShyamSS-95 Actually in your case, since a[1] is just one element, you could do a[1].to_array() to get a host array of size [1] or a[1].to_array()[0] to get a number.

Can you also tell in what scenario you are using this ? Because if you are copying to host costantly, it is better off if you do the copy once and then index the host instead of copying each element individually.

I'm using it in a loop for the implementation of a collision algorithm. Shown below is the code snippet where I've had to convert an af.Array to a scalar.

for i in range(no_of_particles):
  x_particle_considered = af.data.constant(af.algorithm.sum(x_coordinates[i]), (no_of_particles-1-i))
  y_particle_considered = af.data.constant(af.algorithm.sum(y_coordinates[i]), (no_of_particles-1-i))  
  x_coordinates_others  = x_coordinates[(i+1):no_of_particles]        
  y_coordinates_others  = y_coordinates[(i+1):no_of_particles]
  # Determining the components of the vector joining particle i to all other particles    
  x_coordinates_others = x_coordinates_others-x_particle_considered
  y_coordinates_others = y_coordinates_others-y_particle_considered

@ShyamSS-95 try this:

@af.broadcast
def sub(lhs, rhs):
    return lhs - rhhs

for i in range(no_of_particles):
  x_coordinates_others  = x_coordinates[(i+1):no_of_particles]        
  y_coordinates_others  = y_coordinates[(i+1):no_of_particles]
  # Determining the components of the vector joining particle i to all other particles    
  x_coordinates_others = sub(x_coordinates_others, x_coordinates[i])
  y_coordinates_others = sub(y_coordinates_others, y_coordinates[i])

Thanks! It's much easier to write. I wrote the following code to check the effect on speed:

import numpy as np 
import arrayfire as af
import time

no_of_particles = 1000
test_array = np.random.rand(no_of_particles)
x_coordinates = y_coordinates = af.Array(test_array.ctypes.data, test_array.shape, test_array.dtype.char)

@af.broadcast
def sub(lhs, rhs):
  return lhs - rhs

sum_time_start = time.time()
for i in range(no_of_particles-1):
  x_coordinates_others = x_coordinates[(i+1):no_of_particles]        
  y_coordinates_others = y_coordinates[(i+1):no_of_particles]
  x_coordinates_others = x_coordinates_others - af.data.constant(af.algorithm.sum(x_coordinates[i]), (no_of_particles-1-i))
  y_coordinates_others = y_coordinates_others - af.data.constant(af.algorithm.sum(y_coordinates[i]), (no_of_particles-1-i))
sum_time_end  = time.time()

print("Time elapsed using sum = ", (sum_time_end- sum_time_start) )

sub_time_start = time.time()
for i in range(no_of_particles-1):
  x_coordinates_others = x_coordinates[(i+1):no_of_particles]        
  y_coordinates_others = y_coordinates[(i+1):no_of_particles]
  x_coordinates_others = sub(x_coordinates_others, x_coordinates[i])
  y_coordinates_others = sub(y_coordinates_others, y_coordinates[i])
sub_time_end  = time.time()

print("Time elapsed using sub = ", (sub_time_end- sub_time_start) )

It runs faster as well:

Time elapsed using sum =  0.628760576248169
Time elapsed using sub  =  0.25028181076049805

@ShyamSS-95 excellent 👍

af.broadcast allows you to perform elemnt wise operations where the dimensions are not identical. But when you are benchmarking, you need to have af.eval(x_coordinates_others, y_coordinates_others) at the end of the loop and an af.sync() after the loop.