12. Binary operations
12.1. Overview
Binary operations between two objects can be applied in a variety of ways in
Horace. You can either use the symbolic form (+
, -
, *
, /
,
\
), or you can use the explicit function names (plus, minus, mtimes,
mrdivide
, mldivide
).
Most Horace data objects have binary operations defined between them,
and you can perform operations between objects of different classes,
as long as the dimensions of the “image” contained in the objects agree
and/or the size of “pixels” array are the same.
As described in the FAQ,
Horace data objects may contain up to two parts: an “image”
(histogrammed counts on a grid defined by the projection axes) and a set of “pixels”
(the raw counts from the data per detector element and energy bin).
The sqw
class has both sets of information, whereas d1d
, d2d
, d3d
, d4d
,
IX_dataset
and sigvar
objects contain only the “image” information,
and PixelData
objects contain only pixel information.
Thus, you can perform a binary operation between an sqw
object and any other Horace
object (as long as the “image” dimensions and the number of “pixels” agree) because it
contains both “image” and “pixel” data.
However, you cannot perform a binary operation between a d1d
and a PixelData
object
because one only has “image” data and the other only has “pixel” data.
You can perform a binary operation between a scalar number and any Horace data object.
You can perform a binary operation between a numeric Matlab array and any Horace data object which contains an “image” as long as the array dimension and image dimension matches.
The result of the binary operation has the same type as the more “complex” of the input objects,
where “complexity” equates to the amount of information an object carries.
That is, an sqw
object, which has both “image” and “pixel” data, is considered the most complex,
whereas a sigvar
object which just has count data (a signal and a variance array) but no
coordinate information is considered the least complex.
Thus, an operation between a sqw
and a d3d
object will yield a sqw
output.
Such an operation will not only produce a different “image”, it will also change the “pixels”
in order to maintain consistency between the two parts of the sqw
output object.
For example, if you perform the operation:
w_sub = w_sqw - w_d2d_bkg
where w_sqw
is a sqw
object and w_d2d_bkg
is a d2d
, then w_sub
will be a sqw
and the signal value of each “pixel” in w_sub
which contributes to a particular bin (histogram)
in the “image” of w_sqw
will be reduced by the value of the corresponding bin in w_d2d_bkg
divided by the number of “pixels” contributing to that bin.
Commonly used binary operations are described in more detail below.
12.2. sqw objects
Let us take for our example the addition operator +
. Our initial sqw
object is called w2
and has an attached pix
array.
Note
In the case of sqw
objects, binary operations transform the signal
and variance
elements of the pixel data.
12.2.1. Single sqw object
You operate on w2
with the following types:
w2_sqw
is ansqw
object with a pix array of identical size to the pix array ofw2
.wout = w2 - w2_sqw;
Note
A common use for this is when a background dataset has been created that maps exactly onto the real dataset, and needs to be subtracted.
w2_dnd
is adnd
object commensurate withw2.data
wout = w2 + w2_dnd;
num
is a numeric array of the same size as thew2.pix.signal
array ofw2
.wout = w2 + num;
scalar
is a single number to apply to all of the data inw2
.wout = w2 + scalar;
12.2.2. Array of sqw objects
You can use the same binary operation syntax as for single sqw objects, with the following conditions
w2_sqw
is either a scalarsqw
object of commensurate size to all of the sqw objects in the array, or an array ofsqw
objects with each object of commensurate size to its corresponding element ofw2
.
w2_dnd
same rules as for sqw of dnd type above.num
is a numeric array following the size rules above.scalar
the same scalar is subtracted from every pix array in the array of sqw objects.
12.3. dnd objects
Let us again take for our example the addition operator +
, with our initial
dnd
object called w2
.
Note
In the case of dnd
objects, binary operations transform the s
and
e
matrices
12.3.1. Single dnd object
You can add values to w2
in the following ways:
w2_sqw
is ansqw
object with adnd
(indata
) of identical size tow2
.wout = w2 + w2_sqw;
w2_dnd
is adnd
object commensurate withw2
.wout = w2 + w2_dnd;
num
is a numeric array of the same size as the arrays ofw1
.wout = w2 + num;
scalar
is a single number to apply to all of the data inw2
.wout = w2 + scalar;
12.3.2. Array of dnd objects
Similar to arrays of sqw objects.
As for sqw objects, arrays have to be the same size as the array of dnd objects with respectively commensurate array sizes, or a scalar object as the same size of each.
12.4. Tips and Tricks
12.4.1. List of operations and their equivalent code
The arithmetic operations above correspond to equivalent MATLAB functions. For reference, the corresponding functions are:
w1 + w2 --> plus(w1,w2);
w1 - w2 --> minus(w1,w2);
w1 * w2 --> mtimes(w1,w2);
w1 / w2 --> mrdivide(w1,w2);
w1 \ w2 --> mldivide(w1,w2);
w1 ^ w2 --> mpower(w1,w2);
Warning
The matrix operations *
, /
, \
and ^
(mtimes
,
mrdivide
, mldivide
and mpower
) are performed
element-by-element. So the equivalent MATLAB routines would be .*
,
./
, .\
and .^
respectively.
Warning
Binary operations between Horace objects, unlike arithmetic operations are not fully invertible.
If you do w_out = w1+w2
and w1_out = w_out-w2
w1_out ~= w1
.
Actually w1.data.s==w1_out.data.s
and w1.pix.signal==w1_out.pix.signal
but
errors are accumulated in each operation so:
w1.data.e<w1_out.data.e
and w1.pix.variance<w1_out.pix.variance
12.4.2. Binary operations manager
sqw
objects contain both pixels and image information and this information is consistent, i.e.
image is calculated from pixels and pixels are sorted within PixelData
array in such a way that the block of
pixels contributed into image bin(cell) is located in specific position of PixelData
array and this position can be
identified from image. The position i_1
of the first pixel contributing into image bin(cell) number n
is defined by
formula: i_1 = cumsum(sqw.data.npix(1:n-1))+1
and the last by: i_{end} = i_1+sqw.data.npix(n)-1
where
sqw.data.npix
refers to npix
array of dnd
object. Particular pixels positions between i_1
and i_{end}
are random.
When you perform binary operation between two objects containing pixels, the pixels have to be sorted within the bin to ensure
the operation is performed between correspondent pixels. In many cases, user may be sure that the operation is performed between two
objects with pixels ordered in the same way. For example, you calculate foreground and background on the same sqw
object and now want
to add them together. In this case, you may decrease time of your plus
operation by avoiding sorting pixels within the bins as follows:
my_cut = read_sqw(file_with_sqw);
w_fg = sqw_eval(my_cut,@my_foreground,foreground_parameters);
w_bg = sqw_eval(my_cut,@my_background,background_parameters);
w_sum = binary_op_manager(w_fg,w_bg,@plus,true);
If the last parameter of binary_op_manager
is set to true
it disables sorting pixels in bins while performing binary operations.
Warning
Use this option carefully. If you do binary operation between two objects with pixels sorted differently, the first result would look correct. Unfortunately, any future operations on the result of such operation may produce completely unexpected results.