module Phase::MultiIndexable(T)

Overview

The MultiIndexable module provides a unified interface for multidimensional array types, much like how Indexable provides a standard corpus of methods for one-dimensional collections.

How to Implement a MultiIndexable

Implementing MultiIndexable will require that you provide a #shape and #unsafe_fetch_element method, however this is the bare minimum. For a performant implementation, you should consider overriding #unsafe_fetch_chunk, #fast_each, and #size in that order of importance (and more as you see fit).

Included Modules

Direct including types

Defined in:

multi_indexable.cr
multi_indexable/chunk_iterator.cr
multi_indexable/chunk_region_iterator.cr
multi_indexable/elem_coord_iterator.cr
multi_indexable/elem_iterator.cr
multi_indexable/formatter/formatter.cr
multi_indexable/formatter/settings.cr
multi_indexable/tiling_lex_iterator.cr

Class Method Summary

Macro Summary

Instance Method Summary

Class Method Detail

def self.each_with(*args : *U, &) forall U #

[View source]

Macro Detail

macro coord_splat_overload(name) #

[View source]
macro def_elementwise_binary(name) #

[View source]
macro def_elementwise_unary(name) #

[View source]
macro region_splat_overload(name) #

TODO rename these? These primarily exist for user clarity (could easily have all these methods forward all their args to a single funciton to handle tuple-packaging, but would leave a rather opaque method signature behind)


[View source]

Instance Method Detail

def %(other : MultiIndexable(U)) forall U #

Invokes #% element-wise between self and other, returning an NArray that contains the results.


[View source]
def %(other) #

Invokes #%(other) on each element in self, returning an NArray that contains the results.


[View source]
def &(other : MultiIndexable(U)) forall U #

Invokes #& element-wise between self and other, returning an NArray that contains the results.


[View source]
def &(other) #

Invokes #&(other) on each element in self, returning an NArray that contains the results.


[View source]
def &*(other : MultiIndexable(U)) forall U #

Invokes #&* element-wise between self and other, returning an NArray that contains the results.


[View source]
def &*(other) #

Invokes #&*(other) on each element in self, returning an NArray that contains the results.


[View source]
def &**(other : MultiIndexable(U)) forall U #

Invokes #&** element-wise between self and other, returning an NArray that contains the results.


[View source]
def &**(other) #

Invokes #&**(other) on each element in self, returning an NArray that contains the results.


[View source]
def &+(other : MultiIndexable(U)) forall U #

Invokes #&+ element-wise between self and other, returning an NArray that contains the results.


[View source]
def &+(other) #

Invokes #&+(other) on each element in self, returning an NArray that contains the results.


[View source]
def &-(other : MultiIndexable(U)) forall U #

Invokes #&- element-wise between self and other, returning an NArray that contains the results.


[View source]
def &-(other) #

Invokes #&-(other) on each element in self, returning an NArray that contains the results.


[View source]
def *(other : MultiIndexable(U)) forall U #

Invokes #* element-wise between self and other, returning an NArray that contains the results.


[View source]
def *(other) #

Invokes #*(other) on each element in self, returning an NArray that contains the results.


[View source]
def **(other : MultiIndexable(U)) forall U #

Invokes #** element-wise between self and other, returning an NArray that contains the results.


[View source]
def **(other) #

Invokes #**(other) on each element in self, returning an NArray that contains the results.


[View source]
def +(other : MultiIndexable(U)) forall U #

Invokes #+ element-wise between self and other, returning an NArray that contains the results.


[View source]
def +(other) #

Invokes #+(other) on each element in self, returning an NArray that contains the results.


[View source]
def + #

[View source]
def -(other : MultiIndexable(U)) forall U #

Invokes #- element-wise between self and other, returning an NArray that contains the results.


[View source]
def -(other) #

Invokes #-(other) on each element in self, returning an NArray that contains the results.


[View source]
def - #

[View source]
def /(other : MultiIndexable(U)) forall U #

Invokes #/ element-wise between self and other, returning an NArray that contains the results.


[View source]
def /(other) #

Invokes #/(other) on each element in self, returning an NArray that contains the results.


[View source]
def //(other : MultiIndexable(U)) forall U #

Invokes #// element-wise between self and other, returning an NArray that contains the results.


[View source]
def //(other) #

Invokes #//(other) on each element in self, returning an NArray that contains the results.


[View source]
def <(other : MultiIndexable(U)) forall U #

Invokes #< element-wise between self and other, returning an NArray that contains the results.


[View source]
def <(other) #

Invokes #<(other) on each element in self, returning an NArray that contains the results.


[View source]
def <=(other : MultiIndexable(U)) forall U #

Invokes #<= element-wise between self and other, returning an NArray that contains the results.


[View source]
def <=(other) #

Invokes #<=(other) on each element in self, returning an NArray that contains the results.


[View source]
def <=>(other : MultiIndexable(U)) forall U #

Invokes #<=> element-wise between self and other, returning an NArray that contains the results.


[View source]
def <=>(other) #

Invokes #<=>(other) on each element in self, returning an NArray that contains the results.


[View source]
def ==(other : self) : Bool #

Returns true if both the shape and elements of self and other are equal.

NArray.new([1, 2]) == NArray.new([1, 2]) # => true
NArray.new([[1], [2]]) == NArray.new([1, 2]) # => false
NArray.new([8, 2]) == NArray.new([1, 2]) # => false

[View source]
def =~(value) : MultiIndexable(Bool) #

TODO DISCUSS this syntax as an alternative or supplement to elem_eq


[View source]
def >(other : MultiIndexable(U)) forall U #

Invokes #> element-wise between self and other, returning an NArray that contains the results.


[View source]
def >(other) #

Invokes #>(other) on each element in self, returning an NArray that contains the results.


[View source]
def >=(other : MultiIndexable(U)) forall U #

Invokes #>= element-wise between self and other, returning an NArray that contains the results.


[View source]
def >=(other) #

Invokes #>=(other) on each element in self, returning an NArray that contains the results.


[View source]
def [](region_literal : Indexable, drop : Bool = MultiIndexable::DROP_BY_DEFAULT) #

Copies the elements described by region into a new MultiIndexable. If region does not describe a valid region of this MultiIndexable, this method will raise either a DimensionError (in the case of an improper number of dimensions) or a ShapeError (in the case where the number of dimensions is correct, but the region is not meaningful for this MultiIndexable's shape.

Note: this method has a tuple accepting overload, as well, which makes the syntax much more intuitive. The following example contains both versions, but please note the difference.

narr = NArray.new([[1, 2, 3], [4, 5, 6], [7, 8, 9]])

# Select only the first row:
narr[[0], drop: true] # => NArray[1, 2, 3]

# Unless you need to explicitly disable dropping, use the tuple overload:
narr[0] # => NArray[1, 2, 3] (drop is true by default)

# Select only the first column:
narr[.., 0] # => NArray[1, 2, 3]

# Select only the first column, without dropping dimensions:
# (in this case, we can't use the tuple accepting overload, hence the extra brackets)
narr[[.., 0], drop: false] # => NArray[[1], [2], [3]]

# Equivalently to the above, using anything other than an integer will bypass
# dropping:
narr[.., 0..0] # => NArray[[1], [2], [3]]

# Select only elements from both even-numbered rows and columns:
narr[0..2.., 0..2..] # => NArray[[1, 3], [7, 9]]

# This method raises a DimensionError when there is a dimensions mismatch:
narr[0, 1, 2, 3] # => DimensionError

# This method raises a ShapeError when there is a shape mismatch:
narr[1..100, 2..30] # => ShapeError

[View source]
def [](region : IndexRegion) #

IndexRegion accepting form of #[](region_literal : Indexable, drop : Bool). Note that region is what controls the dimension dropping behaviour, here.


[View source]
def [](*region_literal, drop : Bool = MultiIndexable::DROP_BY_DEFAULT) #

Tuple-accepting overload of #[](region_literal : Indexable, drop : Bool).


[View source]
def []?(bool_mask : MultiIndexable(Bool)) : MultiIndexable(T | Nil) #

Returns a MultiIndexable that draws from self where bool_mask is true, but contains nil where bool_mask is false. If bool_mask has a different shape than self, this method will raise a ShapeError.

narr = NArray[3, 4, 5]
mask = NArray[false, true, true]
narr[mask]? # => NArray[nil, 4, 5]

oversized_mask = NArray[false, true, true, false]
narr[oversized_mask]? # => ShapeError

[View source]
def []?(region : Indexable, drop : Bool = MultiIndexable::DROP_BY_DEFAULT) : MultiIndexable(T) | Nil #

Copies the elements in region to a new MultiIndexable if #has_region?(region) is true, and returns nil otherwise.

narr = NArray[[1, 2, 3], [4, 5, 6]]
narr[1.., 10..12]? # => nil
narr[0.., 1..2]? # => NArray[[2, 3], [5, 6]]

[View source]
def []?(region : IndexRegion) : MultiIndexable(T) | Nil #

[View source]
def []?(*region_literal, drop : Bool = MultiIndexable::DROP_BY_DEFAULT) #

Tuple-accepting overload of #[]?(region_literal : Indexable, drop : Bool).


[View source]
def ^(other : MultiIndexable(U)) forall U #

Invokes #^ element-wise between self and other, returning an NArray that contains the results.


[View source]
def ^(other) #

Invokes #^(other) on each element in self, returning an NArray that contains the results.


[View source]
def |(other : MultiIndexable(U)) forall U #

Invokes #| element-wise between self and other, returning an NArray that contains the results.


[View source]
def |(other) #

Invokes #|(other) on each element in self, returning an NArray that contains the results.


[View source]
def ~ #

[View source]
def apply : ApplyProxy #

[View source]
def apply! : InPlaceApplyProxy #

[View source]
def colex_each : ElemIterator #

Returns an iterator that will yield each element of self in colexicographic (column-major) order.

narr = NArray[[1, 2, 3], [4, 5, 6]]
iter = narr.each
iter.next # => 1
iter.next # => 4
iter.next # => 2
iter.next # => 5
iter.next # => 3
iter.next # => 6
iter.next # => Iterator::Stop

#colex_each can be manipulated the same ways as #each. See #each or MultiIndexable::ElemIterator for more information.


[View source]
def colex_each(&) : Nil #

Block accepting form of #colex_each.


[View source]
def colex_each_coord : ColexIterator #

Returns an iterator that will yield each coordinate of self in colexicographic (column-major) order.

narr = NArray[[1, 2, 3], [4, 5, 6]]
iter = narr.each_coord
iter.next # => [0, 0]
iter.next # => [0, 1]
iter.next # => [0, 2]
iter.next # => [1, 0]
iter.next # => [1, 1]
iter.next # => [1, 2]
iter.next # => Iterator::Stop

[View source]
def colex_each_coord(&) : Nil #

Block accepting form of #colex_each_coord.


[View source]
def dimensions : Int #

Returns the number of dimensions that this MultiIndexable is embedded in. This can equally be seen by the number of indices required to uniquely specify a coordinate into this MultiIndexable, and is always equal to shape.size

NArray.new([1, 2]).dimensions # => 1
NArray.new([[1, 2], [3, 4]]).dimensions # => 2
NArray.new([[[1]]]).dimensions # => 3

[View source]
def each : ElemIterator #

Returns an iterator that will yield each element of self in lexicographic (row-major) order.

narr = NArray[[1, 2, 3], [4, 5, 6]]
iter = narr.each
iter.next # => 1
iter.next # => 2
iter.next # => 3
iter.next # => 4
iter.next # => 5
iter.next # => 6
iter.next # => Iterator::Stop

#each can also be chained with other calls to manipulate its behaviour - for example each.with_coord.reverse_each. See MultiIndexable::ElemIterator for more information.


[View source]
def each(&) : Nil #

Block accepting form of #each.


[View source]
def each_coord : LexIterator #

Returns an iterator that will yield each coordinate of self in lexicographic (row-major) order.

narr = NArray[[1, 2, 3], [4, 5, 6]]
iter = narr.each_coord
iter.next # => [0, 0]
iter.next # => [0, 1]
iter.next # => [0, 2]
iter.next # => [1, 0]
iter.next # => [1, 1]
iter.next # => [1, 2]
iter.next # => Iterator::Stop

[View source]
def each_coord(&) : Nil #

Block accepting form of #each_coord.


[View source]
def each_slice(axis = 0) : Iterator #

Returns an Iterator equivalent to the method #each_slice(axis, &block).


[View source]
def each_slice(axis = 0, &) #

Yields the slices of this MultiIndexable along a given axis to the provided block. The elements returned in each slice are the ones with a constant index along the specified axis.

narr = NArray[[1, 2, 3], [4, 5, 6]]

#          axis one ->
#
# axis      0 1 2
# zero   0 [1 2 3]
#  |     1 [4 5 6]
#  v

# defaults to axis = 0, so the slices will be the rows.
narr.each_slice do |slice|
  # in loop 0, slice will be NArray[1, 2, 3], because those elements have coords [0, ...]
  # in loop 1, slice will be NArray[4, 5, 6], because those elements have coords [1, ...]
end

# here we pick axis = 1, so the slices will be the columns.
narr.each_slice(axis: 1) do |slice|
  # in loop 0, slice will be NArray[1, 4], because those elements have coords [..., 0]
  # in loop 1, slice will be NArray[2, 5], because those elements have coords [..., 1]
  # in loop 0, slice will be NArray[3, 6], because those elements have coords [..., 2]
end

[View source]
def each_with(*args, &) #

Iterates over tuples of elements drawn from self and args, where args contains other MultiIndexables you wish to access. This is effectively an n-dimensional analogue of Enumerable#zip.

narr_1 = NArray[[1, 2], [3, 4]]
narr_2 = NArray[[:a, :b], [:c, :d]]

narr_1.each_with(narr_2) { |el_1, el_2| print el_1, el_2 }
print "\n"
# Output: 1a2b3c4d

# When an argument that is not a MultiIndexable is passed,
# #each_with behaves like Object#tap, passing the value into the block.
narr_1.each_with(narr_2, "some other value I want in the block") do |*els|
    puts els 
end

# Output:
# {1, :a, "some other value I want in the block"}
# {2, :b, "some other value I want in the block"}
# {3, :c, "some other value I want in the block"}
# {4, :d, "some other value I want in the block"}

narr_3 = NArray[[1, 2]] # This has a different shape than narr_1!
narr_1.each_with(narr_3) {} # ShapeError

[View source]
def each_with_coord : ElemAndCoordIterator #

Returns an iterator that will yield tuples of the elements and coords comprising self in lexicographic (row-major) order.

narr = NArray[[1, 2, 3], [4, 5, 6]]
iter = narr.each_with_coord
iter.next # => {1, [0, 0]}
iter.next # => {2, [0, 1]}
iter.next # => {3, [0, 2]}
iter.next # => {4, [1, 0]}
iter.next # => {5, [1, 1]}
iter.next # => {6, [1, 2]}
iter.next # => Iterator::Stop

This method is a convenience included to mirror Indexable#each_with_index. If you're looking for a colexicographic version, use #colex_each.with_coord.


[View source]
def each_with_coord(&) : Nil #

Block accepting form of #each_with_coord.


[View source]
def empty? : Bool #

Returns true if and only if this MultiIndexable spans no elements.

NArray.new([1, 2, 3]).empty? # => false
NArray.new([]).empty? # => true

[View source]
def ensure_writable #

[View source]
def eq(other : MultiIndexable(U)) : MultiIndexable(Bool) forall U #

TODO rename to elem_eq Produces an NArray(Bool) (by default) describing which elements of self and other are equal.


[View source]
def eq(value) : MultiIndexable(Bool) #

[View source]
def equals?(other : MultiIndexable, &) : Bool #

Returns true if the block returns true for each pair of elements (that share a coordinate) from self and other.

narr_1 = NArray[[1, 2], [3, 4]]
narr_2 = NArray[[2, 3], [4, 5]]

narr_1.equals?(narr_1.clone) { |a, b| a == b } # => true
narr_1.equals?(narr_2) { |a, b| a == b } # => false

# The block doesn't neccessarily have to involve equality,
# just any pairwise comparison you want to evaluate globally.
narr_1.equals?(narr_2) { |a, b| a < b } # => true

[View source]
def fast_each : Iterator(T) #

Returns an Iterator over the elements in this MultiIndexable that will iterate in the fastest order possible. For most implementations, it is very likely that #each will be just as fast. However, certain implementations of MultiIndexable may have substantial performance differences. As a rule of thumb, this method is only worth using if the MultiIndexable you call it on explicitly mentions that you should.

NArray[1, 2, 3].fast_each.each do |el|
  # ...
end

[View source]
def fast_each(&) : Nil #

Block accepting form of #fast_each.


[View source]
def first : T #

Returns the element at the zero coordinate (position 0 along every axis). For example:

# create the following matrix:
# [5 2]
# [8 3]
narr = NArray.new([[5, 2], [8, 3]])

# extract the top-left element (coordinate [0, 0])
narr.first # => 5

[View source]
def get(coord : Indexable) : T #

Shorthand for #get_element. :ditto:


[View source]
def get(*coord : Int) #

Tuple-accepting overload of #get.


[View source]
def get_available(region_literal : Indexable, drop : Bool = DROP_BY_DEFAULT) #

Returns a MultiIndexable containing elements whose coordinates belong both to #shape and region_literal. This method is very similar to #get_chunk(region_literal : Indexable, drop : Bool), except that it trims the region_literal down to a valid size automatically.

narr = NArray.new([[1, 2, 3], [4, 5, 6]])

narr.get_chunk(1..5, 1) # => ShapeError (this chunk is not contained in a 2x3 MultiIndexable
narr.get_available(1..5, 1) # => NArray[5]

[View source]
def get_available(region : IndexRegion, drop : Bool = DROP_BY_DEFAULT) #

[View source]
def get_available(*region_literal, drop : Bool = MultiIndexable::DROP_BY_DEFAULT) #

[View source]
def get_chunk(coord : Indexable, region_shape : Indexable(I)) forall I #

TEST Extracts a chunk given a shape (region_shape) and the coord in that region with the smallest value in each axis.

narr = NArray.new([[1, 2, 3], [4, 5, 6], [7, 8, 9]])

narr.get_chunk([1, 1], [2, 2]) # => NArray[[5, 6], [8, 9]]
narr.get_chunk([1, 0], [1, 3]) # => NArray[[4, 5, 6]]
narr.get_chunk([1, 0], [10, 10]) # => ShapeError
narr.get_chunk([0], [1]) # => DimensionError

[View source]
def get_chunk(region_literal : Indexable, drop : Bool = DROP_BY_DEFAULT) #

A more verbose overload of #[](region_literal : Indexable, drop : Bool). This is just syntactic sugar, and can be more readable in certain applications.


[View source]
def get_chunk(region : IndexRegion) : MultiIndexable(T) #

IndexRegion accepting form of #get_chunk(region_literal : Indexable, drop : Bool). Note that region is what controls the dimension dropping behaviour, here.


[View source]
def get_chunk(*region_literal, drop : Bool = MultiIndexable::DROP_BY_DEFAULT) #

[View source]
def get_element(coord : Indexable) : T #

Retrieves the element specified by coord, throwing an error if coord is out-of-bounds for self.

narr = NArray[['a', 'b'], ['c', 'd']]
narr.get_element([0, 1]) # => 'b'
narr.get_element([1, 0]) # => 'c'
narr.get_element([0]) # => DimensionError
narr.get_element([0, 10]) # => IndexError

[View source]
def get_element(*coord : Int) #

Tuple-accepting overload of #get_element.


[View source]
def has_coord?(coord : Indexable) : Bool #

Returns true if coord is a valid coordinate in this MultiIndexable. Any coordinate for which #has_coord? returns true can be used in #get. A coordinate for which #has_coord? returns false is out of bounds.

# creates the following matrix:
# [1 2 3]
# [4 5 6]
narr = NArray.build([2, 3]) { |_, idx| idx + 1 }

narr.has_coord?([0, 0]) # => true
narr.get([0, 0]) # => 1

narr.has_coord?([-2, 1]) # => true
narr.get(-2, 1) # => 2

narr.has_coord?([-2]) # => true
narr.get(-2) # => DimensionError

[View source]
def has_coord?(*coord : Int) #

Tuple-accepting overload of #has_coord?.


[View source]
def has_region?(region_literal : Indexable, drop : Bool = DROP_BY_DEFAULT) : Bool #

Returns true if all the coordinates spanned by region_literal are valid coordiantes in this MultiIndexable. In a more geometric sense, an IndexRegion can be considered as a lattice of points (coordinates), and #shape can be considered as a bounding box for those coordinates. If every coordinate within region (each point on that lattice) is inside of the bounding box, then #has_region will return true.

narr = NArray.build([10, 3]) { |_, idx| idx }

# First, we'll make an IndexRegion that fits in the above. This IndexRegion
# contains all coordinates with a row equal to 2, 3, or 4, and a column
# equal to 0, 1, or 2.
valid = [2..4, 0...3]

# narr has 10 rows and 3 columns, so that region is definitely
# contained in it.
narr.has_region?(valid) # => true

# now, we can use that IndexRegion safely.
LexIterator(Int32).new(valid).each do |coord|
  narr.unsafe_fetch_element(coord) # this is definitely defined!
end

# Now we'll create an IndexRegion that's way too big for narr:
invalid = [100, 2..8]
narr.has_region?(invalid) # => false

# The region doesn't fit - so:
narr.get_chunk(invalid) # => raises an IndexError

[View source]
def has_region?(region : IndexRegion) : Bool #

IndexRegion accepting form of #has_region?(region_literal)


[View source]
def has_region?(*region_literal, drop : Bool = MultiIndexable::DROP_BY_DEFAULT) #

[View source]
def hash(hasher) #

[View source]
def last : T #

Returns the element with the largest ordinate in each axis (the element at the largest coordinate). For example:

# create the following matrix:
# [5 2]
# [8 3]
narr = NArray.new([[5, 2, 1], [8, 3, 4]])

# extract the bottom-right element (coordinate [1, 2])
narr.last # => 4

[View source]
def map(&block : T -> R) : MultiIndexable(R) forall R #

Returns a MultiIndexable with the results of running the block against each element of self.

narr = NArray[[1, 2, 3], [4, 5, 6]]
res = narr.map { |el| el.to_s } # => NArray[["1", "2", "3"], ["4", "5", "6"]]

[View source]
def map!(&block : T -> T | MultiIndexable(T)) : MultiIndexable(T) #

TODO docs, test


[View source]
def map_with(*args : *U, &) forall U #

[View source]
def map_with(*args, &) #

Iterates over tuples of elements from self and args, and creating a MultiIndexable containing the output of the block to which those tuples are provided.


[View source]
def map_with!(*args : *U, &) forall U #

[View source]
def map_with_coord(&) #

Returns a MultiIndexable with the results of running the block against each element and coordinate comprising self.

narr = NArray[[1, 2, 3], [4, 5, 6]]
narr.map_with_coord do |el, coord|
  el + coord.sum
end # => NArray[[1, 3, 5], [5, 7, 9]]

[View source]
def map_with_coord!(&block : T -> MultiIndexable(T)) #

TODO docs DISCUSS is this good behaviour?


[View source]
def permute(*args) : MultiIndexable(T) #

[View source]
def process(&block : T -> R) : ProcView(self, T, R) forall R #

[View source]
def process(proc : Proc(T, R)) : ProcView(self, T, R) forall R #

[View source]
def reshape(*args) : MultiIndexable(T) #

[View source]
def reverse(*args) : MultiIndexable(T) #

[View source]
def sample(n : Int, random = Random::DEFAULT) : Enumerable(T) #

Returns a collection of n elements picked at random from this MultiIndexable. This method works by randomly generating coordinates and returning the elements at those coordinates. There is no guarantee that the coordinates generated will be distinct from one another.

NArray.new([[1, 2], [3, 4]]).sample(5) # => Enumerable(Int32)
NArray.new([[1, 2], [3, 4]]).sample(5).to_a # => [4, 2, 4, 3, 2]
NArray.new([[1, 2], [3, 4]]).sample(5).to_a # => [1, 3, 2, 4, 1]
NArray.new([[1, 2], [3, 4]]).sample(5).to_a # => [2, 3, 1, 1, 3]

[View source]
def sample(random = Random::DEFAULT) : T #

Returns an element picked at random from this MultiIndexable.

NArray.new([[1, 2], [3, 4]]).sample # => 3
NArray.new([[1, 2], [3, 4]]).sample # => 1
NArray.new([[1, 2], [3, 4]]).sample # => 2

[View source]
def scalar? : Bool #

Returns true if this MultiIndexable contains only a single element.

NArray.new([1]).scalar? # => true
NArray.new([1, 2]).scalar? # => false
NArray.new([[1]]).scalar? # => true
NArray.new([]).scalar? # => false

[View source]
def shape : Array #

Returns the capacity of each axis spanned by self. For example, a matrix with 4 rows and 2 columns will have the shape [4, 2]. This must always return a clone of the actual shape, and is safe to mutate without affecting the MultiIndexable.


[View source]
def size #

Returns the total number of elements in this MultiIndexable. This quantity is always equal to shape.product. However, this method is almost always more performant than computing the product directly.

NArray.new(['a', 'b', 'c']).size # => 3
NArray.new([[0, 1], [1, 0]]).size # => 4

[View source]
def slices(axis = 0) : Indexable #

Returns an Indexable collection of the slices returned by #each_slice.


[View source]
def tile(counts : Enumerable(Int)) : MultiIndexable #

counts specifies how many times to copy the tile in each axis. If it is the wrong size, #tile will return a DimensionError.

unit = NArray[[1, 2], [3, 4]]

puts unit.tile([2, 3])
# 4x6 Phase::NArray(Int32)
# [[1, 2, 1, 2, 1, 2],
#  [3, 4, 3, 4, 3, 4],
#  [1, 2, 1, 2, 1, 2],
#  [3, 4, 3, 4, 3, 4]]

[View source]
def tile(*counts : Int) #

Tuple-accepting overload of #tile(counts : Enumerable).

unit = NArray[[1, 2], [3, 4]]

puts unit.tile(2, 3)
# 4x6 Phase::NArray(Int32)
# [[1, 2, 1, 2, 1, 2],
#  [3, 4, 3, 4, 3, 4],
#  [1, 2, 1, 2, 1, 2],
#  [3, 4, 3, 4, 3, 4]]

[View source]
def to_f : Float #

TEST Returns to_scalar.to_f. This method allows single-element MultiIndexables to be treated like numerics in many cases.

NArray.new([[0.5f32]]).to_f # => 0.5
NArray.new([[1], [2]]).to_f # raises ShapeError
NArray.new(["test"]).to_f # will not compile, as String has no #to_f method.

[View source]
def to_literal_s(io : IO) : Nil #

[View source]
def to_narr : NArray(T) #

Creates an NArray duplicate of this MultiIndexable.

# not_an_narray : MultiIndexable
narr = not_an_narray.to_narr # => NArray
not_an_narray.equals?(narr) { |el_1, el_2| el_1 == el_2 } # => true

[View source]
def to_s(io : IO, settings = Formatter::Settings.new) : Nil #

FIXME NArrayFormatter depends on buffer indices.


[View source]
def to_s(settings = Formatter::Settings.new) : String #

FIXME NArrayFormatter depends on buffer indices.


[View source]
def to_scalar : T #

If this MultiIndexable is a scalar (see #scalar?), #to_scalar will return the sole element that it contains. This method will raise a ShapeError if self.scalar? returns false.

NArray.new(['a']).to_scalar # => 'a'
NArray.new([['a', 'b'], ['c', 'd']]).to_scalar # raises ShapeError

[View source]
def to_scalar? : T | Nil #

Identical to #to_scalar, but returns nil in case of an error.

NArray.new(['a']).to_scalar # => 'a'
NArray.new([['a', 'b'], ['c', 'd']]).to_scalar # => nil

[View source]
def unsafe_fetch_chunk(region : IndexRegion) : MultiIndexable(T) #

Copies the elements described by region into a new MultiIndexable without performing any bounds checking. Unless you are sure that your region will fit inside of this MultiIndexable, you should opt to use #get_chunk instead.

This method may return any MultiIndexable - the default implementation will return an NArray, however implementers of other MultiIndexables are encouraged to override this method where it makes sense to do so.

This method's usage is identical to #get_chunk(region : IndexRegion), but it is slightly faster.


[View source]
abstract def unsafe_fetch_element(coord : Indexable) : T #

Returns the element at the provided coord, possibly mutating coord, without performing canonicalization or bounds-checking. This method cannot be used with negative coordinates, and is not safe unless you are certain your coordinate is already canonicalized.


[View source]
def view(region : Indexable | Nil | IndexRegion = nil) : View(self, T) #

[View source]
def view(*region) : View(self, T) #

[View source]