-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmaths.py
More file actions
314 lines (251 loc) · 10.4 KB
/
maths.py
File metadata and controls
314 lines (251 loc) · 10.4 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
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
import random
import math
import numpy as np
from functools import reduce
from fractions import Fraction
# General floating point tolerace (note np.isclose has tolerance 10^-5)
tol = 10 ** -8
class Point:
def __init__(self, *args, id=None):
'''
Handles n-dimensional points.
'''
self.id = id
if type(args[0]) == list:
args = args[0]
self.X = args[0]
self.x = args[0] # in case of case issues :)
try:
self.Y = args[1]
self.y = args[1]
except:
pass
try:
self.Z = args[2]
self.z = args[2]
except:
pass
self.coords = list(args)
def setid(self, id):
self.id = id
def distance(self, p2):
assert issubclass(type(p2), Point)
assert len(p2.coords) == len(self.coords)
return sum([(self.coords[i] - p2.coords[i]) ** 2 for i in range(len(self.coords))]) ** (1/2)
def __str__(self):
if self.id is None:
return repr(self.coords)
else:
return repr(self.coords) + f" ID: {self.id}"
def __repr__(self):
return repr(self.coords)
def __eq__(self, p2):
assert issubclass(type(p2), Point)
for i in range(len(self.coords)):
if not math.isclose(self.coords[i], p2.coords[i]):
return False
return True
def __add__(self, p2):
assert issubclass(type(p2), Point)
assert len(p2.coords) == len(self.coords)
return Point([self.coords[i] + p2.coords[i] for i in range(len(self.coords))])
__radd__ = __add__
def __sub__(self, p2):
assert issubclass(type(p2), Point)
assert len(p2.coords) == len(self.coords)
return Point([self.coords[i] - p2.coords[i] for i in range(len(self.coords))])
__rsub__ = __sub__
def __hash__(self):
return int((10 ** 20) * self.x) + int((10 ** 10) * self.y)
def distance(p1, p2):
assert issubclass(type(p1), Point)
assert issubclass(type(p2), Point)
assert len(p2.coords) == len(p1.coords)
return sum([(p1.coords[i] - p2.coords[i]) ** 2 for i in range(len(p1.coords))]) ** (1/2)
def mid(p1, p2):
assert issubclass(type(p1), Point)
assert issubclass(type(p2), Point)
assert len(p2.coords) == len(p1.coords)
return Point([Fraction(1, 2) * (p1.coords[i] + p2.coords[i]) for i in range(len(p1.coords))])
class Line:
def __init__(self, p1, p2):
self.p1 = p1
self.p2 = p2
self.length = distance(p1, p2)
class Gen1DPoly:
"""
Generic 1D polynominal.
Form: a_0 + a_1 * x + a_2 * x^2 + ...
Inputs:
coeffs - coefficients, None by default, array of floats if overriden
empty - None by default, zero polynominal of degree empty if overriden
"""
def __init__(self, coeffs = None, empty = None):
if coeffs != None:
self.d = len(coeffs) - 1
self.coeffs = coeffs
elif empty:
self.coeffs = [0] * (empty + 1)
self.d = empty
def randomise(self):
self.coeffs = [random.random() for coeff in self.coeffs]
def __add__(self, other):
# Check degrees
maxd = max(self.d, other.d)
self = self.coerce(maxd)
other = other.coerce(maxd)
return Gen1DPoly([self.coeffs[i] + other.coeffs[i] for i in range(self.d + 1)])
def __sub__(self, other):
# Check degrees
maxd = max(self.d, other.d)
self = self.coerce(maxd)
other = other.coerce(maxd)
return Gen1DPoly([self.coeffs[i] - other.coeffs[i] for i in range(self.d + 1)])
def __mul__(self, other):
if isinstance(other, Gen1DPoly):
newcoeffs = [0] * (self.d + other.d + 1)
for i in range(len(self.coeffs)):
for j in range(len(other.coeffs)):
newcoeffs[i + j] += self.coeffs[i] * other.coeffs[j]
else:
newcoeffs = [other * coef for coef in self.coeffs]
return Gen1DPoly(newcoeffs)
def value(self, x):
return sum([self.coeffs[i] * (x ** i) for i in range(self.d+1)])
def derivative(self):
return Gen1DPoly([self.coeffs[i] * i for i in range(1, self.d+1)]).coerce(self.d)
def integral(self):
# Returns the antiderivative
return Gen1DPoly([0] + [self.coeffs[i] / (i+1) for i in range(self.d + 1)])
def coerce(self, d):
# Coerce to a polynominal of degree d, with missing coefficents = 0
newcoeffs = self.coeffs + [0] * (d - self.d)
return Gen1DPoly(newcoeffs)
def similar(self, other, lower, upper):
# Calculates the integral of (f(x) -g(x))^2 from lower < x < upper
theint = (self - other) * (self - other)
res = theint.integral()
return res.value(upper) - res.value(lower)
class Gen2DPoly:
# Generic 2D polynominal.
# Form: a_00 + a_10 * x + a_01 * y + a_20 * x^2 + a_11 * x * y + a_02 * y^2 + ...
# Inputs:
# coeffs - coefficients, None by default, array of arrays if overriden
# empty - None by defaut, generates zero polynominal of degree empty if overriden
def __init__(self, coeffs = None, empty = None):
if coeffs != None:
self.d = len(coeffs) - 1
self.coeffs = coeffs
elif empty:
self.coeffs = [[0] * i for i in range(1, empty + 2)]
self.d = empty
def randomise(self):
self.coeffs = [[random.random() for coef in coeff] for coeff in self.coeffs]
def __add__(self, other):
# Check degrees
maxd = max(self.d, other.d)
self = self.coerce(maxd)
other = other.coerce(maxd)
return Gen2DPoly([[self.coeffs[i][j] + other.coeffs[i][j] for j in range(len(self.coeffs[i]))] for i in range(len(self.coeffs))])
def __sub__(self, other):
# Check degrees
maxd = max(self.d, other.d)
self = self.coerce(maxd)
other = other.coerce(maxd)
return Gen2DPoly([[self.coeffs[i][j] - other.coeffs[i][j] for j in range(len(self.coeffs[i]))] for i in range(len(self.coeffs))])
def __mul__(self, other):
if isinstance(other, Gen2DPoly):
newcoeffs = [[0] * i for i in range(1, 2 * (self.d + 1))]
for i in range(len(self.coeffs)):
for j in range(len(self.coeffs[i])):
for k in range(len(other.coeffs)):
for l in range(len(other.coeffs[k])):
newcoeffs[i + k][j + l] += self.coeffs[i][j] * other.coeffs[k][l]
else:
newcoeffs = [[other * coef for coef in coeff] for coeff in self.coeffs]
return Gen2DPoly(newcoeffs)
__rmul__ = __mul__
def coerce(self, d):
# Coerce to a polynominal of degree d, with missing coefficiets = 0
if self.d >= d:
# Truncate
return Gen2DPoly(self.coeffs[:d + 1])
newcoeffs = [[0] * i for i in range(1, d+2)]
for i in range(len(self.coeffs)):
for j in range(len(self.coeffs[i])):
newcoeffs[i][j] = self.coeffs[i][j]
return Gen2DPoly(newcoeffs)
def create1DPoly(self, x = None, y = None):
newcoeffs = [0] * (self.d + 1)
if x == None:
for pow in range(len(self.coeffs)):
for step in range(len(self.coeffs[pow])):
newcoeffs[pow - step] += self.coeffs[pow][step] * y ** (step)
elif y == None:
for pow in range(len(self.coeffs)):
for step in range(len(self.coeffs[pow])):
newcoeffs[step] += self.coeffs[pow][step] * x ** (pow - step)
return Gen1DPoly(newcoeffs)
def value(self, x, y):
return self.create1DPoly(y=y).value(x)
def partialx(self):
newcoeffs = [[0] * (i + 1) for i in range(self.d)]
for pow in range(len(self.coeffs)):
for step in range(len(self.coeffs[pow])):
if step == pow:
continue
else:
newcoeffs[pow - 1][step] += self.coeffs[pow][step] * (pow - step)
return Gen2DPoly(newcoeffs).coerce(self.d)
def partialy(self):
newcoeffs = [[0] * (i) for i in range(1,self.d + 1)]
for pow in range(len(self.coeffs)):
for step in range(len(self.coeffs[pow])):
if step == 0:
continue
else:
newcoeffs[pow - 1][step - 1] += self.coeffs[pow][step] * (step)
return Gen2DPoly(newcoeffs).coerce(self.d)
def grad(self):
return VectorFieldPoly2D(self.partialx(), self.partialy())
class VectorFieldPoly2D:
# Polynominal 2D Vector Field
# Inputs:
# fi - 2D Polynominal in x, y describing the i-component
# fj - 2D Polynominal in x, y describing the j-component
def __init__(self, fi, fj):
self.fi = fi
self.fj = fj
def __add__(self, other):
return VectorFieldPoly2D(self.fi + other.fi, self.fj + other.fj)
def __sub__(self, other):
return VectorFieldPoly2D(self.fi - other.fi, self.fj - other.fj)
def __mul__(self, other):
return VectorFieldPoly2D(other * self.fi, other * self.fj)
__rmul__ = __mul__
def abs2(self):
return (self.fi * self.fi + self.fj * self.fj)
def dot(self, other):
if isinstance(other, VectorFieldPoly2D):
return self.fi * other.fi + self.fj * other.fj
else:
# Probably a 2x2 matrix
return VectorFieldPoly2D(other[0][0] * self.fi + other[1][0] * self.fj, other[0][1] * self.fi + other[1][1] * self.fj)
def grad(self):
# Using the tensor definition. Returns a matrix (list of lists)
return [[self.fi.partialx(), self.fj.partialx()], [self.fi.partialy(), self.fj.partialy()]]
def jacobian(self):
return [[self.fi.partialx(), self.fi.partialy()], [self.fj.partialx(), self.fj.partialy()]]
def divergence(self):
return self.fi.partialx() + self.fj.partialy()
def laplacian(self):
return VectorFieldPoly2D(self.fi.partialx().partialx() + self.fi.partialy().partialy(), self.fj.partialx().partialx() + self.fj.partialy().partialy())
#quad = Gen1DPoly([6,4,2])
#quad2d = Gen2DPoly([[6],[3,6],[8,1,8]])
#quad22d = Gen2DPoly([[2],[4,5],[-2,-9,-12]])
#vec2d = VectorFieldPoly2D(quad2d, quad22d)
# Testing zone
if __name__ == "__main__":
p = Point(1, 2)
pp = Point(3, 4)
print(p + pp)