Class: VectorNumber
- Inherits:
-
Object
- Object
- VectorNumber
- Includes:
- Comparable, Enumerable
- Defined in:
- lib/vector_number.rb,
lib/vector_number/mathing.rb,
lib/vector_number/version.rb,
lib/vector_number/querying.rb,
lib/vector_number/rounding.rb,
lib/vector_number/comparing.rb,
lib/vector_number/vectoring.rb,
lib/vector_number/converting.rb,
lib/vector_number/similarity.rb,
lib/vector_number/enumerating.rb,
lib/vector_number/special_unit.rb,
lib/vector_number/stringifying.rb
Overview
VectorNumber provides a Numeric-like experience for doing arithmetics on heterogeneous objects, with more advanced operations based on real vector spaces available when needed.
VectorNumber inherits from Object and includes Enumerable and Comparable. It implements mostly the same interface as Numeric classes, but can #coerce any value. Its behavior follows Complex when reasonable.
All instances are frozen after creation.
Working with numeric units
Real and imaginary units are represented by R and I constants, not numbers, as may be expected. They are instances of SpecialUnit class which has useful behavior. However, they are not equal to any object besides themselves, which necessitates always using these constants, not new objects.
User must use these specific constants when
-
initializing vector using a hash,
-
accessing vector by unit.
Customizing #to_s result should also check for these using VectorNumber.numeric_unit?, or, alternatively, for all SpecialUnits with VectorNumber.unit?.
Defined Under Namespace
Classes: SpecialUnit
Constant Summary collapse
- NUMERIC_UNITS =
List of special numeric unit constants.
[SpecialUnit.new("1", "").freeze, SpecialUnit.new("i").freeze].freeze
- R =
Constant for real unit (1).
Its string representation is an empty string.
- I =
Constant for imaginary unit (i).
Its string representation is “i”.
- VERSION =
"0.7.0"- MULT_STRINGS =
Predefined symbols for multiplication to display between unit and coefficient.
{ asterisk: "*", # U+002A cross: "×", # U+00D7 dot: "⋅", # U+22C5 invisible: "", # U+2062, zero-width multiplication operator space: " ", none: "", }.freeze
Miscellaneous methods collapse
-
#size ⇒ Integer
readonly
Number of non-zero dimensions.
Creation collapse
-
.[](*values, **hash_values) ⇒ VectorNumber
Create new VectorNumber from a list of values or a hash.
-
#initialize(values = nil) {|coefficient| ... } ⇒ VectorNumber
constructor
Create new VectorNumber from
values, possibly modifying coefficients with a block.
Miscellaneous methods collapse
-
.numeric_unit?(unit) ⇒ Boolean
Check if an object is a unit representing a numeric dimension (real or imaginary unit).
-
.special_unit?(unit) ⇒ Boolean
(also: unit?)
Check if an object is a SpecialUnit.
-
#+@ ⇒ VectorNumber
(also: #dup)
Return self.
-
#clone(freeze: true) ⇒ VectorNumber
Return self.
-
#inspect ⇒ String
Return string representation of the vector suitable for display.
-
#pretty_print(pp) ⇒ void
Support for PP, usually outputs the same text as #inspect.
-
#to_s(mult: :dot) {|unit, coefficient, index, operator| ... } ⇒ String
Return string representation of the vector suitable for output.
Arithmetic operations collapse
-
#%(other) ⇒ VectorNumber
(also: #modulo)
Return the modulus of dividing self by a real
otheras a vector. -
#*(other) ⇒ VectorNumber
(also: #mult)
Multiply all coefficients by a real
other, returning new vector scaled byother. -
#+(other) ⇒ VectorNumber
(also: #add)
Return new vector as a sum of this and
othervalue. -
#-(other) ⇒ VectorNumber
(also: #sub)
Return new vector as a sum of this and additive inverse of
othervalue. -
#-@ ⇒ VectorNumber
(also: #neg)
Return new vector with negated coefficients (additive inverse).
-
#/(other) ⇒ VectorNumber
(also: #quo)
Divide all coefficients by a real
other, returning new vector scaled by reciprocal ofother. -
#ceildiv(other) ⇒ VectorNumber
Divide all coefficients by a real
other, converting results to integers using#ceil. -
#coerce(other) ⇒ Array(VectorNumber, VectorNumber)
The coerce method provides support for Ruby type coercion.
-
#div(other) ⇒ VectorNumber
Divide all coefficients by a real
other, converting results to integers using#floor. -
#divmod(other) ⇒ Array(VectorNumber, VectorNumber)
Return the quotient and modulus of dividing self by a real
other. -
#fdiv(other) ⇒ VectorNumber
Divide all coefficients by a real
otherusingfdiv, returning new vector with decimal coefficients. -
#remainder(other) ⇒ VectorNumber
Return the remainder of dividing self by a real
otheras a vector.
Querying collapse
-
#finite? ⇒ Boolean
Returns
trueif all coefficients are finite,falseotherwise. -
#infinite? ⇒ 1?
Returns
1if any coefficients are non-finite,nilotherwise. -
#integer? ⇒ false
Always returns
false, as vectors are not Integers. -
#negative? ⇒ Boolean
Returns
trueif number is non-zero and all non-zero coefficients are negative, andfalseotherwise. -
#nonnumeric?(dimensions = 2) ⇒ Boolean
Returns
trueif this VectorNumber contains any non-zero dimensions with non-numeric units, andfalseotherwise. -
#nonzero? ⇒ VectorNumber?
Returns
selfif there are any non-zero coefficients,nilotherwise. -
#numeric?(dimensions = 2) ⇒ Boolean
Returns
trueif all non-zero dimensions in this VectorNumber are numeric (real or complex), andfalseotherwise. -
#positive? ⇒ Boolean
Returns
trueif number is non-zero and all non-zero coefficients are positive, andfalseotherwise. -
#real? ⇒ false
Always returns
false, as vectors are not real numbers. -
#zero? ⇒ Boolean
Returns
trueif there are no non-zero coefficients, andfalseotherwise.
Rounding collapse
-
#ceil(digits = 0) ⇒ VectorNumber
Return a new vector with every coefficient rounded using their
#ceil. -
#floor(digits = 0) ⇒ VectorNumber
Return a new vector with every coefficient rounded using their
#floor. -
#round(digits = 0, half: :up) ⇒ VectorNumber
Return a new vector with every coefficient rounded using their
#round. -
#truncate(digits = 0) ⇒ VectorNumber
Return a new vector with every coefficient truncated using their
#truncate.
Comparing collapse
-
#<=>(other) ⇒ Integer?
Compare to
otherand return -1, 0, or 1 ifselfis less than, equal, or larger thanotheron real number line, ornilif any or both values are non-real. -
#==(other) ⇒ Boolean
Test whether
otherhas the same value with == semantics. -
#eql?(other) ⇒ Boolean
Test whether
otheris VectorNumber and has the same value witheql?semantics. -
#hash ⇒ Integer
Generate an Integer hash value for self.
Vector operations collapse
-
#abs2 ⇒ Numeric
Calculate the square of absolute value.
-
#angle(other) ⇒ Numeric
Calculate the angle between this vector and
othervector in radians. -
#codirectional?(other) ⇒ Boolean
Determine if this vector is codirectional with
othervector. -
#collinear?(other) ⇒ Boolean
Determine if this vector is collinear with
othervector. -
#cross_product(other, basis = %i[x y z])) ⇒ VectorNumber
(also: #vector_product)
Calculate the cross product (vector product) of this vector with
othervector. -
#dot_product(other) ⇒ Numeric
(also: #inner_product, #scalar_product)
Calculate the dot product (inner product/scalar product) of this vector with
othervector. -
#magnitude ⇒ Numeric
(also: #abs)
Calculate the magnitude of the vector (its length/absolute value).
-
#maximum_norm ⇒ Numeric
(also: #infinity_norm)
Calculate the maximum norm (infinity norm) of the vector.
-
#opposite?(other) ⇒ Boolean
Determine if this vector is opposite with
othervector. -
#orthogonal?(other) ⇒ Boolean
Determine if this vector is orthogonal with
othervector. -
#p_norm(p) ⇒ Numeric
Calculate the p-norm of the vector.
-
#parallel?(other) ⇒ Boolean
Determine if this vector is collinear with
othervector and both are non-zero. -
#scalar_projection(other) ⇒ Numeric
Calculate the scalar projection of this vector onto
othervector. -
#scalar_rejection(other) ⇒ Numeric
Calculate the scalar rejection of this vector from
othervector. -
#subspace_basis ⇒ Array<VectorNumber>
Return an array of vectors forming orthonormal basis of linear subspace this vector belongs to.
-
#subspace_projections ⇒ Array<VectorNumber>
Return an array of vectors representing projections onto basis vectors of linear subspace this vector belongs to.
-
#uniform_vector ⇒ VectorNumber
Return a new vector with the same non-zero dimensions, but with coefficients equal to 1.
-
#unit_vector ⇒ VectorNumber
Return a unit vector (vector with magnitude 1) in the direction of this vector.
-
#vector_projection(other) ⇒ VectorNumber
Calculate the vector projection of this vector onto
othervector. -
#vector_rejection(other) ⇒ VectorNumber
Calculate the vector rejection of this vector from
othervector.
Type conversion collapse
-
#imaginary ⇒ Numeric
(also: #imag)
Get imaginary part of the vector.
-
#real ⇒ Numeric
Get real part of the vector.
-
#to_c ⇒ Complex
Convert vector to a Complex if only real and/or imaginary dimensions are non-zero.
-
#to_d(ndigits = nil) ⇒ BigDecimal
Convert vector to a BigDecimal if only real dimension is non-zero.
-
#to_f ⇒ Float
Convert vector to a Float if only real dimension is non-zero.
-
#to_h(&block) ⇒ Hash{Any => Numeric}
Get mutable hash with vector’s data.
-
#to_i ⇒ Integer
(also: #to_int)
Convert vector to an Integer, if only real dimension is non-zero.
-
#to_r ⇒ Rational
Convert vector to a Rational if only real dimension is non-zero.
Similarity measures collapse
-
#cosine(other) ⇒ Numeric
(also: #cosine_similarity)
Calculate cosine between this vector and
other. -
#jaccard_index(other) ⇒ Rational
Calculate Jaccard index of similarity between this vector and
other. -
#jaccard_similarity(other) ⇒ Rational
Calculate weighted Jaccard similarity index between this vector and
other.
Hash-like operations collapse
-
#[](unit) ⇒ Numeric
Get the coefficient for the unit.
-
#assoc(unit) ⇒ Array(Any, Numeric)
Get a 2-element array containing a given unit and associated coefficient.
-
#coefficients ⇒ Array<Numeric>
(also: #values)
Get a list of non-zero coefficients.
-
#coefficients_at(*units) ⇒ Array<Numeric>
(also: #values_at)
Get a list of coefficients corresponding to
units. -
#dig(*identifiers) ⇒ Numeric
Finds and returns the object in nested objects that is specified by
identifiers. -
#each(&block) ⇒ Object
(also: #each_pair)
Iterate through every pair of unit and coefficient.
-
#fetch ⇒ Object
Get the coefficient for the unit, treating 0 coefficients as missing.
-
#fetch_coefficients ⇒ Object
(also: #fetch_values)
Get coefficients for multiple units in the same way as #fetch.
-
#transform_coefficients(mapping = nil, &transform) ⇒ Object
(also: #transform_values)
Return a new VectorNumber with coefficients transformed by the mapping hash and/or a block.
-
#transform_units(mapping = nil, &transform) ⇒ Object
(also: #transform_keys)
Return a new VectorNumber with units transformed by the mapping hash and/or a block.
-
#unit?(unit) ⇒ Boolean
(also: #key?)
Check if a unit has a non-zero coefficient.
-
#units ⇒ Array<Any>
(also: #keys)
Get a list of units with non-zero coefficients.
Constructor Details
#initialize(values = nil) {|coefficient| ... } ⇒ VectorNumber
Create new VectorNumber from values, possibly modifying coefficients with a block.
Using VectorNumber.new directly is more efficient than VectorNumber[...].
values can be:
-
an array of values;
-
a hash of units and coefficients;
-
a VectorNumber to copy, most useful with a block;
-
nilto specify a 0-sized vector (same as an empty array or hash).
180 181 182 183 184 185 186 |
# File 'lib/vector_number.rb', line 180 def initialize(values = nil, **nil, &transform) initialize_from(values) apply_transform(&transform) finalize_contents @data.freeze freeze end |
Instance Attribute Details
#size ⇒ Integer (readonly)
Number of non-zero dimensions.
146 147 148 |
# File 'lib/vector_number.rb', line 146 def size @size end |
Class Method Details
.[](*values) ⇒ VectorNumber .[](**hash_values) ⇒ VectorNumber
Create new VectorNumber from a list of values or a hash.
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/vector_number.rb', line 83 def self.[](*values, **hash_values) raise ArgumentError, "no block accepted" if block_given? if !values.empty? # rubocop:disable Style/NegatedIfElseCondition unless hash_values.empty? raise ArgumentError, "either list of values or hash can be used, not both" end # @type var values : list[unit_type] new(values) else # @type var hash_values : Hash[unit_type, coefficient_type] new(hash_values) end end |
.numeric_unit?(unit) ⇒ Boolean
Check if an object is a unit representing a numeric dimension (real or imaginary unit).
117 118 119 |
# File 'lib/vector_number.rb', line 117 def self.numeric_unit?(unit) NUMERIC_UNITS.include?(unit) # steep:ignore ArgumentTypeMismatch end |
.special_unit?(unit) ⇒ Boolean Also known as: unit?
Check if an object is a SpecialUnit. This includes numeric units.
134 135 136 |
# File 'lib/vector_number.rb', line 134 def self.special_unit?(unit) SpecialUnit === unit end |
Instance Method Details
#%(other) ⇒ VectorNumber Also known as: modulo
Return the modulus of dividing self by a real other as a vector.
This is equal to self - other * (self/other).floor, or, alternatively, self - other * self.div(other).
293 294 295 296 297 298 299 |
# File 'lib/vector_number/mathing.rb', line 293 def %(other) check_divisibility(other) other = other.real # @type var other: Float new { _1 % other } end |
#*(other) ⇒ VectorNumber Also known as: mult
Multiply all coefficients by a real other, returning new vector scaled by other.
This effectively multiplies #magnitude by other.
115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/vector_number/mathing.rb', line 115 def *(other) if real_number?(other) other = other.real # @type var other: Float new { _1 * other } elsif real_number?(self) && VectorNumber === other # @type var other: untyped other * self else raise RangeError, "can't multiply #{self} and #{other}" end end |
#+(other) ⇒ VectorNumber Also known as: add
Return new vector as a sum of this and other value. This is analogous to [].
66 67 68 |
# File 'lib/vector_number/mathing.rb', line 66 def +(other) new([self, other]) end |
#+@ ⇒ VectorNumber Also known as: dup
Return self.
195 196 |
# File 'lib/vector_number.rb', line 195 def +@ = self # @since 0.2.4 |
#-(other) ⇒ VectorNumber Also known as: sub
89 90 91 |
# File 'lib/vector_number/mathing.rb', line 89 def -(other) new([self, new([other], &:-@)]) end |
#-@ ⇒ VectorNumber Also known as: neg
Return new vector with negated coefficients (additive inverse).
44 45 46 |
# File 'lib/vector_number/mathing.rb', line 44 def -@ new(&:-@) end |
#/(other) ⇒ VectorNumber Also known as: quo
This method never does integer division.
Divide all coefficients by a real other, returning new vector scaled by reciprocal of other.
This effectively divides #magnitude by other.
153 154 155 156 157 158 159 160 161 |
# File 'lib/vector_number/mathing.rb', line 153 def /(other) check_divisibility(other) other = other.real # Prevent integer division, but without loss of accuracy. other = Rational(other) if other.integer? # @type var other: Float new { _1 / other } end |
#<=>(other) ⇒ Integer?
Compare to other and return -1, 0, or 1 if self is less than, equal, or larger than other on real number line, or nil if any or both values are non-real.
Most VectorNumbers are non-real and therefore not comparable with this method.
117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/vector_number/comparing.rb', line 117 def <=>(other) return nil unless numeric?(1) case other when VectorNumber other.numeric?(1) ? real <=> other.real : nil when Numeric other.imaginary.zero? ? real <=> other.real : nil else nil end end |
#==(other) ⇒ Boolean
Test whether other has the same value with == semantics.
Values are considered equal if
-
otheris a VectorNumber and it has the same units and equal coefficients, or -
otheris a Numeric equal in value to this (real or complex) number.
32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/vector_number/comparing.rb', line 32 def ==(other) return true if equal?(other) case other when VectorNumber # HACK: Using instance variable access to avoid hash copying overhead. size == other.size && @data == other.instance_variable_get(:@data) when Numeric numeric?(2) && other.real == real && other.imaginary == imaginary else # Can't compare a number-like value to a non-number. false end end |
#[](unit) ⇒ Numeric
Get the coefficient for the unit.
If the unit?(unit) is false, 0 is returned. Note that units for real and imaginary parts are VectorNumber::R and VectorNumber::I respectively.
106 107 108 |
# File 'lib/vector_number/enumerating.rb', line 106 def [](unit) @data[unit] end |
#abs2 ⇒ Numeric
Calculate the square of absolute value.
35 36 37 |
# File 'lib/vector_number/vectoring.rb', line 35 def abs2 @data.values.sum(&:abs2) end |
#angle(other) ⇒ Numeric
Calculate the angle between this vector and other vector in radians.
Result of this method is particularly imprecise due to the nature of the arccosine function. It should not be used if vectors are suspected to be collinear, use #collinear? family of methods instead.
241 242 243 |
# File 'lib/vector_number/vectoring.rb', line 241 def angle(other) Math.acos(cosine(other)) end |
#assoc(unit) ⇒ Array(Any, Numeric)
Get a 2-element array containing a given unit and associated coefficient.
123 124 125 |
# File 'lib/vector_number/enumerating.rb', line 123 def assoc(unit) @data.assoc(unit) || [unit, 0] end |
#ceil(digits = 0) ⇒ VectorNumber
Return a new vector with every coefficient rounded using their #ceil.
36 37 38 |
# File 'lib/vector_number/rounding.rb', line 36 def ceil(digits = 0) new { _1.ceil(digits) } end |
#ceildiv(other) ⇒ VectorNumber
Divide all coefficients by a real other, converting results to integers using #ceil.
This is equal to (self / other).ceil.
254 255 256 257 258 259 260 261 |
# File 'lib/vector_number/mathing.rb', line 254 def ceildiv(other) check_divisibility(other) other = other.real other = Rational(other) if other.integer? # @type var other: Float new { (_1 / other).ceil } end |
#clone(freeze: true) ⇒ VectorNumber
Return self.
205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/vector_number.rb', line 205 def clone(freeze: true) case freeze when true, nil self when false raise ArgumentError, "can't unfreeze VectorNumber" else raise ArgumentError, "unexpected value for freeze: #{Kernel.instance_method(:class).bind_call(freeze)}" end end |
#codirectional?(other) ⇒ Boolean
Determine if this vector is codirectional with other vector.
If either vector is a zero vector, they are not considered codirectional.
340 341 342 |
# File 'lib/vector_number/vectoring.rb', line 340 def codirectional?(other) scale_factor(other)&.positive? || false end |
#coefficients ⇒ Array<Numeric> Also known as: values
Get a list of non-zero coefficients.
63 |
# File 'lib/vector_number/enumerating.rb', line 63 def coefficients = @data.values |
#coefficients_at(*units) ⇒ Array<Numeric> Also known as: values_at
Get a list of coefficients corresponding to units.
81 82 83 |
# File 'lib/vector_number/enumerating.rb', line 81 def coefficients_at(*units) @data.values_at(*units) # : Array[coefficient_type] end |
#coerce(other) ⇒ Array(VectorNumber, VectorNumber)
The coerce method provides support for Ruby type coercion.
Unlike other numeric types, VectorNumber can coerce anything.
25 26 27 28 29 30 31 32 |
# File 'lib/vector_number/mathing.rb', line 25 def coerce(other) case other when VectorNumber [other, self] else [new([other]), self] end end |
#collinear?(other) ⇒ Boolean
Determine if this vector is collinear with other vector.
If either vector is a zero vector, they are considered collinear.
294 295 296 |
# File 'lib/vector_number/vectoring.rb', line 294 def collinear?(other) !!scale_factor(other) end |
#cosine(other) ⇒ Numeric Also known as: cosine_similarity
Calculate cosine between this vector and other.
Cosine can be used as a measure of similarity.
26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/vector_number/similarity.rb', line 26 def cosine(other) has_direction? return 1.0 if equal?(other) other = new([other]) unless VectorNumber === other has_direction?(other) return 0.0 if (product = dot_product(other)).zero? # Due to precision errors, the result might be slightly outside [-1, 1], so we clamp. (product / magnitude / other.magnitude).clamp(-1.0, 1.0) end |
#cross_product(other, basis = %i[x y z])) ⇒ VectorNumber Also known as: vector_product
Calculate the cross product (vector product) of this vector with other vector.
As cross product is normally defined only for regular 3D space, this method has unusual properties:
-
basisargument specifies which triplet of dimensions to work in, -
all other dimensions are discarded.
201 202 203 204 205 206 207 208 209 210 211 212 213 |
# File 'lib/vector_number/vectoring.rb', line 201 def cross_product(other, basis = i[x y z]) # rubocop:disable Metrics/AbcSize raise ArgumentError, "`basis` must contain exactly 3 units" if basis.size != 3 other = new([other]) unless VectorNumber === other return VectorNumber.new if collinear?(other) i, j, k = *basis new({ i => (self[j] * other[k] - self[k] * other[j]), # steep:ignore UnresolvedOverloading j => (self[k] * other[i] - self[i] * other[k]), # steep:ignore UnresolvedOverloading k => (self[i] * other[j] - self[j] * other[i]), # steep:ignore UnresolvedOverloading }) end |
#dig(*identifiers) ⇒ Numeric
Finds and returns the object in nested objects that is specified by identifiers.
As nested objects for a VectorNumber are numeric coefficients, digging deeper will most probably result in an error.
142 143 144 |
# File 'lib/vector_number/enumerating.rb', line 142 def dig(*identifiers) @data.dig(*identifiers) end |
#div(other) ⇒ VectorNumber
Divide all coefficients by a real other, converting results to integers using #floor.
This is equal to (self / other).floor.
223 224 225 226 227 228 229 |
# File 'lib/vector_number/mathing.rb', line 223 def div(other) check_divisibility(other) other = other.real # @type var other: Float new { _1.div(other) } end |
#divmod(other) ⇒ Array(VectorNumber, VectorNumber)
327 328 329 |
# File 'lib/vector_number/mathing.rb', line 327 def divmod(other) [div(other), modulo(other)] end |
#dot_product(other) ⇒ Numeric Also known as: inner_product, scalar_product
Calculate the dot product (inner product/scalar product) of this vector with other vector.
165 166 167 168 169 170 171 172 173 |
# File 'lib/vector_number/vectoring.rb', line 165 def dot_product(other) return 0 if zero? return abs2 if equal?(other) other = new([other]) unless VectorNumber === other return 0 if other.zero? @data.sum { |u, c| c * other[u] } # steep:ignore UnresolvedOverloading end |
#each {|unit, coefficient| ... } ⇒ VectorNumber #each ⇒ Enumerator Also known as: each_pair
Iterate through every pair of unit and coefficient. Returns Enumerator (with set size) if no block is given.
35 36 37 38 39 40 41 |
# File 'lib/vector_number/enumerating.rb', line 35 def each(&block) return to_enum { size } unless block_given? # @type var block: ^([unit_type, coefficient_type]) -> untyped @data.each(&block) self end |
#eql?(other) ⇒ Boolean
Test whether other is VectorNumber and has the same value with eql? semantics.
Values are considered equal only if other is a VectorNumber and it has exactly the same units and coefficients, though possibly in a different order. Additionally, ‘a.eql?(b)` implies `a.hash == b.hash`.
62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/vector_number/comparing.rb', line 62 def eql?(other) return true if equal?(other) return false unless self.class == other.class # @type var other : vector_instance size.eql?(other.size) && @data.eql?(other.instance_variable_get(:@data)) rescue NoMethodError => e # :nocov: raise unless e.receiver.equal?(other) # :nocov: # Should only happen if `other.class` is undefined. false end |
#fdiv(other) ⇒ VectorNumber
Divide all coefficients by a real other using fdiv, returning new vector with decimal coefficients.
There isn’t much benefit to this method, as #/ doesn’t do integer division, but it is provided for consistency.
189 190 191 192 193 194 |
# File 'lib/vector_number/mathing.rb', line 189 def fdiv(other) check_divisibility(other) other = other.real new { _1.fdiv(other) } # steep:ignore BlockBodyTypeMismatch end |
#fetch(unit) ⇒ Numeric #fetch(unit, default_value) ⇒ Any #fetch(unit) {|unit| ... } ⇒ Any
Get the coefficient for the unit, treating 0 coefficients as missing.
If self.unit?(unit) is true, returns the associated coefficient. Otherwise:
-
if neither block, not
default_valueis given, raisesKeyError; -
if block is given, returns the result of the block;
-
if
default_valueis given, returnsdefault_value.
179 180 181 |
# File 'lib/vector_number/enumerating.rb', line 179 def fetch(...) @data.fetch(...) # steep:ignore UnresolvedOverloading end |
#fetch_coefficients(*units) ⇒ Array<Numeric> #fetch_coefficients(*units) {|unit| ... } ⇒ Array<Any> Also known as: fetch_values
Get coefficients for multiple units in the same way as #fetch.
207 208 209 |
# File 'lib/vector_number/enumerating.rb', line 207 def fetch_coefficients(...) @data.fetch_values(...) # steep:ignore UnresolvedOverloading end |
#finite? ⇒ Boolean
Returns true if all coefficients are finite, false otherwise.
62 63 64 |
# File 'lib/vector_number/querying.rb', line 62 def finite? all? { |_u, v| v.finite? } end |
#floor(digits = 0) ⇒ VectorNumber
Return a new vector with every coefficient rounded using their #floor.
53 54 55 56 |
# File 'lib/vector_number/rounding.rb', line 53 def floor(digits = 0) # Why is there a problem here, but not in #truncate and #ceil? I dunno. new { _1.floor(digits) } # steep:ignore BlockBodyTypeMismatch end |
#hash ⇒ Integer
Generate an Integer hash value for self.
87 88 89 |
# File 'lib/vector_number/comparing.rb', line 87 def hash [self.class, @data].hash end |
#imaginary ⇒ Numeric Also known as: imag
Get imaginary part of the vector.
22 |
# File 'lib/vector_number/converting.rb', line 22 def imaginary = @data[I] |
#infinite? ⇒ 1?
Returns 1 if any coefficients are non-finite, nil otherwise.
This behavior is the same as Complex‘s.
76 77 78 |
# File 'lib/vector_number/querying.rb', line 76 def infinite? finite? ? nil : 1 # rubocop:disable Style/ReturnNilInPredicateMethodDefinition end |
#inspect ⇒ String
Return string representation of the vector suitable for display.
This is similar to Complex#inspect — it returns result of #to_s in round brackets.
69 70 71 72 73 |
# File 'lib/vector_number/stringifying.rb', line 69 def inspect return "(0)" if zero? "(#{build_string("⋅")})" end |
#integer? ⇒ false
Always returns false, as vectors are not Integers.
151 |
# File 'lib/vector_number/querying.rb', line 151 def integer? = false |
#jaccard_index(other) ⇒ Rational
Calculate Jaccard index of similarity between this vector and other.
This measure is binary: it considers only sets of non-zero dimensions in vectors, ignoring coefficients.
63 64 65 66 67 |
# File 'lib/vector_number/similarity.rb', line 63 def jaccard_index(other) other = new([other]) unless VectorNumber === other intersection = units.intersection(other.units) Rational(intersection.size, size + other.size - intersection.size) end |
#jaccard_similarity(other) ⇒ Rational
Calculate weighted Jaccard similarity index between this vector and other.
This measure only makes sense for non-negative vectors.
90 91 92 93 94 95 96 |
# File 'lib/vector_number/similarity.rb', line 90 def jaccard_similarity(other) other = new([other]) unless VectorNumber === other Rational( @data.sum { |u, c| [c, other[u]].min }, units.union(other.units).sum { |u| [self[u], other[u]].max } ) end |
#magnitude ⇒ Numeric Also known as: abs
Calculate the magnitude of the vector (its length/absolute value).
This is also known as Euclidean norm or 2-norm.
18 19 20 |
# File 'lib/vector_number/vectoring.rb', line 18 def magnitude abs2**0.5 end |
#maximum_norm ⇒ Numeric Also known as: infinity_norm
Calculate the maximum norm (infinity norm) of the vector.
67 68 69 70 71 |
# File 'lib/vector_number/vectoring.rb', line 67 def maximum_norm return 0 if zero? @data.values.map(&:abs).max end |
#negative? ⇒ Boolean
Returns true if number is non-zero and all non-zero coefficients are negative, and false otherwise.
133 134 135 |
# File 'lib/vector_number/querying.rb', line 133 def negative? !zero? && all? { |_u, c| c.negative? } end |
#nonnumeric?(dimensions = 2) ⇒ Boolean
Returns true if this VectorNumber contains any non-zero dimensions with non-numeric units, and false otherwise.
This is exactly the opposite of #numeric?.
50 51 52 |
# File 'lib/vector_number/querying.rb', line 50 def nonnumeric?(dimensions = 2) !numeric?(dimensions) end |
#nonzero? ⇒ VectorNumber?
Returns self if there are any non-zero coefficients, nil otherwise.
This is synonymous with size not being equal to 0. Behavior of returning self or nil is the same as Numeric‘s.
105 106 107 |
# File 'lib/vector_number/querying.rb', line 105 def nonzero? zero? ? nil : self # rubocop:disable Style/ReturnNilInPredicateMethodDefinition end |
#numeric?(dimensions = 2) ⇒ Boolean
Returns true if all non-zero dimensions in this VectorNumber are numeric (real or complex), and false otherwise.
This is exactly the opposite of #nonnumeric?.
27 28 29 30 31 32 |
# File 'lib/vector_number/querying.rb', line 27 def numeric?(dimensions = 2) raise ArgumentError, "`dimensions` must be non-negative" unless dimensions >= 0 size <= dimensions && (0...dimensions).count { (unit = NUMERIC_UNITS[_1]) && @data[unit].nonzero? } == size end |
#opposite?(other) ⇒ Boolean
Determine if this vector is opposite with other vector.
If either vector is a zero vector, they are not considered opposite.
364 365 366 |
# File 'lib/vector_number/vectoring.rb', line 364 def opposite?(other) scale_factor(other)&.negative? || false end |
#orthogonal?(other) ⇒ Boolean
Determine if this vector is orthogonal with other vector.
If either vector is a zero vector, they are not considered orthogonal. If vectors have no non-zero dimensions in common, they are orthogonal.
264 265 266 267 268 269 270 271 |
# File 'lib/vector_number/vectoring.rb', line 264 def orthogonal?(other) return false if zero? other = new([other]) unless VectorNumber === other return false if other.zero? dot_product(other).zero? end |
#p_norm(p) ⇒ Numeric
Calculate the p-norm of the vector.
52 53 54 |
# File 'lib/vector_number/vectoring.rb', line 52 def p_norm(p) # rubocop:disable Naming/MethodParameterName @data.values.sum { _1.abs**p }**(1.0 / p) # steep:ignore UnresolvedOverloading end |
#parallel?(other) ⇒ Boolean
Determine if this vector is collinear with other vector and both are non-zero.
316 317 318 |
# File 'lib/vector_number/vectoring.rb', line 316 def parallel?(other) !!scale_factor(other)&.nonzero? end |
#positive? ⇒ Boolean
Returns true if number is non-zero and all non-zero coefficients are positive, and false otherwise.
119 120 121 |
# File 'lib/vector_number/querying.rb', line 119 def positive? !zero? && all? { |_u, c| c.positive? } end |
#pretty_print(pp) ⇒ void
This method returns an undefined value.
Support for PP, usually outputs the same text as #inspect.
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/vector_number/stringifying.rb', line 81 def pretty_print(pp) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength pp.text("(0)") and return if zero? pp.group(1, "(", ")") do # This should use `pp.fill_breakable`, but PrettyPrint::SingleLine haven't had it. pp.seplist(@data, -> { fill_breakable(pp) }, :each_with_index) do |(unit, coefficient), i| pp.text("-") if coefficient.negative? unless i.zero? pp.text("+") if coefficient.positive? fill_breakable(pp) end pp.pp(coefficient.abs) if SpecialUnit === unit pp.text(unit.to_s) else pp.text("⋅") pp.pp(unit) end end end end |
#real ⇒ Numeric
Get real part of the vector.
13 |
# File 'lib/vector_number/converting.rb', line 13 def real = @data[R] |
#real? ⇒ false
Always returns false, as vectors are not real numbers.
This behavior is the same as Complex‘s.
144 |
# File 'lib/vector_number/querying.rb', line 144 def real? = false |
#remainder(other) ⇒ VectorNumber
Return the remainder of dividing self by a real other as a vector.
This is equal to self - other * (self/other).truncate.
358 359 360 361 362 363 364 |
# File 'lib/vector_number/mathing.rb', line 358 def remainder(other) check_divisibility(other) other = other.real # @type var other: Float new { _1.remainder(other) } end |
#round(digits = 0, half: :up) ⇒ VectorNumber
Return a new vector with every coefficient rounded using their #round.
In the case of BigDecimal coefficients, the half parameter is converted to the corresponding BigDecimal rounding mode (one of :half_up, :half_down, or :half_even). Other modes can not be specified.
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/vector_number/rounding.rb', line 81 def round(digits = 0, half: :up) # rubocop:disable Metrics/MethodLength if defined?(BigDecimal) bd_mode = case half when :down then :half_down when :even then :half_even else :half_up end new do |c| if BigDecimal === c c.round(digits, bd_mode) else c.round(digits, half: half) # steep:ignore UnresolvedOverloading end end # :nocov: else new { _1.round(digits, half: half) } # steep:ignore UnresolvedOverloading end # :nocov: end |
#scalar_projection(other) ⇒ Numeric
Calculate the scalar projection of this vector onto other vector.
Absolute value of scalar projection is equal to #magnitude of #vector_projection. Sign of scalar projection depends on the #angle between vectors.
414 415 416 417 418 419 420 421 |
# File 'lib/vector_number/vectoring.rb', line 414 def scalar_projection(other) return magnitude if equal?(other) && has_direction? other = new([other]) unless VectorNumber === other has_direction?(other) dot_product(other) / other.magnitude end |
#scalar_rejection(other) ⇒ Numeric
Calculate the scalar rejection of this vector from other vector.
Scalar rejection is equal to #magnitude of #vector_rejection, and is always non-negative.
471 472 473 474 475 476 477 478 |
# File 'lib/vector_number/vectoring.rb', line 471 def scalar_rejection(other) return 0.0 if equal?(other) && has_direction? other = new([other]) unless VectorNumber === other has_direction?(other) (abs2 - dot_product(other)**2 / other.abs2)**0.5 # steep:ignore NoMethod end |
#subspace_basis ⇒ Array<VectorNumber>
Return an array of vectors forming orthonormal basis of linear subspace this vector belongs to.
89 90 91 |
# File 'lib/vector_number/vectoring.rb', line 89 def subspace_basis units.map { new([_1]) } end |
#subspace_projections ⇒ Array<VectorNumber>
Return an array of vectors representing projections onto basis vectors of linear subspace this vector belongs to.
105 106 107 |
# File 'lib/vector_number/vectoring.rb', line 105 def subspace_projections map { |u, c| new({ u => c }) } end |
#to_c ⇒ Complex
Convert vector to a Complex if only real and/or imaginary dimensions are non-zero.
130 131 132 133 134 |
# File 'lib/vector_number/converting.rb', line 130 def to_c raise_convert_error(Complex) unless numeric?(2) Complex(real, imaginary) end |
#to_d(ndigits = nil) ⇒ BigDecimal
Convert vector to a BigDecimal if only real dimension is non-zero.
108 109 110 111 112 113 114 115 |
# File 'lib/vector_number/converting.rb', line 108 def to_d(ndigits = nil) raise_convert_error(BigDecimal) unless numeric?(1) return BigDecimal(real, ndigits) if ndigits return BigDecimal(real, Float::DIG) if Float === real BigDecimal(real) end |
#to_f ⇒ Float
Convert vector to a Float if only real dimension is non-zero.
62 63 64 65 66 |
# File 'lib/vector_number/converting.rb', line 62 def to_f raise_convert_error(Float) unless numeric?(1) real.to_f end |
#to_h(&block) ⇒ Hash{Any => Numeric}
Get mutable hash with vector’s data.
Returned hash has a default value of 0.
145 146 147 148 149 150 151 152 153 |
# File 'lib/vector_number/converting.rb', line 145 def to_h(&block) # TODO: Remove block argument. if block_given? # @type var block: ^(unit_type, coefficient_type) -> each_value_type @data.to_h(&block) else @data.dup end end |
#to_i ⇒ Integer Also known as: to_int
Convert vector to an Integer, if only real dimension is non-zero.
41 42 43 44 45 |
# File 'lib/vector_number/converting.rb', line 41 def to_i raise_convert_error(Integer) unless numeric?(1) real.to_i end |
#to_r ⇒ Rational
Convert vector to a Rational if only real dimension is non-zero.
81 82 83 84 85 |
# File 'lib/vector_number/converting.rb', line 81 def to_r raise_convert_error(Rational) unless numeric?(1) real.to_r end |
#to_s(mult: :dot) {|unit, coefficient, index, operator| ... } ⇒ String
Return string representation of the vector suitable for output.
An optional block can be supplied to provide customized substrings for each unit and coefficient pair. Care needs to be taken in handling VectorNumber::R and VectorNumber::I units. numeric_unit?/special_unit? can be used to check if a particular unit requires different logic.
49 50 51 52 53 54 55 56 57 |
# File 'lib/vector_number/stringifying.rb', line 49 def to_s(mult: :dot, &block) if !(String === mult) && !MULT_STRINGS.key?(mult) raise ArgumentError, "unknown key #{mult.inspect}", caller end return "0" if zero? operator = (String === mult) ? mult : MULT_STRINGS[mult] build_string(operator, &block) end |
#transform_coefficients(mapping) ⇒ VectorNumber #transform_coefficients {|coefficient| ... } ⇒ VectorNumber #transform_coefficients(mapping) {|coefficient| ... } ⇒ VectorNumber #transform_coefficients ⇒ Enumerator Also known as: transform_values
Return a new VectorNumber with coefficients transformed by the mapping hash and/or a block.
An optional mapping argument can be provided to map coefficients to new coefficients. Any coefficient not given in mapping will be mapped using the provided block, or remain the same if no block is given. If neither mapping nor block is given, an enumerator is returned.
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 |
# File 'lib/vector_number/enumerating.rb', line 261 def transform_coefficients(mapping = nil, &transform) if mapping if block_given? # @type var transform: ^(coefficient_type) -> coefficient_type new { |c| mapping.fetch(c) { yield(c) } } else new { |c| mapping.fetch(c, c) } end elsif block_given? # @type var transform: ^(coefficient_type) -> coefficient_type new(&transform) else to_enum(:transform_coefficients) { size } # rubocop:disable Lint/ToEnumArguments end end |
#transform_units(mapping) ⇒ VectorNumber #transform_units {|unit| ... } ⇒ VectorNumber #transform_units(mapping) {|unit| ... } ⇒ VectorNumber #transform_units ⇒ Enumerator Also known as: transform_keys
Return a new VectorNumber with units transformed by the mapping hash and/or a block.
An optional mapping argument can be provided to map units to new units. Any unit not given in mapping will be mapped using the provided block, or remain the same if no block is given. If neither mapping nor block is given, an enumerator is returned.
312 313 314 315 316 317 318 319 320 321 322 323 324 325 |
# File 'lib/vector_number/enumerating.rb', line 312 def transform_units(mapping = nil, &transform) if block_given? # @type var transform: ^(unit_type unit) -> unit_type if mapping new(@data.transform_keys(mapping, &transform)) else new(@data.transform_keys(&transform)) end elsif mapping new(@data.transform_keys(mapping)) else to_enum(:transform_units) { size } # rubocop:disable Lint/ToEnumArguments end end |
#truncate(digits = 0) ⇒ VectorNumber
Return a new vector with every coefficient truncated using their #truncate.
19 20 21 |
# File 'lib/vector_number/rounding.rb', line 19 def truncate(digits = 0) new { _1.truncate(digits) } end |
#uniform_vector ⇒ VectorNumber
Return a new vector with the same non-zero dimensions, but with coefficients equal to 1.
Resulting vector is equal to self.subspace_basis.sum.
124 125 126 |
# File 'lib/vector_number/vectoring.rb', line 124 def uniform_vector new { 1 } end |
#unit?(unit) ⇒ Boolean Also known as: key?
Check if a unit has a non-zero coefficient.
224 225 226 |
# File 'lib/vector_number/enumerating.rb', line 224 def unit?(unit) @data.key?(unit) end |
#unit_vector ⇒ VectorNumber
Return a unit vector (vector with magnitude 1) in the direction of this vector.
Magnitude of resulting vector may not be exactly 1 due to rounding errors.
142 143 144 145 146 |
# File 'lib/vector_number/vectoring.rb', line 142 def unit_vector has_direction? self / magnitude end |
#units ⇒ Array<Any> Also known as: keys
Get a list of units with non-zero coefficients.
52 |
# File 'lib/vector_number/enumerating.rb', line 52 def units = @data.keys |
#vector_projection(other) ⇒ VectorNumber
Calculate the vector projection of this vector onto other vector.
385 386 387 388 389 390 391 392 |
# File 'lib/vector_number/vectoring.rb', line 385 def vector_projection(other) return self if equal?(other) && has_direction? other = new([other]) unless VectorNumber === other has_direction?(other) other * dot_product(other) / other.abs2 end |
#vector_rejection(other) ⇒ VectorNumber
Calculate the vector rejection of this vector from other vector.
Vector rejection is equal to self - self.vector_projection(other).
442 443 444 445 446 447 448 449 |
# File 'lib/vector_number/vectoring.rb', line 442 def vector_rejection(other) return VectorNumber.new if equal?(other) && has_direction? other = new([other]) unless VectorNumber === other has_direction?(other) self - vector_projection(other) end |
#zero? ⇒ Boolean
Returns true if there are no non-zero coefficients, and false otherwise.
This is synonymous with size being 0.
91 |
# File 'lib/vector_number/querying.rb', line 91 def zero? = size.zero? |