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.
@@ -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