rucas 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,99 @@
1
+ require 'rucas/symbolic'
2
+
3
+ module Rucas
4
+ module Extensions
5
+ #
6
+ # Avoid collisions with other patches; this maps operators (+,-, etc.) to
7
+ # normal method names, so they can be called without the additional overhead
8
+ # of Kernel#send.
9
+ #
10
+ PATCH_HASH = Hash.new {|h,k| k} # return key by default
11
+ PATCH_HASH[:+] = :add
12
+ PATCH_HASH[:-] = :sub
13
+ PATCH_HASH[:*] = :mul
14
+ PATCH_HASH[:/] = :div
15
+ PATCH_HASH[:**] = :pow
16
+ PATCH_HASH[:=~] = :eqs
17
+ PATCH_HASH[:<] = :lt
18
+ PATCH_HASH[:<=] = :lte
19
+ PATCH_HASH[:>] = :gt
20
+ PATCH_HASH[:>=] = :gte
21
+ PATCH_HASH[:&] = :and
22
+ PATCH_HASH[:|] = :or
23
+ raise "PATCH_HASH does not match BINARY_OPS" unless
24
+ Symbolic::BINARY_OPS.keys.all?{|k| PATCH_HASH.member?(k)}
25
+
26
+ # See PATCH_HASH.
27
+ def self.without_rucas_name original
28
+ "#{PATCH_HASH[original]}__without_rucas"
29
+ end
30
+
31
+ # See PATCH_HASH.
32
+ def self.with_rucas_name original
33
+ "#{PATCH_HASH[original]}__with_rucas"
34
+ end
35
+
36
+ # Add rucas methods constant classes, but don't alias_method them yet; that
37
+ # is handled in the apply and unapply methods.
38
+ for c in Symbolic::CONST_CLASSES
39
+ for op in Symbolic::BINARY_OPS.keys
40
+ c.class_eval %Q{
41
+ def #{with_rucas_name(op)} rhs
42
+ if rhs.is_a?(Rucas::Symbolic::Expr)
43
+ Rucas::Symbolic::ConstExpr.new(self) #{op} rhs
44
+ else
45
+ self.#{without_rucas_name(op)}(rhs)
46
+ end
47
+ end
48
+ }
49
+ end
50
+
51
+ # Try to convert to ConstExpr if can't find method (e.g. +simplify+).
52
+ c.module_eval do
53
+ def method_missing_with_rucas method, *args, &block
54
+ method_missing_without_rucas(method *args, &block)
55
+ rescue NoMethodError
56
+ error = $!
57
+ begin
58
+ return Symbolic::Expr.make(self).send(method, *args, &block)
59
+ rescue NoMethodError
60
+ nil
61
+ end
62
+ raise error
63
+ end
64
+ end
65
+ end
66
+
67
+ # Patch system classes to make constants work with vars (e.g. 0 + x).
68
+ def self.apply
69
+ for c in Symbolic::CONST_CLASSES
70
+ for op in Symbolic::BINARY_OPS.keys
71
+ c.class_eval %Q{
72
+ alias_method :#{without_rucas_name(op)}, :#{op}
73
+ alias_method :#{op}, :#{with_rucas_name(op)}
74
+ }
75
+ end
76
+ c.module_eval do
77
+ alias_method :method_missing_without_rucas, :method_missing
78
+ alias_method :method_missing, :method_missing_with_rucas
79
+ end
80
+ end
81
+ end
82
+
83
+ # Get rid of patches.
84
+ def self.unapply
85
+ for c in Symbolic::CONST_CLASSES
86
+ for op in Symbolic::BINARY_OPS.keys
87
+ c.class_eval %Q{
88
+ alias_method :#{with_rucas_name(op)}, :#{op}
89
+ alias_method :#{op}, :#{without_rucas_name(op)}
90
+ }
91
+ end
92
+ c.module_eval do
93
+ alias_method :method_missing_with_rucas, :method_missing
94
+ alias_method :method_missing, :method_missing_without_rucas
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,181 @@
1
+ require 'rucas/symbolic'
2
+ require 'facets/dictionary'
3
+
4
+ module Rucas
5
+ #
6
+ # Tools for recursively rewriting expressions based on rules.
7
+ #
8
+ module Rewrite
9
+ #
10
+ # Construct an ordered hash of rewrite rules.
11
+ #
12
+ # Example:
13
+ # my_rules = make_rules {
14
+ # var :x
15
+ # rule x + 0 => x
16
+ # rule 0 + x => x
17
+ # }
18
+ #
19
+ def self.make_rules &block
20
+ s = Scope.new
21
+ class <<s
22
+ def dict
23
+ @dict ||= Dictionary[]
24
+ end
25
+ def rule rule
26
+ raise unless rule.size == 1
27
+ dict[rule.keys.first] = Symbolic::Expr.make(rule.values.first)
28
+ end
29
+ end
30
+ s.rucas(&block)
31
+ s.dict
32
+ end
33
+ end
34
+
35
+ module Symbolic
36
+ module Expr
37
+ #
38
+ # If this expression matches +pattern+, return +output+ (with appropriate
39
+ # bindings of variables in pattern applied to output); otherwise, return
40
+ # self (or an object == to self).
41
+ #
42
+ def rewrite pattern, output
43
+ bindings = pattern.match(self)
44
+ return output.with(bindings) if bindings
45
+ self
46
+ end
47
+
48
+ #
49
+ # Non-recursive matching. Here, self is a pattern and +expr+ is the input
50
+ # to be matched against this pattern. Returns bindings for the free
51
+ # variables in self that make the match succeed, or nil if there is no
52
+ # such match.
53
+ #
54
+ def match expr, bindings = {}
55
+ nil # see subclasses
56
+ end
57
+
58
+ #
59
+ # If this expression involves only constants, evaluate it and return the
60
+ # result; if it is not, return nil.
61
+ #
62
+ def value
63
+ nil # see subclasses
64
+ end
65
+ end
66
+
67
+ module OpExpr
68
+ def rewrite pattern, output
69
+ # Rewrite children first.
70
+ new_children = self.children.map{|c| c.rewrite(pattern, output)}
71
+ new_self = self.class.new(*new_children)
72
+ # See if it's a constant.
73
+ v = new_self.value
74
+ if v
75
+ Symbolic::Expr.make(v)
76
+ else
77
+ # It's not a constant; try to rewrite using the rule.
78
+ bindings = pattern.match(new_self)
79
+ return output.with(bindings) if bindings
80
+ new_self
81
+ end
82
+ end
83
+
84
+ def with bindings
85
+ self.class.new(*self.children.map{|c| c.with(bindings)})
86
+ end
87
+ end
88
+
89
+ class ConstExpr
90
+ def match expr, bindings = {}
91
+ return bindings if self == expr
92
+ return nil
93
+ end
94
+
95
+ def with bindings
96
+ self
97
+ end
98
+ end
99
+
100
+ class VarExpr
101
+ def match expr, bindings = {}
102
+ binding = bindings[self]
103
+ return bindings.merge(self => expr) if !binding
104
+ return bindings if binding == expr
105
+ return nil
106
+ end
107
+
108
+ def with bindings
109
+ bindings[self] || self
110
+ end
111
+ end
112
+
113
+ class UnaryOpExpr
114
+ def match expr, bindings = {}
115
+ return nil unless expr.is_a?(UnaryOpExpr)
116
+ return nil unless self.op == expr.op
117
+ rhs.match(expr.rhs, bindings)
118
+ end
119
+
120
+ def value
121
+ v = rhs.value
122
+ eval "(#{v}).#{self.op}" if v
123
+ end
124
+ end
125
+
126
+ class BinaryOpExpr
127
+ def match expr, bindings = {}
128
+ if expr.is_a?(BinaryOpExpr) && self.op == expr.op
129
+ lb = self.lhs.match(expr.lhs, bindings)
130
+ rb = self.rhs.match(expr.rhs, lb) if lb
131
+ return rb if rb
132
+ end
133
+ nil
134
+ end
135
+
136
+ def value
137
+ lv = lhs.value
138
+ rv = rhs.value
139
+ eval "(#{lv})#{self.op}(#{rv})" if lv && rv
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+ # module Expr
146
+ # def simplify bindings={}, rules=DEFAULT_SIMPLIFY_RULES
147
+ # for rule in rules
148
+ # new_bindings = self.match(rule.lhs, bindings)
149
+ # return rule.rhs.with(new_bindings).simplify(bindings,rules) if
150
+ # new_bindings
151
+ # end
152
+ # end
153
+
154
+ # rule: pair of expressions, which may refer to matchers
155
+ # matcher: name, predicate block
156
+ # bindings: map from matcher to expression
157
+ # the result of matching an expression with another expression should be
158
+ # false if the match fails, or it should return bindings for the matchers
159
+ # that make the match succeed.
160
+ # note: match is not commutative; it's probably easiest if we return
161
+ # bindings for the lhs (self), since most of the tricky stuff then goes
162
+ # into VarExpr. This isn't maximally rubyish: "foo" =~ /foo/ -- the
163
+ # pattern comes last -- but /foo/.match("foo") is also valid.
164
+ # note: a convention is also required for handling things like
165
+ # (x + y).match(a - b) -- this could succeed with x => a, y => -b, but it
166
+ # requires more checks -- can't just check op of root.
167
+ # def match expr
168
+ #
169
+ # end
170
+ # end
171
+ #
172
+ # Simplification isn't really recursive (in the tree structure) -- we have
173
+ # to apply a list of rules, and we restart when the current rule matches
174
+ # any part of the expression tree.
175
+ # The simplification process for one rewrite rule could be recursive: do a
176
+ # post-order traversal, rewriting children; at the top level, we just
177
+ # compare the whole resulting expression with the original to see if
178
+ # anything's changed.
179
+ # The match is then non-recursive.
180
+ #
181
+
@@ -0,0 +1,102 @@
1
+ require 'rucas/rewrite'
2
+
3
+ module Rucas
4
+ #
5
+ # Simplification; this is still fairly primitive.
6
+ #
7
+ # things that don't simplify:
8
+ # Examples from PAIP 8.3: commutativity and associativity
9
+ # {3*(2*x)}
10
+ # {2 * x * x * 3}
11
+ # {2 * x * 3 * y * 4 * z * 5 * 6}
12
+ # {3 + x + 4 + x}
13
+ # {2 * x * 3 * x * 4 * (1 / x) * 5 * 6}
14
+ #
15
+ module Simplify
16
+ #
17
+ # Ordered hash of simplification rules.
18
+ #
19
+ # The order of the rules is very important for avoiding unsoundness due to
20
+ # division by zero, for example.
21
+ #
22
+ # Note that division by zero always results in a NaN. If it were possible to
23
+ # prove that x was strictly positive (negative), x / 0 should arguably
24
+ # evaluate to +Inf (-Inf), but this is substantially beyond what we can do
25
+ # at this point.
26
+ #
27
+ RULES = Rewrite.make_rules {
28
+ var :w
29
+ var :x
30
+ var :y
31
+ z = Symbolic::Expr.make(0)
32
+ nan = Symbolic::Expr.make(0.0/0.0)
33
+
34
+ # These rules are taken from Norvig's Paradigms of AI Programming
35
+ # (chapter 8). The source for that book is freely available.
36
+ rule( x + 0 => x )
37
+ rule( 0 + x => x )
38
+ rule( x + x => 2*x )
39
+ rule( x - 0 => x )
40
+ rule( 0 - x => -x )
41
+ rule( x - x => 0 )
42
+ rule( +x => x )
43
+ rule( --x => x )
44
+ rule( x * 1 => x )
45
+ rule( 1 * x => x )
46
+ rule( x * 0 => 0 )
47
+ rule( 0 * x => 0 )
48
+ rule( x * x => x**2)
49
+ rule( x / 0 => nan )
50
+ rule( 0 / x => 0 )
51
+ rule( x / 1 => x )
52
+ rule( x / x => 1 )
53
+ rule( z ** 0 => 1 ) # note: Ruby says 0 ** 0 is 1; be consistent
54
+ rule( x ** 0 => 1 )
55
+ rule( 0 ** x => 0 )
56
+ rule( 1 ** x => 1 )
57
+ rule( x ** 1 => x )
58
+ rule( x ** -1 => 1 / x)
59
+ rule( x * (y / x) => y )
60
+ rule( (y / x) * x => y )
61
+ rule( (y * x) / x => y )
62
+ rule( (x * y) / x => y )
63
+ rule( x + -x => 0 )
64
+ rule( -x + x => 0 )
65
+ rule( x + y - x => y )
66
+ rule( x - y - x => -y )
67
+
68
+ # These rules are helpful for "rebalancing" long trees, which can make
69
+ # some of the rules above effective. This is because we don't actually
70
+ # handle associativity properly. These rules do slow things down a bit,
71
+ # but hopefully it doesn't break anything too badly.
72
+ #
73
+ # Interestingly, Norvig's "infix->prefix" functions used right-
74
+ # associative grouping, which made things go more smoothly. For example,
75
+ # x^2 + x + x + 1 simplifies to x^2 + 2*x + 1 without the rebalancing
76
+ # rule, if you group from the right (but the reverse doesn't simplify).
77
+ rule( (w + x) + y => w + (x + y) )
78
+ rule( (w * x) * y => w * (x * y) )
79
+ }
80
+ end
81
+
82
+ module Symbolic
83
+ module Expr
84
+ #
85
+ # Return expression after algebraic simplification. Note that this isn't a
86
+ # very smart simplifier.
87
+ #
88
+ def simplify
89
+ new_self = self
90
+ changed = false
91
+ for pattern, output in Simplify::RULES
92
+ new_self = new_self.rewrite(pattern, output)
93
+ #puts "#{pattern}\t#{new_self.to_s_paren}"
94
+ changed = (new_self != self)
95
+ break if changed
96
+ end
97
+ if changed then new_self.simplify else self end
98
+ end
99
+ end
100
+ end
101
+ end
102
+
@@ -0,0 +1,238 @@
1
+ module Rucas
2
+ #
3
+ # Translate Ruby expressions into expression trees for symbolic manipulation.
4
+ #
5
+ module Symbolic
6
+ include Utility
7
+
8
+ #
9
+ # Define variable in this scope and return it.
10
+ #
11
+ def var name
12
+ var = VarExpr.new(name)
13
+ meta_def var.name do
14
+ var
15
+ end
16
+ var
17
+ end
18
+
19
+ CONST_CLASSES = [Fixnum, Float, Bignum]
20
+
21
+ #
22
+ # Symbolic expression; subclasses represent various kinds of expressions.
23
+ #
24
+ module Expr
25
+ # Children in the expression tree, if any. For example, an AddExpr returns
26
+ # its left and right operands (x and y in x + y).
27
+ def children; [] end
28
+
29
+ # Operator precedence; this reflects Ruby's built-in order-of-operations
30
+ # rules. You should not rely on the particular values; only the ordering
31
+ # they define is guaranteed.
32
+ def precedence; 0 end
33
+
34
+ # String representation including all parentheses; by default, +to_s+
35
+ # omits parentheses when operator precedence rules allow.
36
+ def to_s_paren; to_s end
37
+
38
+ def self.make e
39
+ return e if e.is_a?(Expr)
40
+ return ConstExpr.new(e) if CONST_CLASSES.any? {|t| e.is_a?(t)}
41
+ raise "#{e} is not a symbolic expression"
42
+ end
43
+ end
44
+
45
+ # Numeric constant.
46
+ ConstExpr = Struct.new(:value)
47
+ class ConstExpr
48
+ include Expr
49
+
50
+ def to_s; value.to_s end
51
+ def constant?; true end
52
+ end
53
+
54
+ # Variable (literal).
55
+ VarExpr = Struct.new(:name)
56
+ class VarExpr
57
+ include Expr
58
+
59
+ def to_s; name.to_s end
60
+ def constant?; false end
61
+ end
62
+
63
+ #
64
+ # Expression with an operator and operand(s).
65
+ #
66
+ module OpExpr
67
+ include Expr
68
+
69
+ #
70
+ # The operators on the same line have the same precedence; the list is
71
+ # from highest precedence to lowest precedence. This list is from the
72
+ # Pickaxe book ("The Ruby Language").
73
+ #
74
+ op_precedence_table =
75
+ [%w(**),
76
+ %w(~@ +@ -@),
77
+ %w(* / %),
78
+ %w(+ -)]
79
+
80
+ #
81
+ # Operator precedence; you shouldn't rely on the numbers, but (barring
82
+ # changes to Ruby), the order shouldn't change.
83
+ #
84
+ OP_PRECEDENCE = Hash[*op_precedence_table.
85
+ zip((1..op_precedence_table.size).to_a).
86
+ map{|ops,prec| ops.map{|op| [op.to_sym, -prec]}}.flatten]
87
+
88
+ #
89
+ # Precedence (in Ruby) of this operator; this affects how expressions are
90
+ # parenthesized.
91
+ #
92
+ def precedence
93
+ OP_PRECEDENCE[self.op]
94
+ end
95
+
96
+ def constant?; children.all {|c| c.constant?} end
97
+ end
98
+
99
+ #
100
+ # Unary operations.
101
+ #
102
+ UnaryOpExpr = Struct.new(:op, :rhs)
103
+ class UnaryOpExpr
104
+ include OpExpr
105
+
106
+ def children; [rhs] end
107
+
108
+ def to_s_paren
109
+ "#{self.op}(#{self.rhs.to_s_paren})"
110
+ end
111
+ end
112
+
113
+ class PosExpr < UnaryOpExpr
114
+ def to_s; rhs.to_s end
115
+ end
116
+
117
+ class NegExpr < UnaryOpExpr
118
+ def to_s; rhs.precedence < self.precedence ? "-(#{rhs})" : "-#{rhs}" end
119
+ end
120
+
121
+ UNARY_OPS = {
122
+ :+@ => :PosExpr,
123
+ :-@ => :NegExpr,
124
+ :~@ => :NotExpr,
125
+ }
126
+
127
+ for op, op_class in UNARY_OPS
128
+ class_eval %Q{
129
+ class #{op_class}
130
+ def initialize(rhs)
131
+ self.op, self.rhs = :#{op}, rhs
132
+ end
133
+ end
134
+ }
135
+ end
136
+
137
+ module Expr
138
+ for op, op_class in UNARY_OPS
139
+ class_eval %Q{
140
+ def #{op} ; #{op_class}.new(self) end
141
+ }
142
+ end
143
+ end
144
+
145
+ #
146
+ # Binary operations.
147
+ #
148
+ BinaryOpExpr = Struct.new(:op, :lhs, :rhs)
149
+ class BinaryOpExpr
150
+ include OpExpr
151
+
152
+ def children; [lhs, rhs] end
153
+
154
+ def to_s_paren
155
+ op_string = " #{self.op} "
156
+ "(#{self.children.map{|c| c.to_s_paren}.join(op_string)})"
157
+ end
158
+
159
+ def to_s
160
+ inner = self.children.map{|c|
161
+ c.precedence < self.precedence ? "(#{c})" : c.to_s}
162
+ "#{inner.join(self.op_string)}"
163
+ end
164
+
165
+ protected
166
+ # Allow for control of spacing between operands.
167
+ def op_string; " #{self.op} " end
168
+ end
169
+
170
+ # Arithmetic
171
+ class ArithmeticOpExpr < BinaryOpExpr ; end
172
+ class AddExpr < ArithmeticOpExpr; end
173
+ class SubExpr < ArithmeticOpExpr; end
174
+ class MulExpr < ArithmeticOpExpr; end
175
+ class DivExpr < ArithmeticOpExpr; end
176
+ class PowExpr < ArithmeticOpExpr; end
177
+
178
+ # Comparison
179
+ class CompareOpExpr < BinaryOpExpr ; end
180
+ class EqExpr < CompareOpExpr ; end
181
+ class LTExpr < CompareOpExpr ; end
182
+ class LTEqExpr < CompareOpExpr ; end
183
+ class GTExpr < CompareOpExpr ; end
184
+ class GTEqExpr < CompareOpExpr ; end
185
+
186
+ # Logic -- see comments below
187
+ #class BooleanOpExpr < BinaryOpExpr ; end
188
+ #class AndExpr < BooleanOpExpr ; end
189
+ #class OrExpr < BooleanOpExpr ; end
190
+
191
+ BINARY_OPS = {
192
+ :+ => :AddExpr,
193
+ :- => :SubExpr,
194
+ :* => :MulExpr,
195
+ :/ => :DivExpr,
196
+ :** => :PowExpr,
197
+ :=~ => :EqExpr, # =, == and === are all taken!
198
+ :< => :LTExpr,
199
+ :<= => :LTEqExpr,
200
+ :> => :GTExpr,
201
+ :>= => :GTEqExpr}
202
+
203
+ # Note: can use & and | for logic, but there are problems with
204
+ # precedence: it binds before the (in)equality symbols. Also need to
205
+ # change the simplification code to treat them specially. This needs more
206
+ # thinking.
207
+ # :& => :AndExpr,
208
+ # :| => :OrExpr}
209
+
210
+ for op, op_class in BINARY_OPS
211
+ class_eval %Q{
212
+ class #{op_class}
213
+ def initialize(lhs, rhs)
214
+ self.lhs, self.op, self.rhs = lhs, :#{op}, rhs
215
+ end
216
+ end
217
+ }
218
+ end
219
+
220
+ module Expr
221
+ for op, op_class in BINARY_OPS
222
+ class_eval %Q{
223
+ def #{op} rhs ; #{op_class}.new(self, Expr.make(rhs)) end
224
+ }
225
+ end
226
+ end
227
+
228
+ class MulExpr
229
+ # Reduce spacing between factors.
230
+ def op_string; self.op.to_s end
231
+ end
232
+
233
+ class PowExpr
234
+ # Reduce spacing between base and exponent.
235
+ def op_string; self.op.to_s end
236
+ end
237
+ end
238
+ end