NAME
Sidef::Types::Array::Vector - Mathematical vector operations in Sidef
DESCRIPTION
This class implements mathematical vectors with support for common vector operations including arithmetic, geometric distances, and angle calculations. A Vector is a subclass of Sidef::Types::Array::Array -- it's just an ordered array of elements, conventionally numeric, though this is a convention only and isn't enforced by the constructor.
For every arithmetic operator below, the other operand is treated as "vector-like" -- triggering per-element behavior instead of scalar-broadcast behavior -- if it's a Vector or a plain Array. This matters because a few of these operators behave quite differently depending on whether the other operand is a plain number or a vector/array: see "SPECIAL-CASE ARITHMETIC" below in particular.
SYNOPSIS
# Create vectors
var v1 = Vector(1, 2, 3)
var v2 = Vector(4, 5, 6)
# Element-wise arithmetic (vector or scalar operand)
say(v1 + v2) #=> Vector(5, 7, 9)
say(v1 - v2) #=> Vector(-3, -3, -3)
say(v1 + 5) #=> Vector(6, 7, 8)
# '*', '/', and '**' behave differently with a vector operand --
# see SPECIAL-CASE ARITHMETIC below
say(v1 * 2) #=> Vector(2, 4, 6) # scalar: element-wise
say(v1 * v2) #=> 32 # vector: dot product (a scalar!)
# Norms and distances
say(v1.norm) #=> 14 # sum of squares (NOT the magnitude)
say(v1.abs) # sqrt(14) -- the actual Euclidean magnitude
say(v1.dist(v2)) # Euclidean distance between v1 and v2
# Create special vectors
var zeros = Vector.zero(5) #=> Vector(0, 0, 0, 0, 0)
INHERITS
Inherits methods from Sidef::Types::Array::Array.
CONSTRUCTION
new
Vector(*elements)
Vector.new(*elements)
Creates a new Vector from elements, in order. Nothing enforces that elements are actually numeric -- that's a convention the rest of this class relies on, not a runtime check.
var v1 = Vector.new(1, 2, 3)
var v2 = Vector(1, 2, 3) # same as above
Aliases: call
zero
Vector.zero(n)
Creates a new vector of length n with every element set to 0.
say(Vector.zero(3)) #=> Vector(0, 0, 0)
ELEMENT-WISE ARITHMETIC
These operators behave consistently element-wise regardless of whether the other operand is a scalar or a vector/array: with a scalar, the same value is applied to every element; with a vector/array, corresponding elements are combined pairwise. If the two vectors have different lengths, only the overlapping range (up to the shorter one's length) contributes -- see the note under "SPECIAL-CASE ARITHMETIC" for how the distance/norm methods handle this too.
add
self.add(other)
self + other
Adds other to every element (if other is a scalar), or adds corresponding elements together (if other is a vector/array).
say(Vector(1, 2, 3) + 5) #=> Vector(6, 7, 8)
say(Vector(1, 2, 3) + Vector(4, 5, 6)) #=> Vector(5, 7, 9)
Aliases: operator +
sub
self.sub(other)
self - other
Subtracts other from every element (if other is a scalar), or subtracts corresponding elements (if other is a vector/array).
say(Vector(5, 7, 9) - 2) #=> Vector(3, 5, 7)
say(Vector(5, 7, 9) - Vector(1, 2, 3)) #=> Vector(4, 5, 6)
Aliases: operator -
and
self.and(other)
self & other
Bitwise ANDs every element with other (if other is a scalar), or ANDs corresponding elements together (if other is a vector/array).
say(Vector(5, 7, 3) & Vector(3, 5, 1)) #=> Vector(1, 5, 1)
Aliases: operator &
or
self.or(other)
self | other
Bitwise ORs every element with other (if other is a scalar), or ORs corresponding elements together (if other is a vector/array).
say(Vector(5, 7, 3) | Vector(3, 5, 1)) #=> Vector(7, 7, 3)
Aliases: operator |
xor
self.xor(other)
self ^ other
Bitwise XORs every element with other (if other is a scalar), or XORs corresponding elements together (if other is a vector/array).
say(Vector(5, 7, 3) ^ Vector(3, 5, 1)) #=> Vector(6, 2, 2)
Aliases: operator ^
SPECIAL-CASE ARITHMETIC
Unlike "ELEMENT-WISE ARITHMETIC" above, these three operators give a different kind of result when the other operand is a vector/array instead of a plain scalar -- not just a per-element version of the scalar case.
mul
self.mul(other)
self * other
If other is a scalar, scales every element by it, returning a new Vector. If other is a vector/array, returns their dot product instead -- the sum of the products of corresponding elements, as a single Number, not a per-element (Hadamard) product.
say(Vector(1, 2, 3) * 2) #=> Vector(2, 4, 6)
say(Vector(1, 2, 3) * Vector(2, 3, 4)) #=> 20 # 1*2 + 2*3 + 3*4, a Number
Aliases: operator *
div
self.div(other)
self / other
If other is a scalar, divides every element by it, returning a new Vector. If other is a vector/array, this is computed as self dotted with the element-wise reciprocal of other (self.mul(other's element-wise inverse)) -- again a single Number, not a per-element division.
say(Vector(10, 20, 30) / 2) #=> Vector(5, 10, 15)
say(Vector(10, 20, 30) / Vector(2, 4, 5)) #=> 16 # 10/2 + 20/4 + 30/5, a Number
Aliases: operator /, operator ÷
pow
self.pow(other)
self ** other
If other is a scalar, raises every element to that power, returning a new Vector. If other is a vector/array, returns the full outer-product Matrix of the two -- a matrix whose entry at row i, column j is self[i] ** other[j] -- not a per-element (matching-index) power.
say(Vector(2, 3, 4) ** 2) #=> Vector(4, 9, 16)
say(Vector(2, 3) ** Vector(1, 2))
#=> Matrix([2, 4],
#=> [3, 9]) # row i, col j = v1[i] ** v2[j]
Aliases: operator **
UNARY TRANSFORMATIONS
neg
self.neg
Returns a new vector with every element negated. Equivalent to multiplying by -1.
say(Vector(1, -2, 3).neg) #=> Vector(-1, 2, -3)
not
self.not
Returns a new vector with the bitwise complement of every element.
say(Vector(5, 7, 3).not)
floor
self.floor
Returns a new vector with every element rounded down to the nearest integer.
say(Vector(1.8, 2.3, -0.5).floor) #=> Vector(1, 2, -1)
ceil
self.ceil
Returns a new vector with every element rounded up to the nearest integer.
say(Vector(1.2, 2.8, -0.5).ceil) #=> Vector(2, 3, 0)
round
self.round(digits)
Returns a new vector with every element rounded to digits decimal places (or to the nearest integer, if digits is omitted).
say(Vector(1.234, 2.567, 3.891).round) #=> Vector(1, 3, 4)
say(Vector(1.234, 2.567, 3.891).round(2)) #=> Vector(1.23, 2.57, 3.89)
NORMS AND DISTANCES
norm
self.norm
Returns the sum of the squares of every element. Despite the name, this is not the vector's magnitude/length -- for that, see "abs".
say(Vector(3, 4).norm) #=> 25 # 3**2 + 4**2, not the magnitude 5
abs
self.abs
Returns the vector's Euclidean magnitude (length): the square root of "norm".
say(Vector(3, 4).abs) #=> 5
say(Vector(1, 2, 3).abs) # sqrt(14) ≈ 3.742
manhattan_norm
self.manhattan_norm
Returns the Manhattan (L1) norm of the vector: the sum of the absolute values of all elements.
say(Vector(1, -2, 3).manhattan_norm) #=> 6
dist_norm
self.dist_norm(other)
Returns the sum of the squares of the differences between corresponding elements of self and other (the squared Euclidean distance -- no square root is taken). If the vectors have different lengths, only their overlapping prefix (up to the shorter one's length) is used.
say(Vector(1, 2, 3).dist_norm(Vector(4, 5, 6))) #=> 27
dist
self.dist(other)
Returns the Euclidean distance between self and other: the square root of "dist_norm".
say(Vector(1, 2, 3).dist(Vector(4, 5, 6))) # sqrt(27) ≈ 5.196
manhattan_dist
self.manhattan_dist(other)
Returns the Manhattan (L1, taxicab) distance between self and other: the sum of the absolute differences between corresponding elements. If the vectors have different lengths, only their overlapping prefix is used.
say(Vector(1, 2, 3).manhattan_dist(Vector(4, 5, 6))) #=> 9
chebyshev_dist
self.chebyshev_dist(other)
Returns the Chebyshev (L∞, chessboard) distance between self and other: the maximum absolute difference between corresponding elements (0 if the overlapping range is empty). If the vectors have different lengths, only their overlapping prefix is used.
say(Vector(1, 2, 3).chebyshev_dist(Vector(4, 5, 6))) #=> 3
atan2
self.atan2(other)
Returns the angle (in radians, as a plain Number) between self and other -- not an element-wise arctangent. For vectors of exactly 2 elements, this is computed directly from their dot and cross products, giving a signed angle. For any other dimension, it's computed by projecting other onto self's direction and taking the angle between the projection and the leftover perpendicular component -- which is always non-negative (between 0 and π), since it relies on "abs".
say(Vector(1, 0).atan2(Vector(0, 1))) # pi/2 (~1.5708) -- a right angle
CONVERSION
to_array
self.to_array
Converts the vector to a plain Array, preserving all elements in order.
say(Vector(1, 2, 3).to_array) #=> [1, 2, 3]
Aliases: to_a
dump
self.dump
Returns a String representation of the vector, in the format Vector(element, element, ...).
say(Vector(1, 2, 3).dump) #=> "Vector(1, 2, 3)"
Aliases: to_s, to_str
EXAMPLES
Basic vector arithmetic
var v1 = Vector(1, 2, 3)
var v2 = Vector(4, 5, 6)
say(v1 + v2) #=> Vector(5, 7, 9)
say(v1 * 2) #=> Vector(2, 4, 6) # scalar: element-wise
say(v1 * v2) #=> 32 # vector: dot product
Norms and distances
var v1 = Vector(1, 2, 3)
var v2 = Vector(4, 5, 6)
say(v1.norm) #=> 14 # sum of squares
say(v1.abs) # sqrt(14) ≈ 3.742 -- the real magnitude
say(v1.manhattan_norm) #=> 6
say(v1.dist(v2)) # sqrt(27) ≈ 5.196
say(v1.manhattan_dist(v2)) #=> 9
say(v1.chebyshev_dist(v2)) #=> 3
Angle between two vectors
var right = Vector(1, 0)
var up = Vector(0, 1)
var diag = Vector(1, 1)
say(right.atan2(up)) # pi/2 ≈ 1.5708 -- perpendicular
say(right.atan2(diag)) # pi/4 ≈ 0.7854 -- 45 degrees
Component-wise transformations
var v = Vector(1.234, -2.567, 3.891)
say(v.round(2)) #=> Vector(1.23, -2.57, 3.89)
say(v.floor) #=> Vector(1, -3, 3)
say(v.ceil) #=> Vector(2, -2, 4)
say(v.neg) #=> Vector(-1.234, 2.567, -3.891)