Skip to content
Rodrigo Botafogo edited this page May 1, 2013 · 17 revisions

MDArrays can be of the following types:

  • boolean, byte, short, int, long, float, double, string, structure.

To create a new MDArray one needs to inform at least the type and shape of the array. It is also possible to pass to the array factory initialization data, but this is not required. When no initialization data is provided, the array is initialized with 0. For instance, to create a byte array of shape [2, 2, 3] and all values equal to 0 (zero). No initialization data is given:

> require `mdarray`
> @a = MDArray.byte([2, 2, 3])

We can now check some basic informatin about array a:

Shape of the array:

> puts @a.shape
[2, 2, 3]

Number of dimensions:

> puts @a.ndim
3

Size of the array, the number of elements in it:

> puts @a.size
12

The type of the array:

> puts @a.dtype
"byte"

Accessing elements of an array is done by using [] to index the element.

> puts @a[0, 0, 0]
0

> puts @a[1, 1, 2]
0

Unlike Ruby arrays, an MDArray has a uniform and fixed type, so trying to add values from a different type in an MDArray will raise an exception. In this case a RuntimeError.

> a[0, 0, 0] = true
RuntimeError

Writing a double value on byte array will cast double to byte:

> a[0, 0, 0] = 10.25
> puts a[0, 0, 0]
10

> a[0, 0, 0] = 200
> puts a[0, 0, 0] 
-56

Now, lets build a "short" array. Both MDArray.build("short", <dimension>) or MDArray.short(<dimension>) can be used:

> short = MDArray.build("short", [2, 2, 3])

All other types can be build the same way:

> int = MDArray.int([5,5,5,5])

Now lets create a float array with some initialization data:

> float = MDArray.float([2, 3], [0, 1, 2, 3, 4, 5, 6])

Note that the data is shaped according to the given shape, so, in this case the array is:

[[0.0 1.0 2.0]
 [3.0 4.0 5.0]]

Note also that although the initialization data is in "int" format, the resulting array is of type float

> puts float.dtype
"float"
> puts float[0, 1]
1.0

An array can be create with method arange. Method arange generates all values in the given range. If only one argument is given to arange, then all values up to the given value minus 1 are generated. for instance arange(15) will generate: [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14]

> d = MDArray.arange(15)

The following method should return true.

def compare 
  counter = 0
  d.each do |elmt|
    if (counter != elmt)
       false
    end
    counter += 1
  end
  true
end

With 2 arguments we have the begining and ending values for arange

  e = MDArray.arange(5, 15)       # e = [5 6 7 8 9 10 11 12 13 14]

With 3 arguments we have the begining, ending and step arguments

  f = MDArray.arange(10, 30, 5)   # f = [10 15 20 25]

Method typed_arange does the same as arange but for arrays of other type

  g = MDArray.typed_arange("double", 10, 30)      
  h = MDArray.typed_arange("double", 10, 30, 2.5) # h = [10.0 12.5 15.0 17.5 20.0 22.5 25.0 27.5]

MDArrays can be creat from method fromfunction. We use method name "fromfunction" to be consistent with numpy although we do not pass a function but rather a block in Ruby. The block is called with N parameters, where N is the rank of shape. Each parameter represents the coordinates of the array varying along a specific axis. For example, if shape were [2, 2], then the parameters in turn be (0, 0), (0, 1), (1, 0), (1, 1).

  # arr = [0, 1, 2, 3, 4]
  arr = MDArray.fromfunction("double", [5]) do |x|
    x
  end

  # Parameters passed will be (0, 0), (0, 1), (0, 2)... (0, 4), (1, 0), (1, 1), (1, 2)... (4, 4)
  arr = MDArray.fromfunction("double", [5, 5]) do |x, y|
    x + y
  end

  # Anything can be inside the block
  arr = MDArray.fromfunction("double", [2, 3, 4]) do |x, y, z|
    3.21 * x + y + z
  end

Up to 7 dimensions, we need to receive parameters independently for efficiency reasons. After 7 dimensions, fromfunction receives an array.

  arr = MDArray.fromfunction("double", [5, 5, 5, 5, 5, 5, 5]) do |x, y, z, k, w, i, l|
    x + y + z + k + w + i + l
  end

MDArrays with dimension larger than 7, the data is treated as an array, and we cannot use the same notation as before.

  arr = MDArray.fromfunction("double", [5, 5, 5, 5, 5, 5, 5, 5]) do |x|
    x.inject(:+)
  end

A similar notation as the array notation above can be used for lower dimensions using ruby operator '*'. This is a little less efficient though.

  arr = MDArray.fromfunction("double", [5, 5, 5, 5, 5]) do |*x|
    x.inject(:+)
  end

#-------------------------------------------------------------------------------------
#
#-------------------------------------------------------------------------------------

should "create array from linspace" do

  arr = MDArray.linspace("double", 0, 2, 9)
  assert_equal(0.0, arr[0])
  assert_equal(0.5, arr[2])
  assert_equal(1.0, arr[4])
  assert_equal(1.5, arr[6])
  assert_equal(2.0, arr[8])

end

#-------------------------------------------------------------------------------------
#
#-------------------------------------------------------------------------------------

should "create array with ones" do

  # creates an array with all 1's
  ones = MDArray.ones("byte", [2, 2, 2, 2])
  assert_equal(1, ones[1, 1, 1, 1])
  assert_equal(1, ones[1, 0, 1, 0])

end

#-------------------------------------------------------------------------------------
#
#-------------------------------------------------------------------------------------

should "create array with given value" do

  # creates an array with a given value and type
  fives = MDArray.init_with("double", [3, 3], 5.34)
  assert_equal(5.34, fives[2, 1])
  assert_equal(5.34, fives[0, 2])

end

#-------------------------------------------------------------------------------------
#
#-------------------------------------------------------------------------------------

should "allow filling the array with data" do

  # fill b with a given value
  b = MDArray.double([2, 3], [0, 1, 2, 3, 4, 5])
  b.fill(5.47)
  b.each do |elmt|
    assert_equal(5.47, elmt)
  end

  # typed_arange does the same as arange but for arrays of other type
  g = MDArray.typed_arange("double", 6)
  g.reshape!([2, 3])
  b.fill(g)
  assert_equal(b.to_string, g.to_string)

end

#-------------------------------------------------------------------------------------
#
#-------------------------------------------------------------------------------------

should "create boolean arrays" do

  bool = MDArray.boolean([2, 2])
  bool[0, 0] = true
  assert_raise ( RuntimeError ) { bool[0, 1] = 10.0 }
  assert_equal(bool[0, 0], true)
  bool[0, 1] = false
  assert_equal(bool[0, 1], false)

end

#-------------------------------------------------------------------------------------
#
#-------------------------------------------------------------------------------------

should "create string arrays" do

  sarray = MDArray.string([2, 3], ["hello", "this", "is", "a", "string", "array"])
  assert_equal(6, sarray.size)
  assert_equal("hello", sarray.get([0, 0]))
  assert_equal("hello this is a string array ", sarray.to_string)

end

#-------------------------------------------------------------------------------------
# A struct array is an array of pointers to structures
#-------------------------------------------------------------------------------------

should "create struct arrays" do

  m = Hash.new
  m[:hello] = "world"
  m[:test] = 1.23

  struct = MDArray.structure([10])
  struct[0] = m
  struct.print

end

Clone this wiki locally