symbolic 0.0.5 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +6 -9
- data/Rakefile +1 -1
- data/lib/extensions/kernel.rb +4 -3
- data/lib/symbolic.rb +15 -105
- data/lib/symbolic/core.rb +89 -0
- data/lib/symbolic/expression.rb +3 -3
- data/lib/symbolic/operatable.rb +9 -8
- data/lib/symbolic/optimizations.rb +15 -3
- data/spec/spec_helper.rb +2 -4
- data/spec/symbolic_spec.rb +39 -3
- metadata +3 -2
data/README.rdoc
CHANGED
@@ -1,21 +1,18 @@
|
|
1
1
|
Symbolic math for ruby.
|
2
2
|
|
3
|
-
Installation:
|
3
|
+
Installation:
|
4
|
+
gem install symbolic
|
4
5
|
|
6
|
+
Introduction:
|
5
7
|
Symbolic math can be really helpful if you want to simplify some giant equation or if you don't want to get a performance hit re-evaluating big math expressions every time when few variables change.
|
6
8
|
|
7
9
|
Currently I've implemented only naive optimizations to simplify math expressions, but it's pretty simple to add your own - see Symbolic::Optimizations module for examples.
|
8
10
|
|
9
|
-
|
10
|
-
require 'rubygems'
|
11
|
-
require 'symbolic'
|
12
|
-
|
13
|
-
Symbolic.enabled = true
|
14
|
-
|
11
|
+
Examples:
|
15
12
|
x = var :name => 'x'
|
16
13
|
y = var :name => 'y'
|
17
14
|
|
18
|
-
f = (4 - 2*(-x) + y*(-1.0))*x - 0*y + 0 - 2
|
15
|
+
f = symbolic { (4 - 2*(-x) + y*(-1.0))*x - 0*y + 0 - 2 }
|
19
16
|
puts f # => (4+2*x-y)*x-2
|
20
17
|
|
21
18
|
p f.undefined_variables.map &:name # => ["x", "y"]
|
@@ -25,7 +22,7 @@ Simple example:
|
|
25
22
|
puts f.value # => 6
|
26
23
|
|
27
24
|
|
28
|
-
g = Math.cos(y)
|
25
|
+
g = symbolic { Math.cos(y) }
|
29
26
|
puts g # => cos(y)
|
30
27
|
y.value = 0
|
31
28
|
puts g.value # => 1.0
|
data/Rakefile
CHANGED
@@ -5,7 +5,7 @@ begin
|
|
5
5
|
require 'jeweler'
|
6
6
|
Jeweler::Tasks.new do |gem|
|
7
7
|
gem.name = "symbolic"
|
8
|
-
gem.version = '0.0.
|
8
|
+
gem.version = '0.0.6'
|
9
9
|
gem.summary = 'Symbolic math for ruby'
|
10
10
|
gem.description = %Q{Symbolic math can be really helpful if you want to simplify some giant equation or if you don't want to get a performance hit re-evaluating big math expressions every time when few variables change.}
|
11
11
|
gem.email = "ravwar@gmail.com"
|
data/lib/extensions/kernel.rb
CHANGED
data/lib/symbolic.rb
CHANGED
@@ -1,111 +1,21 @@
|
|
1
|
+
module Symbolic
|
2
|
+
def self.operations
|
3
|
+
{ :* => :multiplication,
|
4
|
+
:+ => :addition,
|
5
|
+
:- => :subtraction,
|
6
|
+
:/ => :division }
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.math_operations
|
10
|
+
[:cos, :sin]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'symbolic/core'
|
1
15
|
require 'symbolic/operatable'
|
2
16
|
require 'symbolic/optimizations'
|
3
17
|
require 'symbolic/variable'
|
4
18
|
require 'symbolic/expression'
|
5
19
|
require 'symbolic/method'
|
6
20
|
require 'symbolic/unary_minus'
|
7
|
-
require 'extensions/kernel'
|
8
|
-
|
9
|
-
module Symbolic
|
10
|
-
@enabled = false
|
11
|
-
class << self
|
12
|
-
def enabled?
|
13
|
-
@enabled
|
14
|
-
end
|
15
|
-
|
16
|
-
def enabled=(enable_flag)
|
17
|
-
if enable_flag && !@enabled
|
18
|
-
enable
|
19
|
-
elsif !enable_flag && @enabled
|
20
|
-
disable
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def aliases
|
25
|
-
{ :* => :non_symbolic_multiplication,
|
26
|
-
:+ => :non_symbolic_addition,
|
27
|
-
:- => :non_symbolic_substraction }
|
28
|
-
end
|
29
|
-
|
30
|
-
def math_operations
|
31
|
-
[:cos, :sin]
|
32
|
-
end
|
33
|
-
|
34
|
-
private
|
35
|
-
|
36
|
-
def numerical_context(&proc)
|
37
|
-
[Fixnum, Bignum, Float].each do |klass|
|
38
|
-
klass.class_eval &proc
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def enable
|
43
|
-
@enabled = true
|
44
|
-
numerical_context do
|
45
|
-
Symbolic.aliases.each do |standard_operation, non_symbolic_operation|
|
46
|
-
alias_method non_symbolic_operation, standard_operation
|
47
|
-
end
|
48
|
-
|
49
|
-
def *(value)
|
50
|
-
if value.is_a?(Operatable)
|
51
|
-
Optimizations.multiply value, self, :reverse
|
52
|
-
else
|
53
|
-
non_symbolic_multiplication(value)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def +(value)
|
58
|
-
if value.is_a? Operatable
|
59
|
-
Optimizations.plus value, self, :reverse
|
60
|
-
else
|
61
|
-
non_symbolic_addition value
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
def -(value)
|
66
|
-
if value.is_a? Operatable
|
67
|
-
Optimizations.minus value, self, :reverse
|
68
|
-
else
|
69
|
-
non_symbolic_substraction value
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end # numerical_context
|
73
|
-
|
74
|
-
math_operations.each do |operation|
|
75
|
-
Math.module_eval <<-CODE
|
76
|
-
class << self
|
77
|
-
alias non_symbolic_#{operation} #{operation}
|
78
|
-
|
79
|
-
def #{operation}(value)
|
80
|
-
if value.is_a? Operatable
|
81
|
-
Symbolic::Method.new value, :#{operation}
|
82
|
-
else
|
83
|
-
non_symbolic_#{operation} value
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
CODE
|
88
|
-
end
|
89
|
-
end # enable
|
90
|
-
|
91
|
-
def disable
|
92
|
-
@enabled = false
|
93
|
-
numerical_context do
|
94
|
-
Symbolic.aliases.each do |standard_operation, non_symbolic_operation|
|
95
|
-
alias_method standard_operation, non_symbolic_operation
|
96
|
-
remove_method non_symbolic_operation
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
Math.module_eval do
|
101
|
-
class << self
|
102
|
-
Symbolic.math_operations.each do |operation|
|
103
|
-
non_symbolic_operation = "non_symbolic_#{operation}"
|
104
|
-
alias_method operation, non_symbolic_operation
|
105
|
-
remove_method non_symbolic_operation
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end # disable
|
110
|
-
end # class << self
|
111
|
-
end
|
21
|
+
require 'extensions/kernel'
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Symbolic
|
2
|
+
module Core
|
3
|
+
@enabled = false
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def enable
|
7
|
+
if !@enabled
|
8
|
+
@enabled = true
|
9
|
+
redefine_numerical_methods
|
10
|
+
redefine_math_methods
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def disable
|
15
|
+
if @enabled
|
16
|
+
@enabled = false
|
17
|
+
restore_numerical_methods
|
18
|
+
restore_math_methods
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def numerical_context(&proc)
|
25
|
+
[Fixnum, Bignum, Float].each do |klass|
|
26
|
+
klass.class_eval &proc
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def redefine_numerical_methods
|
31
|
+
numerical_context do
|
32
|
+
Symbolic.operations.each do |operation_sign, operation_name|
|
33
|
+
alias_method "non_symbolic_#{operation_name}", operation_sign
|
34
|
+
|
35
|
+
method = <<-CODE
|
36
|
+
def #{operation_sign}(value)
|
37
|
+
if value.is_a?(Operatable)
|
38
|
+
Optimizations.#{operation_name} value, self, :reverse
|
39
|
+
else
|
40
|
+
non_symbolic_#{operation_name}(value)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
CODE
|
44
|
+
|
45
|
+
class_eval method, __FILE__, __LINE__
|
46
|
+
end
|
47
|
+
end # numerical_context
|
48
|
+
end # redefine_numerical_methods
|
49
|
+
|
50
|
+
def redefine_math_methods
|
51
|
+
Symbolic.math_operations.each do |operation|
|
52
|
+
code = <<-CODE
|
53
|
+
alias non_symbolic_#{operation} #{operation}
|
54
|
+
|
55
|
+
def #{operation}(value)
|
56
|
+
if value.is_a? Operatable
|
57
|
+
Symbolic::Method.new value, :#{operation}
|
58
|
+
else
|
59
|
+
non_symbolic_#{operation} value
|
60
|
+
end
|
61
|
+
end
|
62
|
+
CODE
|
63
|
+
Math.instance_eval code, __FILE__, __LINE__
|
64
|
+
end
|
65
|
+
end # redefine_math_methods
|
66
|
+
|
67
|
+
def restore_numerical_methods
|
68
|
+
numerical_context do
|
69
|
+
Symbolic.operations.each do |operation_sign, operation_name|
|
70
|
+
alias_method operation_sign, "non_symbolic_#{operation_name}"
|
71
|
+
remove_method "non_symbolic_#{operation_name}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def restore_math_methods
|
77
|
+
Math.module_eval do
|
78
|
+
class << self
|
79
|
+
Symbolic.math_operations.each do |operation|
|
80
|
+
non_symbolic_operation = "non_symbolic_#{operation}"
|
81
|
+
alias_method operation, non_symbolic_operation
|
82
|
+
remove_method non_symbolic_operation
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end # class << self
|
88
|
+
end # Core
|
89
|
+
end # Symbolic
|
data/lib/symbolic/expression.rb
CHANGED
@@ -8,9 +8,9 @@ module Symbolic
|
|
8
8
|
def to_s
|
9
9
|
var1 = "#{@var1}"
|
10
10
|
var2 = "#{@var2}"
|
11
|
-
if
|
12
|
-
var1 = "(#{@var1})" if @var1.is_a?(Expression) && (@var1.plus? || @var1.minus?)
|
13
|
-
var2 = "(#{@var2})" if @var2.is_a?(Expression) && (@var2.plus? || @var2.minus?)
|
11
|
+
if ['*', '/'].include? @operation
|
12
|
+
var1 = "(#{@var1})" if @var1.is_a?(Expression) && (@var1.plus? || @var1.minus?) || @var1.is_a?(UnaryMinus)
|
13
|
+
var2 = "(#{@var2})" if @var2.is_a?(Expression) && (@var2.plus? || @var2.minus?) || @var2.is_a?(UnaryMinus)
|
14
14
|
end
|
15
15
|
"#{var1}#{@operation}#{var2}"
|
16
16
|
end
|
data/lib/symbolic/operatable.rb
CHANGED
@@ -4,16 +4,17 @@ module Symbolic
|
|
4
4
|
UnaryMinus.create self
|
5
5
|
end
|
6
6
|
|
7
|
-
def
|
8
|
-
|
7
|
+
def +@
|
8
|
+
self
|
9
9
|
end
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
11
|
+
Symbolic.operations.each do |operation_sign, operation_name|
|
12
|
+
method = <<-CODE
|
13
|
+
def #{operation_sign}(value)
|
14
|
+
Optimizations.#{operation_name} self, value
|
15
|
+
end
|
16
|
+
CODE
|
17
|
+
class_eval method, __FILE__, __LINE__
|
17
18
|
end
|
18
19
|
end
|
19
20
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Symbolic
|
2
2
|
module Optimizations
|
3
|
-
def self.
|
3
|
+
def self.addition(symbolic_var, var, reverse=false)
|
4
4
|
if var == 0
|
5
5
|
symbolic_var
|
6
6
|
elsif var.is_a? UnaryMinus
|
@@ -16,7 +16,7 @@ module Symbolic
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
def self.
|
19
|
+
def self.subtraction(symbolic_var, var, reverse=false)
|
20
20
|
if var == 0
|
21
21
|
symbolic_var
|
22
22
|
elsif var.is_a? UnaryMinus
|
@@ -32,7 +32,7 @@ module Symbolic
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
-
def self.
|
35
|
+
def self.multiplication(symbolic_var, var, reverse=false)
|
36
36
|
if var == 0
|
37
37
|
var
|
38
38
|
elsif var == 1
|
@@ -53,5 +53,17 @@ module Symbolic
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
end
|
56
|
+
|
57
|
+
def self.division(symbolic_var, var, reverse=false)
|
58
|
+
if var == 1
|
59
|
+
symbolic_var
|
60
|
+
else
|
61
|
+
if reverse
|
62
|
+
Expression.new var, symbolic_var, '/'
|
63
|
+
else
|
64
|
+
Expression.new symbolic_var, var, '/'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
56
68
|
end
|
57
69
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,9 +1,7 @@
|
|
1
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
1
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
2
|
require 'symbolic'
|
3
|
+
require 'rubygems'
|
4
4
|
require 'spec'
|
5
5
|
require 'spec/autorun'
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
end
|
7
|
+
Symbolic::Core.enable
|
data/spec/symbolic_spec.rb
CHANGED
@@ -1,7 +1,43 @@
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) +
|
1
|
+
require File.expand_path(File.dirname(__FILE__) +'/spec_helper')
|
2
2
|
|
3
3
|
describe "Symbolic" do
|
4
|
-
|
5
|
-
|
4
|
+
def self.x
|
5
|
+
var :value => 1, :name => 'x'
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.y
|
9
|
+
var :value => 2, :name => 'y'
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "evaluation (x=1, y=2):" do
|
13
|
+
def self.check_evaluation(conditions)
|
14
|
+
conditions.each do |symbolic_expression, result|
|
15
|
+
it symbolic_expression do
|
16
|
+
symbolic_expression.value.should == result
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
check_evaluation \
|
22
|
+
x => 1,
|
23
|
+
y => 2,
|
24
|
+
+x => 1,
|
25
|
+
-x => -1,
|
26
|
+
x + 4 => 5,
|
27
|
+
3 + x => 4,
|
28
|
+
x + y => 3,
|
29
|
+
x - 1 => 0,
|
30
|
+
1 - x => 0,
|
31
|
+
x - y => -1,
|
32
|
+
-x + 3 => 2,
|
33
|
+
-y - x => -3,
|
34
|
+
x*3 => 3,
|
35
|
+
4*y => 8,
|
36
|
+
(+x)*(-y) => -2,
|
37
|
+
x/2 => 0,
|
38
|
+
y/2 => 1,
|
39
|
+
x/2.0 => 0.5,
|
40
|
+
-2/x => -2,
|
41
|
+
4/(-y) => -2
|
6
42
|
end
|
7
43
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: symbolic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- brainopia
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-11-
|
12
|
+
date: 2009-11-24 00:00:00 +03:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -36,6 +36,7 @@ files:
|
|
36
36
|
- Rakefile
|
37
37
|
- lib/extensions/kernel.rb
|
38
38
|
- lib/symbolic.rb
|
39
|
+
- lib/symbolic/core.rb
|
39
40
|
- lib/symbolic/expression.rb
|
40
41
|
- lib/symbolic/method.rb
|
41
42
|
- lib/symbolic/operatable.rb
|