|
A
number of arithmetic operators are defined for Atom
Vectors (AV). In the following
A
is an AV of N atoms and D dimensions,
V
is an AV that has either N atoms and 1 dimension or
1 atom and D dimensions,
E
is an AV of N atoms and 3 dimensions and
s
is a scalar, i.e., an integer or a floating point
value.
The
binary operations are carried out side-by-side,
between corresponding elements of the left and
right operands. The operands are first checked and
the operation is carried out only if the operands
are conformal, or can be made conformal. The cross
product (Vector Product) requires operands to have
exactly three dimensions (and any number of atoms
as long as it is the same on both operands). The
cross product is carried out individually for each
atom; this is not a cross product between the two
operands. The scalar product equivalent is
implemented as a function:
DotPerAtom().
In
the first four operations, if the right operand is
a scalar (s),
the operation is performed side-by-side between
individual elements of the left operand and the
single scalar value. If necessary, the scalar value
is converted to a double precision floating point
number before use.
In
the Spread & Add and Spread & Multiply
operations, the elements of the right operand are
replicated either in the atom axis (if the number
of atoms in V
is one) or in the dimension axis (if the number of
dimensions in V
is one). This creates an augmented vector that is
conformal with the left AV.
As
long as operands are conformal, they can be of any
variants: Entire or Extract, Mapped or Anonymous.
The result AV is always an Entire AV and is either
Mapped or Anonymous following the left
operand.
The
binary operations are:
|
Operation
|
Result
|
Left
Operand
|
Operator
|
Right
Operand
|
|
Addition
|
A
|
A
|
+
|
A
or s
|
|
Subtraction
|
A
|
A
|
-
|
A
or s
|
|
Multiplication
|
A
|
A
|
*
|
A
or s
|
|
Division
|
A
|
A
|
/
|
A
or s
|
|
Exponentiation
|
A
|
A
|
**
|
s
|
|
Vector
Product
|
E
|
E
|
^
|
E
|
|
Spread
& Add
|
A
|
A
|
&
|
V
|
|
Spread
& Multiply
|
A
|
A
|
|
|
V
|
The
^,
&
and |
operators are normally bitwise exclusive-or,
bitwise and, and bitwise or operators respectively.
However the operations are not meaningful for
floating point numbers. Hence, we have redefined
these operators for more useful operations.
Caution: these operators have very low precedence
levels while the ^
and |
operators are usually intended to be used at higher
precedence. Parentheses should almost always be
used with these operators.
In
the division operation, the right operand is
checked. If at least one element is zero or if the
single scalar operand is zero, the error is flagged
and no division is attempted.
In
the exponentiation operation, the right operand
(the power) must be a scalar. If the power is an
integer, the exponentiation is accomplished by
nested multiplications. If the power is negative,
the left operand is checked for the presence of
zeroes; if there is at least one zero, the
operation is not carried out and the error is
flagged. The power is regarded as fractional if it
is written as a floating point number even if the
fractional part is zero. For fractional powers, the
left operand must not contain negative numbers. If
the power is negative the left operand is checked
for the presence of zeroes (as with negative
integer powers).
>>> from Yup.Taro.AtomVect import * >>> A = AtomVector( numatom=2, sample='UNIFORM' ) >>> A AtomVector[2:3] { -0.0277413 0.648549 0.382733 -0.0690634 -0.89526 0.656545 }
>>> B = AtomVector( numatom=2, value=1 ) >>> B AtomVector[2:3] { 1 1 1 1 1 1 }
>>> A+1 AtomVector[2:3] { 0.972259 1.64855 1.38273 0.930937 0.10474 1.65654 }
>>> A+B AtomVector[2:3] { 0.972259 1.64855 1.38273 0.930937 0.10474 1.65654 }
>>>
|
The
above example shows how a scalar right operand is
treated like an AV that is filled with only one
value, the scalar value.
The
unary operator abs()
is actually a function.
The
unary operations are:
|
Operation
|
Result
|
Operator
|
Operand
|
|
Negation
|
A
|
-
|
A
|
|
Absolute
Values
|
A
|
abs()
|
A
|
|
Reciprocation
|
A
|
~
|
A
|
The
reciprocation operator co-opts the bitwise
inversion operator. However, the inversion
operation is not meaningful for floating point
numbers. Thus, we have redefined the operator to
return the reciprocal which is after all an
inverse.
In
the reciprocation operations, the operand is first
checked for the presence of zeroes; if at least one
is present the operation is not carried out and the
error is flagged.
The
binary operations also have inplace
versions:
|
Operation
|
Result
|
Operator
|
Operand
|
|
Inplace
Addition
|
A
|
+=
|
A
or s
|
|
Inplace
Subtraction
|
A
|
-=
|
A
or s
|
|
Inplace
Multiplication
|
A
|
*=
|
A
or s
|
|
Inplace
Division
|
A
|
/=
|
A
or s
|
|
Inplace
Exponentiation
|
A
|
**=
|
s
|
|
Inplace
Cross Product
|
E
|
^=
|
E
|
|
Inplace
Spread & Add
|
A
|
&=
|
V
|
|
Inplace
Spread & Multiply
|
A
|
|=
|
V
|
These
operators are not supported by Python version 1.5.2
and earlier. However, as of version 2.1, Python
does not actually perform these operations in
place. For example, A += B is interpreted as A = A
+ B: the addition results in a new AV, the AV
referenced by A is removed and A is pointed to the
new AV. The scale()
method operates in-place and is much more efficient
than, and should be used in place of the
+=,
-=
and &=
operators. The shift()
method operates in-place and is much more efficient
than, and should be used in place of the
*=,
/=
and |=
operators. See
methods.
The
following example requires some mental arithmetic.
In the (inplace) Spread & Add example, the
column vector on the right is duplicated and added
to every column of the left operand. In the
(inplace) Spread and Multiply example, the row
vector on the right is duplicated and multiplied on
every row of the left operand.
>>> from Yup.Taro.AtomVect import * >>> A = AtomVector( numatom=2, numdimen=5, sample='GAUSSIAN' ) >>> x = AtomVector( numatom=2, numdimen=1, sample='UNIFORM' ) >>> y = AtomVector( numatom=1, numdimen=5, sample='UNIFORM' ) >>> A AtomVector[2:5] { -1.32624 1.0387 2.26008 0.0746788 -0.190893 -0.214545 -1.74816 0.961699 -0.475478 1.65758 }
>>> x AtomVector[2:1] { 0.209204 0.135594 }
>>> A &= x >>> A AtomVector[2:5] { -1.11704 1.2479 2.46928 0.283883 0.0183111 -0.0789514 -1.61257 1.09729 -0.339885 1.79318 }
>>> y AtomVector[1:5] { 0.74572 0.0846278 0.524339 -0.972106 -0.305643 }
>>> A |= y >>> A AtomVector[2:5] { -0.832998 0.105607 1.29474 -0.275965 -0.00559666 -0.0588756 -0.136468 0.575353 0.330404 -0.548071 }
>>>
|
Be
careful of operator precedence. Although we have
redefined some operators from their traditional
meanings, the precedence of these operators remain
as before. As is usual in such cases, copious use
of parentheses will remove any
ambiguity.
|