-
-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathVariable.lua
More file actions
261 lines (226 loc) · 7.33 KB
/
Variable.lua
File metadata and controls
261 lines (226 loc) · 7.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
local table = require 'ext.table'
local complex = require 'complex'
local Expression = require 'symmath.Expression'
local symmath
--[[
fields:
name = variable name
dependentVars = table of vars that this var depends on
value = (optional) value of this var ... as a Lua number
set = what set does this variable belong to?
dependentVars = table of...
table containing...
src =
= self for dx/dy
= TensorRef(self, ...) for dx^I/dy
wrt =
= var for dx/dy
= TensorRef(var, ...) for dx/dy^J
So I am going to consider TensorRef(Variable) different from Variable
But for now I'm not going to consider variance of TensorRef
--]]
local Variable = Expression:subclass()
Variable.precedence = 10 -- high since it will never have nested members
Variable.name = 'Variable'
--[[
args:
name = name of variable
dependentVars = variables this var is dependent on
value = numerical value of this variable
--]]
function Variable:init(name, dependentVars, value, set)
self.name = assert(name, "Variable expected name")
--[[ this works, but is rigid.
-- once it is set, you can't change 'fixVariableNames' and get different behavior.
-- you have to manually clear all created variables' nameForExporterTables
-- so instead I'm doing it in Expression:nameForExporter
-- to avoid the require() infinite loop if symmath itself is building these Variables?
symmath = symmath or require 'symmath.namespace'()
-- here, instead of in tostring()/Export, apply the 'fixVariableName'
-- so that all the exporter conditional stuff is handled by nameForExporter()
if symmath.fixVariableNames then
-- manually apply over all exporters that have their own 'fixVariableName' functions
for _,exporter in ipairs{
'Console',
'LaTeX',
} do
local overrideName = symmath.export[exporter]:fixVariableName(self.name)
if overrideName ~= self.name then
self:nameForExporter(exporter, overrideName)
end
end
end
--]]
-- optional
self.value = value
-- optional - inferred otherwise
if not set then
if complex:isa(value) then
set = require 'symmath.set.sets'.complex
elseif type(value) == 'number' then
set = require 'symmath.set.sets'.real
else
-- default set ... Real or Universal?
set = require 'symmath.set.sets'.default
end
end
self.set = set
-- optional
if dependentVars then
self:setDependentVars(table.unpack(dependentVars))
end
end
-- return variable references ... so if the original gets modified, the rest will be updated as well
function Variable:clone()
return self
end
-- same
function Variable:shallowCopy()
return self
end
--[[ this is the same as Derivative.visitors.Prune.self
-- either is interoperable (right?)
-- (TODO double check this with the non-coordinate derivatives that are used to create commutation coefficients)
function Variable:evaluateDerivative(deriv, ...)
local derivs = {...}
if #derivs == 1 and derivs[1] == self then
return require 'symmath.Constant'(1)
end
for i=1,#derivs do
if derivs[i] == self then
return require 'symmath.Constant'(0)
end
end
end
--]]
-- Variable equality is by name and value at the moment
-- this way log(e) fails to simplify, but log(symmath.e) simplifies to 1
function Variable.match(a, b, matches)
-- same as in Expression.match
matches = matches or table()
if b.wildcardMatches then
if not b:wildcardMatches(a, matches) then return false end
return (matches[1] or true), table.unpack(matches, 2, table.maxn(matches))
end
if getmetatable(a) ~= getmetatable(b) then return false end
-- what if it is a subclass?
if a.name ~= b.name then return false end
-- same as return true in Expression.match
return (matches[1] or true), table.unpack(matches, 2, table.maxn(matches))
end
-- ok for performance's sake, since :match() is so slow, I'm overriding __eq
function Variable.__eq(a,b)
return getmetatable(a) == getmetatable(b)
and a.name == b.name
end
--[[
assign or concatenate?
Maxima would concatenate
but that'd leave us no room to remove
so I'll assign
but I'll only assign for unique TensorRefs of self, so x'^i':setDependentVars(...) will clear all the other x'^i" setDependentVars but leave the x, x'^ij', etc setDependentVars based on # of tensor indexes
--]]
function Variable:setDependentVars(...)
-- filter out setDependentVars() of matching # of tensorref indexes
-- this way x:setDependentVars(y) and x'^i':setDependentVars(y) are separate
if self.dependentVars then
self.dependentVars = self.dependentVars:filter(function(depvar)
return depvar.src ~= self
end)
else
self.dependentVars = table()
end
self.dependentVars:append(table{...}:mapi(function(wrt)
return {src=self, wrt=wrt}
end))
self:removeDuplicateDepends()
end
function Variable:removeDuplicateDepends()
if self.dependentVars then
for i=#self.dependentVars-2,1,-1 do
local da = self.dependentVars[i]
local db = self.dependentVars[i+1]
if da.src == db.src
and da.wrt == db.wrt
then
self.dependentVars:remove(i)
end
end
end
end
function Variable:getDependentVars()
return (self.dependentVars or table()):mapi(function(depvar, k, t)
return depvar.src == self and depvar.wrt or nil, #t+1
end)
end
--[[
similar function is found in symmath/tensor/Ref.lua
only return true for the dependentVars entries with src==self
that match x (either Variable equals, or TensorRef with matching Variable and # of indexes)
--]]
function Variable:dependsOn(x)
--DEBUG:local print = symmath.tostring.print or print
--DEBUG(@5):print('does Variable '..self..' depend on '..x..'?')
if x == self then return true end
local TensorRef = require 'symmath.tensor.Ref'
if self.dependentVars then
for _,depvar in ipairs(self.dependentVars) do
if depvar.src == self then
-- [[ matches TensorRef:dependsOn
local wrt = depvar.wrt
if Variable:isa(wrt)
and wrt == x
then
return true
end
if TensorRef:isa(wrt)
and TensorRef:isa(x)
and #wrt == #x
and wrt[1] == x[1]
then
return true
end
--]]
end
end
end
return false
end
--[[
similar is function found in symmath/tensor/Ref.lua
also an equivalent function in symmath/op/eq.lua
set a variable's dependent vars to all variables found in the expression
--]]
function Variable:inferDepenedentVars(...)
self:setDependentVars(Variable.super.getDependentVars(...):unpack())
end
function Variable:getRealRange()
if not rawequal(self.set, self.cachedSet) then self.cachedSet = nil end
if self.cachedSet then return self.cachedSet end
symmath = symmath or require 'symmath'
local RealSubset = symmath.set.RealSubset
if self.value then
if type(self.value) == 'number' then
self.cachedSet = RealSubset(self.value, self.value, true, true)
return self.cachedSet
elseif complex:isa(self.value) and self.value.im == 0 then
self.cachedSet = RealSubset(self.value.re, self.value.re, true, true)
return self.cachedSet
end
end
-- TODO why test this? when won't it be true?
if RealSubset:isa(self.set) then
self.cachedSet = self.set
return self.cachedSet
end
-- assuming start and finish are defined in all Real's subclasses
-- what about Integer? Integer's RealInterval is discontinuous ...
end
-- lim x->a x => a
-- otherwise lim x->a y = y
-- TODO but what if y depends on x ? would that be a Variable? what is the scope of use of Variable.value ? esp when considering total derivatives?
function Variable:evaluateLimit(x, a, side)
if x == self then return a end
return self
end
return Variable