IntegralElimination 0.1.0__tar.gz
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.
- integralelimination-0.1.0/IntegralElimination/IntegralAlgebra.py +312 -0
- integralelimination-0.1.0/IntegralElimination/IntegralMonomial.py +107 -0
- integralelimination-0.1.0/IntegralElimination/IntegralPolynomial.py +138 -0
- integralelimination-0.1.0/IntegralElimination/__init__.py +4 -0
- integralelimination-0.1.0/IntegralElimination/convert.py +38 -0
- integralelimination-0.1.0/IntegralElimination/critical_pairs.py +372 -0
- integralelimination-0.1.0/IntegralElimination/exponentials.py +191 -0
- integralelimination-0.1.0/IntegralElimination/integral_elimination.py +72 -0
- integralelimination-0.1.0/IntegralElimination/ordering.py +58 -0
- integralelimination-0.1.0/IntegralElimination/reduction.py +193 -0
- integralelimination-0.1.0/IntegralElimination/utils.py +63 -0
- integralelimination-0.1.0/IntegralElimination.egg-info/PKG-INFO +17 -0
- integralelimination-0.1.0/IntegralElimination.egg-info/SOURCES.txt +36 -0
- integralelimination-0.1.0/IntegralElimination.egg-info/dependency_links.txt +1 -0
- integralelimination-0.1.0/IntegralElimination.egg-info/requires.txt +4 -0
- integralelimination-0.1.0/IntegralElimination.egg-info/top_level.txt +1 -0
- integralelimination-0.1.0/LICENSE +304 -0
- integralelimination-0.1.0/PKG-INFO +17 -0
- integralelimination-0.1.0/README.md +2 -0
- integralelimination-0.1.0/setup.cfg +4 -0
- integralelimination-0.1.0/setup.py +23 -0
- integralelimination-0.1.0/tests/test01.py +19 -0
- integralelimination-0.1.0/tests/test02.py +19 -0
- integralelimination-0.1.0/tests/test03.py +19 -0
- integralelimination-0.1.0/tests/test04_LM_LC.py +20 -0
- integralelimination-0.1.0/tests/test05_internal_sympy_repr.py +18 -0
- integralelimination-0.1.0/tests/test06_cut_P.py +18 -0
- integralelimination-0.1.0/tests/test07_get_PI_PN.py +26 -0
- integralelimination-0.1.0/tests/test08_monomials_product.py +23 -0
- integralelimination-0.1.0/tests/test09_polynomials_product.py +24 -0
- integralelimination-0.1.0/tests/test10_integrate.py +24 -0
- integralelimination-0.1.0/tests/test11_reduced_product.py +23 -0
- integralelimination-0.1.0/tests/test12_power.py +26 -0
- integralelimination-0.1.0/tests/test13_reduced_power.py +24 -0
- integralelimination-0.1.0/tests/test14_reduce_simple_case.py +48 -0
- integralelimination-0.1.0/tests/test15_reduce_reduced_power.py +40 -0
- integralelimination-0.1.0/tests/test16_1_algo_reduce.py +27 -0
- integralelimination-0.1.0/tests/test16_2_algo_reduce.py +29 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import sympy as sp
|
|
2
|
+
from ordered_set import OrderedSet
|
|
3
|
+
from IPython.display import display, Math
|
|
4
|
+
|
|
5
|
+
from .ordering import IMO
|
|
6
|
+
from .IntegralMonomial import IM
|
|
7
|
+
from .utils import (
|
|
8
|
+
is_int,
|
|
9
|
+
ShuffleList
|
|
10
|
+
)
|
|
11
|
+
from .IntegralPolynomial import IntegralPolynomial
|
|
12
|
+
from .reduction import (
|
|
13
|
+
reduction_M_by_P_simple_case,
|
|
14
|
+
reduction_M_by_P_reduced_power,
|
|
15
|
+
reduction_M_by_P,
|
|
16
|
+
reduce,auto_reduce
|
|
17
|
+
)
|
|
18
|
+
from .critical_pairs import (
|
|
19
|
+
critical_pairs_PI_QI,
|
|
20
|
+
critical_pairs_PI_QN,
|
|
21
|
+
critical_pairs_PN_QN,
|
|
22
|
+
critical_pairs
|
|
23
|
+
)
|
|
24
|
+
from .exponentials import (
|
|
25
|
+
find_A_A0_G_F,
|
|
26
|
+
update_exp,
|
|
27
|
+
extend_X_with_exp
|
|
28
|
+
)
|
|
29
|
+
from .integral_elimination import integral_elimination
|
|
30
|
+
|
|
31
|
+
class IntegralAlgebra():
|
|
32
|
+
"""
|
|
33
|
+
integral polynomials will be represented as follows :
|
|
34
|
+
eq = {IM(x(t):3, IM(y(t),1,y(t)):theta+lambda}
|
|
35
|
+
using eq.as_coefficients_dict(IM)
|
|
36
|
+
It means that eq is the sum of the two tuples
|
|
37
|
+
"""
|
|
38
|
+
def __init__(self, order, parameters):
|
|
39
|
+
self.order = order
|
|
40
|
+
self.t = sp.Symbol("t")
|
|
41
|
+
self.used_order = order
|
|
42
|
+
self.IMO_le = lambda m1, m2 : IMO(m1, m2, self.used_order)
|
|
43
|
+
self.parameters = parameters
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def update_IMO(self, order):
|
|
47
|
+
self.used_order = order
|
|
48
|
+
self.IMO = lambda m1, m2 : IMO(m1, m2, self.used_order)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def LM_LC(self, P:IntegralPolynomial) -> tuple[IM, sp.Expr]:
|
|
52
|
+
"""
|
|
53
|
+
get leading monomials and coeff
|
|
54
|
+
"""
|
|
55
|
+
L = list(P.dict.items())
|
|
56
|
+
LC, LM = L[0][1], L[0][0]
|
|
57
|
+
for M,coeff in L:
|
|
58
|
+
#if True then M <= LM, else M > LM
|
|
59
|
+
if self.IMO_le(M, LM) == False:
|
|
60
|
+
LM = M # im > LM
|
|
61
|
+
LC = coeff
|
|
62
|
+
return LM, LC
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def display_rules(self, sys):
|
|
66
|
+
display(Math(r"\{"))
|
|
67
|
+
for eq in sys:
|
|
68
|
+
LM, LC = self.LM_LC(eq)
|
|
69
|
+
key = IntegralPolynomial(LC*LM)
|
|
70
|
+
value = self.polynomials_subtraction(eq, key)
|
|
71
|
+
arrow = r"{\Huge \color{red} \mathbf{\rightarrow}}"
|
|
72
|
+
key_repr = key.repr_display_math()
|
|
73
|
+
value_repr = value.repr_display_math()
|
|
74
|
+
export = Math(f"{key_repr} {arrow} {value_repr}")
|
|
75
|
+
display(export)
|
|
76
|
+
display(Math(r"\}"))
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def normalize_LC_of_P(self,
|
|
80
|
+
P: IntegralPolynomial) -> IntegralPolynomial:
|
|
81
|
+
_, LC = self.LM_LC(P)
|
|
82
|
+
normalized_P = {}
|
|
83
|
+
for M, coeff in P.dict.items():
|
|
84
|
+
normalized_P[M] = coeff/LC
|
|
85
|
+
# Since we only have division
|
|
86
|
+
# coeffs can't be zero after
|
|
87
|
+
# a simplification process
|
|
88
|
+
# then, using copy=True, we disable de simplification
|
|
89
|
+
# to optimize the reduction process
|
|
90
|
+
return IntegralPolynomial(normalized_P,copy=True), LC
|
|
91
|
+
|
|
92
|
+
def monomials_product(self, M: IM, N: IM )-> IntegralPolynomial:
|
|
93
|
+
e = M.get_nb_int()
|
|
94
|
+
f = N.get_nb_int()
|
|
95
|
+
m0n0 = M.get_content()[0]*N.get_content()[0]
|
|
96
|
+
if e == f == 0:
|
|
97
|
+
MN = IM(m0n0)
|
|
98
|
+
elif e == 0 and f != 0:
|
|
99
|
+
N1p = N.cut("1+").get_content() #N1plus
|
|
100
|
+
MN = IM(m0n0,*N1p)
|
|
101
|
+
elif e != 0 and f== 0 :
|
|
102
|
+
M1p = M.cut("1+").get_content() #N1plus
|
|
103
|
+
MN = IM(m0n0,*M1p)
|
|
104
|
+
else:
|
|
105
|
+
N1p = N.cut("1+").get_content() #N1plus
|
|
106
|
+
M1p = M.cut("1+").get_content() #N1plus
|
|
107
|
+
sh = ShuffleList(M1p,N1p)
|
|
108
|
+
MN = sp.Add(*[IM(m0n0,*elem) for elem in sh])
|
|
109
|
+
return IntegralPolynomial(MN)
|
|
110
|
+
|
|
111
|
+
def product_P_Coeff(self,
|
|
112
|
+
P: IntegralPolynomial,
|
|
113
|
+
alpha: sp.Expr) -> IntegralPolynomial:
|
|
114
|
+
"""
|
|
115
|
+
alpha is cst
|
|
116
|
+
"""
|
|
117
|
+
alpha_P = {}
|
|
118
|
+
for M in P.dict:
|
|
119
|
+
alpha_P[M] = P.dict[M]*alpha
|
|
120
|
+
return IntegralPolynomial(alpha_P)
|
|
121
|
+
|
|
122
|
+
def polynomials_add(self,
|
|
123
|
+
P: IntegralPolynomial,
|
|
124
|
+
Q: IntegralPolynomial) -> IntegralPolynomial:
|
|
125
|
+
PplusQ = P.copy().dict
|
|
126
|
+
for N in Q.dict:
|
|
127
|
+
c_N = Q.dict[N]
|
|
128
|
+
if N not in PplusQ:
|
|
129
|
+
PplusQ[N] = c_N
|
|
130
|
+
else:
|
|
131
|
+
PplusQ[N] += c_N
|
|
132
|
+
PplusQ = {M:coeff for M, coeff in PplusQ.items() if coeff != 0}
|
|
133
|
+
return IntegralPolynomial(PplusQ)
|
|
134
|
+
|
|
135
|
+
def polynomials_subtraction(self,
|
|
136
|
+
P: IntegralPolynomial,
|
|
137
|
+
Q: IntegralPolynomial) -> IntegralPolynomial:
|
|
138
|
+
PminusQ = P.copy().dict
|
|
139
|
+
for N in Q.dict:
|
|
140
|
+
c_N = Q.dict[N]
|
|
141
|
+
if N not in PminusQ:
|
|
142
|
+
PminusQ[N] = -c_N
|
|
143
|
+
else:
|
|
144
|
+
PminusQ[N] -= c_N
|
|
145
|
+
PminusQ = {M:coeff for M, coeff in PminusQ.items() if coeff != 0}
|
|
146
|
+
return IntegralPolynomial(PminusQ)
|
|
147
|
+
|
|
148
|
+
def polynomials_product(self,
|
|
149
|
+
P: IntegralPolynomial,
|
|
150
|
+
Q: IntegralPolynomial) -> IntegralPolynomial:
|
|
151
|
+
PQ = IntegralPolynomial(0)
|
|
152
|
+
for M_P, c_P in P.dict.items():
|
|
153
|
+
for M_Q, c_Q in Q.dict.items():
|
|
154
|
+
M_PdotM_Q = self.monomials_product(M_P,M_Q)
|
|
155
|
+
c_P_c_Q_M_PdotM_Q = self.product_P_Coeff(M_PdotM_Q, c_P*c_Q)
|
|
156
|
+
PQ = self.polynomials_add(PQ, c_P_c_Q_M_PdotM_Q)
|
|
157
|
+
return PQ
|
|
158
|
+
|
|
159
|
+
def integrate_monomial(self, M: IM):
|
|
160
|
+
return IM(1,*M.get_content())
|
|
161
|
+
|
|
162
|
+
def integrate_polynomial(self,
|
|
163
|
+
P: IntegralPolynomial) -> IntegralPolynomial:
|
|
164
|
+
Int_P = {}
|
|
165
|
+
for M, coeff in P.dict.items():
|
|
166
|
+
Int_M = self.integrate_monomial(M)
|
|
167
|
+
Int_P[Int_M] = coeff
|
|
168
|
+
return IntegralPolynomial(Int_P)
|
|
169
|
+
|
|
170
|
+
def add_prefix_to_polynomial(self,
|
|
171
|
+
prefix: IM,
|
|
172
|
+
P: IntegralPolynomial) -> IntegralPolynomial:
|
|
173
|
+
new_P = {}
|
|
174
|
+
for M, coeff in P.dict.items():
|
|
175
|
+
pref_M = M.add_prefix(prefix)
|
|
176
|
+
new_P[pref_M] = coeff
|
|
177
|
+
return IntegralPolynomial(new_P)
|
|
178
|
+
|
|
179
|
+
def reduced_product(self,
|
|
180
|
+
P:IntegralPolynomial,
|
|
181
|
+
M:IM) -> IntegralPolynomial:
|
|
182
|
+
"""
|
|
183
|
+
see section 3.1
|
|
184
|
+
|
|
185
|
+
reduced_product = (P \cdot \int M) - \int (M \cdot P)
|
|
186
|
+
"""
|
|
187
|
+
M = IntegralPolynomial(M)
|
|
188
|
+
IntM = self.integrate_polynomial(M)
|
|
189
|
+
|
|
190
|
+
#(P \cdot \int M)
|
|
191
|
+
PdotIntM = self.polynomials_product(P,IntM)
|
|
192
|
+
|
|
193
|
+
#\int (M \cdot P)
|
|
194
|
+
IntMdotP = self.integrate_polynomial(
|
|
195
|
+
self.polynomials_product(M,P)
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
reduced_product = self.polynomials_add(
|
|
199
|
+
PdotIntM ,self.product_P_Coeff(IntMdotP, -1)
|
|
200
|
+
)
|
|
201
|
+
return reduced_product
|
|
202
|
+
|
|
203
|
+
def polynomial_power(self,
|
|
204
|
+
P:IntegralPolynomial,
|
|
205
|
+
n:int) -> IntegralPolynomial:
|
|
206
|
+
assert is_int(n)
|
|
207
|
+
assert isinstance(P,IntegralPolynomial)
|
|
208
|
+
if n == 0: return IntegralPolynomial(IM(1))
|
|
209
|
+
P_pow_n = P
|
|
210
|
+
for _ in range(n-1):
|
|
211
|
+
P_pow_n = self.polynomials_product(P_pow_n,P)
|
|
212
|
+
return P_pow_n
|
|
213
|
+
|
|
214
|
+
def reduced_power(self,
|
|
215
|
+
P:IntegralPolynomial,
|
|
216
|
+
n:int) -> IntegralPolynomial:
|
|
217
|
+
"""
|
|
218
|
+
see section 3.2
|
|
219
|
+
|
|
220
|
+
P^{\circled{n}} = n (\int (P_I[cut{1+}] cdot P_N^{n-1})) + P_N^n
|
|
221
|
+
"""
|
|
222
|
+
assert is_int(n) and n >= 1
|
|
223
|
+
P_I = P.get_P_I()
|
|
224
|
+
P_N = P.get_P_N()
|
|
225
|
+
P_I_1plus = P_I.cut_P("1+")
|
|
226
|
+
P_N_pow_n_minus_one = self.polynomial_power(P_N, n-1)
|
|
227
|
+
P_N_pow_n = self.polynomials_product(P_N_pow_n_minus_one, P_N)
|
|
228
|
+
|
|
229
|
+
#lets compute n (\int (P_I[cut{1+}] cdot P_N^{n-1})) in temp
|
|
230
|
+
temp = self.integrate_polynomial(
|
|
231
|
+
self.polynomials_product(P_I_1plus, P_N_pow_n_minus_one)
|
|
232
|
+
)
|
|
233
|
+
temp = self.product_P_Coeff(temp, n)
|
|
234
|
+
|
|
235
|
+
reduced_power = self.polynomials_add(temp, P_N_pow_n)
|
|
236
|
+
return reduced_power
|
|
237
|
+
|
|
238
|
+
def reduction_M_by_P_simple_case(self,
|
|
239
|
+
M: IM,
|
|
240
|
+
P: IntegralPolynomial
|
|
241
|
+
) -> IntegralPolynomial:
|
|
242
|
+
return reduction_M_by_P_simple_case(self, M, P)
|
|
243
|
+
|
|
244
|
+
def reduction_M_by_P_reduced_power(self,
|
|
245
|
+
M: IM,
|
|
246
|
+
P: IntegralPolynomial
|
|
247
|
+
) -> IntegralPolynomial:
|
|
248
|
+
return reduction_M_by_P_reduced_power(self, M, P)
|
|
249
|
+
|
|
250
|
+
def reduction_M_by_P(self,
|
|
251
|
+
M: IM,
|
|
252
|
+
P: IntegralPolynomial
|
|
253
|
+
) -> IntegralPolynomial:
|
|
254
|
+
return reduction_M_by_P(self,M,P)
|
|
255
|
+
|
|
256
|
+
def reduce(self,
|
|
257
|
+
Q: IntegralPolynomial,
|
|
258
|
+
T: OrderedSet[IntegralPolynomial]
|
|
259
|
+
) -> IntegralPolynomial:
|
|
260
|
+
return reduce(self,Q,T)
|
|
261
|
+
|
|
262
|
+
def auto_reduce(self,
|
|
263
|
+
T:OrderedSet[IntegralPolynomial]
|
|
264
|
+
) -> OrderedSet[IntegralPolynomial]:
|
|
265
|
+
return auto_reduce(self,T)
|
|
266
|
+
|
|
267
|
+
def critical_pairs_PI_QI(self,
|
|
268
|
+
P: IntegralPolynomial,
|
|
269
|
+
Q: IntegralPolynomial) -> IntegralPolynomial:
|
|
270
|
+
return critical_pairs_PI_QI(self,P,Q)
|
|
271
|
+
|
|
272
|
+
def critical_pairs_PI_QN(self,
|
|
273
|
+
P: IntegralPolynomial,
|
|
274
|
+
Q: IntegralPolynomial) -> IntegralPolynomial:
|
|
275
|
+
return critical_pairs_PI_QN(self,P,Q)
|
|
276
|
+
|
|
277
|
+
def critical_pairs_PN_QN(self,
|
|
278
|
+
P: IntegralPolynomial,
|
|
279
|
+
Q: IntegralPolynomial) -> IntegralPolynomial:
|
|
280
|
+
return critical_pairs_PN_QN(self,P,Q)
|
|
281
|
+
|
|
282
|
+
def critical_pairs(self,
|
|
283
|
+
R: OrderedSet[IntegralPolynomial]
|
|
284
|
+
) -> OrderedSet [IntegralPolynomial]:
|
|
285
|
+
return critical_pairs(self,R)
|
|
286
|
+
|
|
287
|
+
def find_A_A0_G_F(self,
|
|
288
|
+
P:IntegralPolynomial
|
|
289
|
+
) -> tuple[IntegralPolynomial]:
|
|
290
|
+
return find_A_A0_G_F(self,P)
|
|
291
|
+
|
|
292
|
+
def update_exp(self,
|
|
293
|
+
T_prime: OrderedSet[IntegralPolynomial],
|
|
294
|
+
E: OrderedSet[sp.Function, sp.Function, IntegralPolynomial]
|
|
295
|
+
) -> tuple:
|
|
296
|
+
return update_exp(self,T_prime,E)
|
|
297
|
+
|
|
298
|
+
def extend_X_with_exp(self,
|
|
299
|
+
E: set[sp.Function, sp.Function, IntegralPolynomial]
|
|
300
|
+
) -> list:
|
|
301
|
+
return extend_X_with_exp(self, E)
|
|
302
|
+
|
|
303
|
+
def integral_elimination(self,
|
|
304
|
+
F: OrderedSet[IntegralPolynomial],
|
|
305
|
+
disable_exp: bool = False,
|
|
306
|
+
disable_critical_pairs: bool = False,
|
|
307
|
+
nb_iter: int = 0) -> tuple:
|
|
308
|
+
return integral_elimination(self,
|
|
309
|
+
F,
|
|
310
|
+
disable_exp=disable_exp,
|
|
311
|
+
disable_critical_pairs=disable_critical_pairs,
|
|
312
|
+
nb_iter=nb_iter)
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import sympy as sp
|
|
2
|
+
|
|
3
|
+
from .utils import (
|
|
4
|
+
has_add_in_list,
|
|
5
|
+
is_float
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
def has_multiplicative_constant(expr):
|
|
9
|
+
"""
|
|
10
|
+
Search if expr has a multiplicative cst != than 1
|
|
11
|
+
"""
|
|
12
|
+
if is_float(expr):
|
|
13
|
+
if expr == 1:
|
|
14
|
+
return False
|
|
15
|
+
else:
|
|
16
|
+
return True
|
|
17
|
+
elif expr.is_Mul:
|
|
18
|
+
factors = expr.as_ordered_factors()
|
|
19
|
+
for factor in factors:
|
|
20
|
+
if factor.is_number and factor != 1:
|
|
21
|
+
return True
|
|
22
|
+
return False
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class IM(sp.Function):
|
|
26
|
+
def __init__(self, *args):
|
|
27
|
+
self.c = (*args,)
|
|
28
|
+
self.t = sp.Symbol("t")
|
|
29
|
+
|
|
30
|
+
assert "IM" not in str(self.c)
|
|
31
|
+
assert not has_add_in_list(self.c)
|
|
32
|
+
|
|
33
|
+
#check if parameters (symbols) are in self.c
|
|
34
|
+
assert not any([
|
|
35
|
+
len(elem.free_symbols - {self.t})
|
|
36
|
+
for elem in self.c if not is_float(elem)
|
|
37
|
+
])
|
|
38
|
+
#check that every multiplicative csts are 1
|
|
39
|
+
assert not any([
|
|
40
|
+
has_multiplicative_constant(elem)
|
|
41
|
+
for elem in self.c
|
|
42
|
+
])
|
|
43
|
+
|
|
44
|
+
def get_nb_int(self):
|
|
45
|
+
return len(self.c) - 1
|
|
46
|
+
|
|
47
|
+
def get_content(self):
|
|
48
|
+
return self.c
|
|
49
|
+
|
|
50
|
+
def cut(self, cut_type):
|
|
51
|
+
"""
|
|
52
|
+
See Definition 7
|
|
53
|
+
"""
|
|
54
|
+
if cut_type == "0":
|
|
55
|
+
return IM(self.c[0])
|
|
56
|
+
|
|
57
|
+
elif cut_type == "1":
|
|
58
|
+
assert self.get_nb_int() >= 1
|
|
59
|
+
return IM(self.c[1])
|
|
60
|
+
|
|
61
|
+
elif cut_type == "1+":
|
|
62
|
+
assert self.get_nb_int() >= 1
|
|
63
|
+
return IM(*self.c[1:])
|
|
64
|
+
|
|
65
|
+
elif cut_type == "i1+":
|
|
66
|
+
if self.get_nb_int() == 0:
|
|
67
|
+
return IM(1)
|
|
68
|
+
else:
|
|
69
|
+
return IM(1,*self.c[1:])
|
|
70
|
+
|
|
71
|
+
elif cut_type == "i2+":
|
|
72
|
+
if self.get_nb_int() == 1:
|
|
73
|
+
return IM(1)
|
|
74
|
+
else:
|
|
75
|
+
assert self.get_nb_int() >= 2
|
|
76
|
+
return IM(1,*self.c[2:])
|
|
77
|
+
else:
|
|
78
|
+
raise NotImplementedError
|
|
79
|
+
|
|
80
|
+
def get_suffix(self, i):
|
|
81
|
+
assert 0 <= i <= self.get_nb_int()
|
|
82
|
+
return IM(*self.get_content()[i:])
|
|
83
|
+
|
|
84
|
+
def get_prefix(self, i):
|
|
85
|
+
assert 0 <= i <= self.get_nb_int()
|
|
86
|
+
return IM(*self.get_content()[:i])
|
|
87
|
+
|
|
88
|
+
def add_prefix(self, M):
|
|
89
|
+
prefixed_M = IM(*M.get_content(),*self.get_content())
|
|
90
|
+
return prefixed_M
|
|
91
|
+
|
|
92
|
+
def __str__(self):
|
|
93
|
+
return f"IM({str(self.c)[1:-1]})"
|
|
94
|
+
|
|
95
|
+
def get_sympy_repr(self):
|
|
96
|
+
if len(self.c) > 1:
|
|
97
|
+
expr = sp.Integral(self.c[-1], self.t)
|
|
98
|
+
for e in reversed(self.c[1:-1]):
|
|
99
|
+
expr = (sp.Integral(e*expr, self.t))
|
|
100
|
+
expr = self.c[0]*expr
|
|
101
|
+
elif len(self.c) == 1:
|
|
102
|
+
expr = self.c[0]
|
|
103
|
+
return expr
|
|
104
|
+
|
|
105
|
+
def _latex(self, printer=None):
|
|
106
|
+
return f"{printer.doprint(self.get_sympy_repr())}"
|
|
107
|
+
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import sympy as sp
|
|
2
|
+
import sympy.printing as printing
|
|
3
|
+
|
|
4
|
+
from .IntegralMonomial import IM
|
|
5
|
+
from .utils import is_float
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class IntegralPolynomial():
|
|
9
|
+
|
|
10
|
+
def __init__(self,
|
|
11
|
+
P,
|
|
12
|
+
copy=False):
|
|
13
|
+
if type(P) == dict:
|
|
14
|
+
self.dict = {}
|
|
15
|
+
if not copy:
|
|
16
|
+
for M, coeff in P.items():
|
|
17
|
+
coeff = sp.cancel(coeff)
|
|
18
|
+
if coeff != 0:
|
|
19
|
+
self.dict[M] = coeff
|
|
20
|
+
else:
|
|
21
|
+
for k,coeff in P.items():
|
|
22
|
+
self.dict[k] = coeff
|
|
23
|
+
elif isinstance(P, sp.Expr):
|
|
24
|
+
self.dict = dict(P.as_coefficients_dict(IM))
|
|
25
|
+
elif P == 0:
|
|
26
|
+
self.dict = {}
|
|
27
|
+
else:
|
|
28
|
+
raise SyntaxError
|
|
29
|
+
|
|
30
|
+
def get_sympy_repr(self):
|
|
31
|
+
L = self.dict.items()
|
|
32
|
+
sympy_repr = sp.Add(*[coeff*M for M,coeff in L])
|
|
33
|
+
return sympy_repr
|
|
34
|
+
|
|
35
|
+
def add_alpha_M(self, alpha, M):
|
|
36
|
+
if M in self.dict:
|
|
37
|
+
new_coeff = sp.cancel(alpha + self.dict[M])
|
|
38
|
+
if new_coeff !=0:
|
|
39
|
+
self.dict[M] = new_coeff
|
|
40
|
+
else:
|
|
41
|
+
del self.dict[M]
|
|
42
|
+
else:
|
|
43
|
+
self.dict[M] = alpha
|
|
44
|
+
|
|
45
|
+
def is_zero(self):
|
|
46
|
+
return len(self.dict) ==0
|
|
47
|
+
|
|
48
|
+
def __repr__(self):
|
|
49
|
+
return f"IntegralPolynomial({self.get_sympy_repr()})"
|
|
50
|
+
|
|
51
|
+
def repr_display_math(self):
|
|
52
|
+
return '{}'.format(printing.latex(self.get_sympy_repr()))
|
|
53
|
+
|
|
54
|
+
def _repr_latex_(self):
|
|
55
|
+
return '${}$'.format(printing.latex(self.get_sympy_repr()))
|
|
56
|
+
|
|
57
|
+
def copy(self):
|
|
58
|
+
# d = copy.deepcopy(self.dict)
|
|
59
|
+
d = self.dict
|
|
60
|
+
#avoid sp simplify on coeff when copy
|
|
61
|
+
return IntegralPolynomial(d, copy=True)
|
|
62
|
+
|
|
63
|
+
def cut_P(self, cut_type: str):
|
|
64
|
+
"""
|
|
65
|
+
Definition 8 of Contribution to Integral Elimination
|
|
66
|
+
We simply extend the cut method of the Integral Monomial class to
|
|
67
|
+
integral polynomials
|
|
68
|
+
|
|
69
|
+
Disclaimer: this method will throw an error if you use it on
|
|
70
|
+
polynomial that can't be cutted
|
|
71
|
+
for exemple, trying to cut a pol with monomials of depth < 2 will
|
|
72
|
+
throw an error if you cut using i2+
|
|
73
|
+
"""
|
|
74
|
+
P_cutted = {}
|
|
75
|
+
P = self.dict.items()
|
|
76
|
+
for M,coeff in P:
|
|
77
|
+
M_cutted = M.cut(cut_type)
|
|
78
|
+
if P_cutted.get(M_cutted) is None:
|
|
79
|
+
P_cutted[M_cutted] = coeff
|
|
80
|
+
else:
|
|
81
|
+
P_cutted[M_cutted] += coeff
|
|
82
|
+
return IntegralPolynomial(P_cutted)
|
|
83
|
+
|
|
84
|
+
def get_P_I(self):
|
|
85
|
+
P_I = {}
|
|
86
|
+
P = self.dict.items()
|
|
87
|
+
for M,coeff in P:
|
|
88
|
+
M0 = M.cut("0")
|
|
89
|
+
if M.get_nb_int() >= 1 and M0 == IM(1):
|
|
90
|
+
if M not in P_I:
|
|
91
|
+
P_I[M] = coeff
|
|
92
|
+
else:
|
|
93
|
+
# we are not supposed to have this case
|
|
94
|
+
raise ValueError
|
|
95
|
+
# we avoid the simplification process of the coeffs
|
|
96
|
+
# by using copy=True
|
|
97
|
+
return IntegralPolynomial(P_I, copy=True)
|
|
98
|
+
|
|
99
|
+
def get_P_N(self):
|
|
100
|
+
P_N = {}
|
|
101
|
+
P = self.dict.items()
|
|
102
|
+
for M,coeff in P:
|
|
103
|
+
M0 = M.cut("0")
|
|
104
|
+
if M==IM(1): #cst
|
|
105
|
+
P_N[M] = coeff
|
|
106
|
+
elif M0 != IM(1):
|
|
107
|
+
if M not in P_N:
|
|
108
|
+
P_N[M] = coeff
|
|
109
|
+
else:
|
|
110
|
+
# we are not supposed to have this case
|
|
111
|
+
raise ValueError
|
|
112
|
+
# we avoid the simplification process of the coeffs
|
|
113
|
+
# by using copy=True
|
|
114
|
+
return IntegralPolynomial(P_N, copy=True)
|
|
115
|
+
|
|
116
|
+
def get_cst_terms(self):
|
|
117
|
+
CST = {}
|
|
118
|
+
P = self.dict.items()
|
|
119
|
+
for M,coeff in P:
|
|
120
|
+
if M == IM(1):
|
|
121
|
+
if M in CST:
|
|
122
|
+
CST[M] += coeff
|
|
123
|
+
else:
|
|
124
|
+
CST[M] = coeff
|
|
125
|
+
return IntegralPolynomial(CST)
|
|
126
|
+
|
|
127
|
+
def get_time_dependant_functions(self):
|
|
128
|
+
"""
|
|
129
|
+
example: if you have
|
|
130
|
+
P = IntegralPolynomial(IM(x(t)) - x(0)*IM(1)
|
|
131
|
+
- theta*IM(1,x(t)*y(t)**2) - IM(1,y(t)))
|
|
132
|
+
it will return {x,y}
|
|
133
|
+
"""
|
|
134
|
+
res = set()
|
|
135
|
+
for f in self.get_sympy_repr().atoms(sp.Function):
|
|
136
|
+
if f.func != IM and not is_float(f.args[0]):
|
|
137
|
+
res.add(f)
|
|
138
|
+
return res
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import sympy as sp
|
|
2
|
+
from ordered_set import OrderedSet
|
|
3
|
+
|
|
4
|
+
from .utils import expr_has_symbol
|
|
5
|
+
from .IntegralMonomial import IM
|
|
6
|
+
from .IntegralPolynomial import IntegralPolynomial
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def first_order_ODE_to_IntegralPolynomial(
|
|
10
|
+
expr:sp.Expr,
|
|
11
|
+
var=sp.Symbol("t")
|
|
12
|
+
) -> IntegralPolynomial:
|
|
13
|
+
expr = sp.expand(expr)
|
|
14
|
+
dict_coeff = dict(expr.as_coefficients_dict(var))
|
|
15
|
+
P = IntegralPolynomial(0)
|
|
16
|
+
if expr_has_symbol(expr, sp.Integral): raise ValueError
|
|
17
|
+
expr_has_der = expr_has_symbol(expr,sp.Derivative)
|
|
18
|
+
for mons, coeff in dict_coeff.items():
|
|
19
|
+
if mons.func == sp.Derivative :
|
|
20
|
+
assert mons.args[1][1] == 1
|
|
21
|
+
m = mons.args[0]
|
|
22
|
+
CI = sp.Symbol(f"{m.func}0")
|
|
23
|
+
M = IM(m)
|
|
24
|
+
P.add_alpha_M(coeff, M)
|
|
25
|
+
P.add_alpha_M(-CI,IM(1))
|
|
26
|
+
else:
|
|
27
|
+
if expr_has_der:
|
|
28
|
+
M = IM(1,mons)
|
|
29
|
+
P.add_alpha_M(coeff,M)
|
|
30
|
+
else:
|
|
31
|
+
M = IM(mons)
|
|
32
|
+
P.add_alpha_M(coeff,M)
|
|
33
|
+
return P
|
|
34
|
+
|
|
35
|
+
def ODE_sys_to_Integral_sys(sys: list[sp.Expr]
|
|
36
|
+
) -> OrderedSet[IntegralPolynomial]:
|
|
37
|
+
sys = OrderedSet([first_order_ODE_to_IntegralPolynomial(eq) for eq in sys])
|
|
38
|
+
return sys
|