Class: Multivariate

Inherits:
Polynomial show all
Defined in:
lib/polynomial/multivariate.rb

Overview

TODO: consider building an writer for @terms which sorts the value

prior to actually setting it.

Constant Summary collapse

Unity =
new([1,0,0])
Zero =
new([0,0,0])

Constants inherited from Polynomial

Polynomial::FromStringDefaults, Polynomial::ToSDefaults

Instance Attribute Summary collapse

Attributes inherited from Polynomial

#coefs

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Polynomial

#%, #+, #-, #-@, #<=>, #derivative, #derivatives, #div, #divmod, #dup, #equal_in_delta, from_string, #integral, #quo, #quomod, #to_f, #to_i, #to_num, #to_s, #unity?, #zero?

Constructor Details

#initialize(*terms) ⇒ Multivariate

Creates a new multivariate polynomial from supplied string representation, basically delegating to Multivariate.from_string.

Examples:

Polynomial::Multivariate[[1,1,0],[2,0,1]].to_s #=> "x + 2*y"

Polynomial::Multivariate['x+y'].to_s #=> "x + y"
Polynomial::Multivariate['xy+y^3', :power_symbol=>'^'].to_s #=> "x*y + y**3"

++



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/polynomial/multivariate.rb', line 25

def initialize(*terms)
  !terms.empty? or raise ArgumentError, 'at least one coefficient should be supplied'
  arg = terms[0]
  case arg
  when Array
    terms.flatten.all? {|a| a.is_a? Numeric } or raise TypeError, 'coefficient-power should be Numeric'
    sz = arg.size
    sz > 2 or raise ArgumentError, 'coefficient-power tuplets should have length > 2'
    terms.all? {|ary| ary.size == sz } or raise ArgumentError, 'inconsistent data'
  when String
    raise NotImplementedError
#      coefs = self.class.coefs_from_string(coefs[0], coefs[1] || {})
  else
    raise TypeError, 'coefficient-power should be Numeric'
  end
  @vars = sz - 1
  @terms = self.class.reduce_terms(terms).sort
end

Instance Attribute Details

#termsObject (readonly)

Returns the value of attribute terms.



9
10
11
# File 'lib/polynomial/multivariate.rb', line 9

def terms
  @terms
end

#varsObject (readonly)

Returns the value of attribute vars.



9
10
11
# File 'lib/polynomial/multivariate.rb', line 9

def vars
  @vars
end

Class Method Details

.reduce_terms(terms) ⇒ Object

Group same powered terms.

The result is always non-empty, the base case being [[0,..,0]]



128
129
130
131
132
133
134
135
136
# File 'lib/polynomial/multivariate.rb', line 128

def self.reduce_terms(terms)
  new_terms = []
  terms.group_by {|a,*powers| powers }.each_pair do |powers, terms|
    new_coef = 0
    terms.each {|coef, *rest| new_coef += coef }
    new_terms << [new_coef, *powers] unless new_coef.zero?
  end
  new_terms.empty? ? [terms[0].map { 0 }] : new_terms
end

Instance Method Details

#*(other) ⇒ Object



62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/polynomial/multivariate.rb', line 62

def *(other)
  @vars == other.vars or raise ArgumentError, "number of variables must be the same for both multiplicands"
  new_terms = []
  @terms.each do |my_term|
    other.terms.each do |other_term|
      new_coef = my_term[0] * other_term[0]
      new_powers = my_term[1..-1].zip(other_term[1..-1]).map {|a,b| a + b }
      new_term = [new_coef] + new_powers
      new_terms << new_term
    end
  end
  self.class.new(*new_terms.sort)
end

#**(n) ⇒ Object

FIXME: implement efficiently, i.e., O(m) where m is the number of terms



77
78
79
80
81
# File 'lib/polynomial/multivariate.rb', line 77

def **(n)
  n >= 0 or raise RangeError, "negative argument"
  n.is_a?(Integer) or raise TypeError, "non-integer argument"
  ([self]*n).inject(Unity) {|s,k| s*k }
end

#==(other) ⇒ Object



83
84
85
86
87
# File 'lib/polynomial/multivariate.rb', line 83

def ==(other)
  self.instance_variables.all? do |var_name|
    self.instance_variable_get(var_name) == other.instance_variable_get(var_name)
  end
end

#coerce(other) ⇒ Object



99
100
101
102
103
104
105
106
107
108
# File 'lib/polynomial/multivariate.rb', line 99

def coerce(other)
  case other
  when Numeric
    [self.class.new([other]), self]
  when Polynomial
    [other, self]
  else
    raise TypeError, "#{other.class} can't be coerced into Polynomial"
  end
end

#degree(index) ⇒ Object



89
90
91
92
93
94
95
96
97
# File 'lib/polynomial/multivariate.rb', line 89

def degree(index)
  if index == 0
    0
  elsif index > 0 && index <= @vars
    @terms.map {|cp| cp[index] }.max
  else
    raise RangeError, 'invalid variable index'
  end
end

#substitute(*xs) ⇒ Object

Evaluates Multivariate polynomial – FIXME: replace current straightforward but slow implementation

by some efficient Horner's rule inspired algorithm.

++



49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/polynomial/multivariate.rb', line 49

def substitute(*xs)
  xs.size == @vars or raise ArgumentError, "wrong number of arguments (#{xs.size} for #{@vars})"
  total = 0
  @terms.each do |coef, *powers|
    result = coef
    for i in 0 ... @vars
      result *= xs[i] ** powers[i]
    end
    total += result
  end
  total
end