symbolic 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,21 +1,18 @@
1
1
  Symbolic math for ruby.
2
2
 
3
- Installation: gem install symbolic
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
- Simple example:
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.5'
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"
@@ -4,8 +4,9 @@ module Kernel
4
4
  end
5
5
 
6
6
  def symbolic
7
- Symbolic.enabled = true
8
- yield
9
- Symbolic.enabled = false
7
+ Symbolic::Core.enable
8
+ result = yield
9
+ Symbolic::Core.disable
10
+ return result
10
11
  end
11
12
  end
@@ -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
@@ -8,9 +8,9 @@ module Symbolic
8
8
  def to_s
9
9
  var1 = "#{@var1}"
10
10
  var2 = "#{@var2}"
11
- if @operation == '*'
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
@@ -4,16 +4,17 @@ module Symbolic
4
4
  UnaryMinus.create self
5
5
  end
6
6
 
7
- def *(value)
8
- Optimizations.multiply self, value
7
+ def +@
8
+ self
9
9
  end
10
10
 
11
- def +(value)
12
- Optimizations.plus self, value
13
- end
14
-
15
- def -(value)
16
- Optimizations.minus self, value
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.plus(symbolic_var, var, reverse=false)
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.minus(symbolic_var, var, reverse=false)
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.multiply(symbolic_var, var, reverse=false)
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
@@ -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
- Spec::Runner.configure do |config|
8
-
9
- end
7
+ Symbolic::Core.enable
@@ -1,7 +1,43 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
1
+ require File.expand_path(File.dirname(__FILE__) +'/spec_helper')
2
2
 
3
3
  describe "Symbolic" do
4
- it "fails" do
5
- fail "hey buddy, you should probably rename this file and start specing for real"
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.5
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-20 00:00:00 +03:00
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