Technical Details

 

1 Introduction

This page gives some brief details on some of the more technical aspects of the broadcast package. This page will probably not be of much interest for most users; this page is presented nonetheless for the sake of completeness and transparency.

 

2 Overload method tables

The overloaded operators from the ‘broadcast’ package attempts to mimic the base operators accurately, except that broadcasting is used (obviously).

The following tables overview the behaviour of the operators.

 

2.1 Regular Arithmetic operators

If both sides of the operators given here are numeric or logical, the following holds:

operator action functions like
+ add bc.d
- substract bc.d
* multiply bc.d
/ divide bc.d
^ raise to power bc.d
%/% floored integer divide bc.i
%% modulo bc.i

 

2.2 Complex Arithmetic operators

If at least one of the arguments of the operators given here are complex, the following holds:

operator action functions like
+ add bc.cplx
- substract bc.cplx
* multiply bc.cplx
/ divide bc.cplx

 

2.3 Logical Operators

If both sides of the operators given here are complex, numeric or logical, the following holds:

operator action functions like
& and bc.b
| or bc.b

 

2.4 Bit-wise operators

If both sides of the operators given here are type of raw, the following holds:

operator action functions like
& bit-wise and bc.bit
| bit-wise or bc.bit

 

2.5 Relational operators

For relational operators, ‘broadcast’ first finds the “highest” (or most complex) atomic type of both sides, coerces both sides to said type, and then performs the broadcasted relational operation. Recursive types are not supported.

The types, from complex to simple, are: character, complex, numeric (also known as double), integer, logical, and raw.

 

3 Compatibility with Custom Methods

Consider a classed vector like the following:

x <- 1:10
class(x) <- "someclass"
print(class(x))
#> [1] "someclass"

Setting a vector or array as a “broadcaster” using the broadcaster()<- function, will add the “broadcaster” class attribute;
but note that the “broadcaster” class will be placed last in the class attribute:

broadcaster(x) <- TRUE
print(class(x))
#> [1] "someclass"   "broadcaster"

Notice how the “broadcaster” class comes after the “someclass” class.

Let’s make a quick ’n dirty (and not at all smart) + method for “somceclass”, which will just print some text:

`+.someclass` <- function(e1, e2) {print("someclass method called")}

What will happen? Will the + method for the broadcaster be called, or the + method for “someclass”?
Well, since the “broadcaster” class is intentionally placed last, operator methods for the overriding classes will always “win” over the “broadcaster” class. In this case, if there are methods for the base operators (such as +, -, etc.) for the “someclass” class, the method for “someclass” will be called, rather than the broadcaster method:

y <- array(1:10, c(1, 10))
class(y) <- "someclass"
broadcaster(y) <- TRUE
x + y
#> [1] "someclass method called"
#> [1] "someclass method called"

Notice how the + method for “someclass” is called, rather than the + method for the broadcaster.

This ensures compatibility with custom classes.
After all, some classes may not be intended to be broadcasted, or they may not be compatible with the base ‘R’ way of arithmetic (which is what ‘broadcast’ mimics), or they may need careful handling of attributes.

Authors of these custom classes can make their methods use broadcasting if a broadcaster vector/array is detected, if they so choose; only they know what is appropriate for their classes and what isn’t.

 

4 Pre-processing dimensions for broadcasting

Before broadcasting occurs in the bc.* methods and the infix operators, the dimensions of the involved arrays are pre-processed internally to simplify the dimensions. Although this pre-processing increases the overload of the broadcasting methods a bit, it also improves the speed of iterating over the array elements.

The pre-processing of the dimensions consists of the following steps:

  • Normalizing dimensions so that they get the same size.
  • Nullifying dimensions if the dimensions are not necessary (for example if no actual broadcasting will occur).
  • Dropping dimensional elements that are equal to 1 in both arrays.
  • Merging dimensions that can be merged to reduce the number of dimensions without changing the order of the array elements and without overflowing the maximum dimension sizes (2^31 - 1). For example, if x has dimensions {1, 10, 10, 1} and y has dimensions {10, 1, 1, 10}, these dimensions can be merged to become {1, 100, 1} for x and {10, 1, 10} for y.

Pre-processing the dimensions does not affect the actual arrays involved in the operation itself, and neither is any copy made of the involved arrays. Moreover, the dimensions, dimension names, and other relevant attributes will still be correct in the resulting output.

After pre-processing, the “dimensional mode” of the simplified arrays is determined. The following modes are available:

  • Vector mode: This mode is used when no actual broadcasting occurs, and thus the arrays can be treated as dimensionless vectors.
  • Outer mode: This mode is used when broadcasting the simplified dimensions constitute a simple outer operation.
  • Sandwich mode: This mode is used when the broadcasted operation involves a array and a (directional) vector, where all dimensions of the array are greater than or equal to the dimensions of the vector. Note that a directional vector is an array where (after simplification) one dimension is greater than 1, and all other dimensions are equal to 1.
  • General mode: This mode is used for general broadcasting, that cannot be simplified to any of the above cases.