@@ -177,6 +177,30 @@ class constraints:
177
177
def __repr__ (self ):
178
178
return "%s" % self
179
179
180
+ def normalize_factor (p ):
181
+ """Normalizes the sign of primitive polynomials (as returned by factor())
182
+
183
+ This function ensures that the polynomial has a positive leading coefficient.
184
+
185
+ This is necessary because recent sage versions (starting with v9.3 or v9.4,
186
+ we don't know) are inconsistent about the placement of the minus sign in
187
+ polynomial factorizations:
188
+ ```
189
+ sage: R.<ax,bx,ay,by,Az,Bz,Ai,Bi> = PolynomialRing(QQ,8,order='invlex')
190
+ sage: R((-2 * (bx - ax)) ^ 1).factor()
191
+ (-2) * (bx - ax)
192
+ sage: R((-2 * (bx - ax)) ^ 2).factor()
193
+ (4) * (-bx + ax)^2
194
+ sage: R((-2 * (bx - ax)) ^ 3).factor()
195
+ (8) * (-bx + ax)^3
196
+ ```
197
+ """
198
+ # Assert p is not 0 and that its non-zero coeffients are coprime.
199
+ # (We could just work with the primitive part p/p.content() but we want to be
200
+ # aware if factor() does not return a primitive part in future sage versions.)
201
+ assert p .content () == 1
202
+ # Ensure that the first non-zero coefficient is positive.
203
+ return p if p .lc () > 0 else - p
180
204
181
205
def conflicts (R , con ):
182
206
"""Check whether any of the passed non-zero assumptions is implied by the zero assumptions"""
@@ -204,10 +228,10 @@ def get_nonzero_set(R, assume):
204
228
nonzero = set ()
205
229
for nz in map (numerator , assume .nonzero ):
206
230
for (f ,n ) in nz .factor ():
207
- nonzero .add (f )
231
+ nonzero .add (normalize_factor ( f ) )
208
232
rnz = zero .reduce (nz )
209
233
for (f ,n ) in rnz .factor ():
210
- nonzero .add (f )
234
+ nonzero .add (normalize_factor ( f ) )
211
235
return nonzero
212
236
213
237
@@ -222,27 +246,27 @@ def prove_nonzero(R, exprs, assume):
222
246
return (False , [exprs [expr ]])
223
247
allexprs = reduce (lambda a ,b : numerator (a )* numerator (b ), exprs , 1 )
224
248
for (f , n ) in allexprs .factor ():
225
- if f not in nonzero :
249
+ if normalize_factor ( f ) not in nonzero :
226
250
ok = False
227
251
if ok :
228
252
return (True , None )
229
253
ok = True
230
254
for (f , n ) in zero .reduce (allexprs ).factor ():
231
- if f not in nonzero :
255
+ if normalize_factor ( f ) not in nonzero :
232
256
ok = False
233
257
if ok :
234
258
return (True , None )
235
259
ok = True
236
260
for expr in exprs :
237
261
for (f ,n ) in numerator (expr ).factor ():
238
- if f not in nonzero :
262
+ if normalize_factor ( f ) not in nonzero :
239
263
ok = False
240
264
if ok :
241
265
return (True , None )
242
266
ok = True
243
267
for expr in exprs :
244
268
for (f ,n ) in zero .reduce (numerator (expr )).factor ():
245
- if f not in nonzero :
269
+ if normalize_factor ( f ) not in nonzero :
246
270
expl .add (exprs [expr ])
247
271
if expl :
248
272
return (False , list (expl ))
@@ -279,17 +303,17 @@ def describe_extra(R, assume, assumeExtra):
279
303
if base not in zero :
280
304
add = []
281
305
for (f , n ) in numerator (base ).factor ():
282
- if f not in nonzero :
283
- add += ["%s" % f ]
306
+ if normalize_factor ( f ) not in nonzero :
307
+ add += ["%s" % normalize_factor ( f ) ]
284
308
if add :
285
309
ret .add ((" * " .join (add )) + " = 0 [%s]" % assumeExtra .zero [base ])
286
310
# Iterate over the extra nonzero expressions
287
311
for nz in assumeExtra .nonzero :
288
312
nzr = zeroextra .reduce (numerator (nz ))
289
313
if nzr not in zeroextra :
290
314
for (f ,n ) in nzr .factor ():
291
- if zeroextra .reduce (f ) not in nonzero :
292
- ret .add ("%s != 0" % zeroextra .reduce (f ))
315
+ if normalize_factor ( zeroextra .reduce (f ) ) not in nonzero :
316
+ ret .add ("%s != 0" % normalize_factor ( zeroextra .reduce (f ) ))
293
317
return ", " .join (x for x in ret )
294
318
295
319
0 commit comments