Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

406

407

408

409

410

411

412

413

414

415

416

417

418

r""" 

Sage functions to compute minimal models of rational functions 

under the conjugation action of `PGL_2(QQ)`. 

 

AUTHORS: 

 

- Alex Molnar (May 22, 2012) 

 

- Brian Stout, Ben Hutz (Nov 2013): Modified code to use projective 

morphism functionality so that it can be included in Sage. 

 

REFERENCES: [BM2012]_, [Mol2015]_ 

""" 

 

#***************************************************************************** 

# Copyright (C) 2012 Alexander Molnar 

# 

# This program is free software: you can redistribute it and/or modify 

# it under the terms of the GNU General Public License as published by 

# the Free Software Foundation, either version 2 of the License, or 

# (at your option) any later version. 

# http://www.gnu.org/licenses/ 

#***************************************************************************** 

 

from sage.matrix.constructor import matrix 

from sage.rings.finite_rings.integer_mod_ring import Zmod 

from sage.rings.integer_ring import ZZ 

from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing 

from sage.rings.rational_field import QQ 

from sage.schemes.affine.affine_space import AffineSpace 

from sage.arith.all import gcd 

 

 

def bCheck(c, v, p, b): 

r""" 

Compute a lower bound on the value of ``b``. 

 

This value is needed for a transformation 

`A(z) = z*p^k + b` to satisfy `ord_p(Res(\phi^A)) < ord_p(Res(\phi))` for a 

rational map `\phi`. See Theorem 3.3.5 in [Molnar]_. 

 

INPUT: 

 

- ``c`` -- a list of polynomials in `b`. See v for their use 

 

- ``v`` -- a list of rational numbers, where we are considering the inequalities 

`ord_p(c[i]) > v[i]` 

 

- ``p`` -- a prime 

 

- ``b`` -- local variable 

 

OUTPUT: ``bval`` -- Integer, lower bound in Theorem 3.3.5 

 

EXAMPLES:: 

 

sage: R.<b> = PolynomialRing(QQ) 

sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import bCheck 

sage: bCheck(11664*b^2 + 70227*b + 76059, 15/2, 3, b) 

-1 

""" 

val = (v+1).floor() 

deg = c.degree() 

coeffs = c.coefficients(sparse=False) 

lcoeff = coeffs[deg]; coeffs.remove(lcoeff) 

check1 = [(coeffs[i].valuation(p) - lcoeff.valuation(p))/(deg - i) for i in range(0,len(coeffs)) if coeffs[i] != 0] 

check2 = (val - lcoeff.valuation(p))/deg 

check1.append(check2) 

bval = min(check1) 

return (bval).ceil() 

 

 

def scale(c,v,p): 

r""" 

Create scaled integer polynomial with respect to prime ``p``. 

 

Given an integral polynomial ``c``, we can write `c = p^i*c'`, where ``p`` does not 

divide ``c``. Returns ``c'`` and `v - i` where `i` is the smallest valuation of the 

coefficients of `c`. 

 

INPUT: 

 

- ``c`` -- an integer polynomial 

 

- ``v`` -- an integer - the bound on the exponent from blift 

 

- ``p`` -- a prime 

 

OUTPUT: 

 

- boolean -- the new exponent bound is 0 or negative 

 

- the scaled integer polynomial 

 

- an integer the new exponent bound 

 

EXAMPLES:: 

 

sage: R.<b> = PolynomialRing(QQ) 

sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import scale 

sage: scale(24*b^3 + 108*b^2 + 162*b + 81, 1, 3) 

[False, 8*b^3 + 36*b^2 + 54*b + 27, 0] 

""" 

scaleval = min([coeff.valuation(p) for coeff in c.coefficients()]) 

if scaleval > 0: 

c = c/(p**scaleval) 

v = v - scaleval 

if v <= 0: 

flag = False 

else: 

flag = True 

return [flag,c,v] 

 

 

def blift(LF, Li, p, S=None): 

r""" 

Search for a solution to the given list of inequalities. 

 

If found, lift the solution to 

an appropriate valuation. See Lemma 3.3.6 in [Molnar]_ 

 

INPUT: 

 

- ``LF`` -- a list of integer polynomials in one variable (the normalized coefficients) 

 

- ``Li`` -- an integer, the bound on coefficients 

 

- ``p`` -- a prime 

 

OUTPUT: 

 

- boolean -- whether or not the lift is successful 

 

- integer -- the lift 

 

EXAMPLES:: 

 

sage: R.<b> = PolynomialRing(QQ) 

sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import blift 

sage: blift([8*b^3 + 12*b^2 + 6*b + 1, 48*b^2 + 483*b + 117, 72*b + 1341, -24*b^2 + 411*b + 99, -144*b + 1233, -216*b], 2, 3) 

(True, 4) 

""" 

 

P = LF[0].parent() 

#Determine which inequalities are trivial, and scale the rest, so that we only lift 

#as many times as needed. 

keepScaledIneqs = [scale(P(coeff),Li,p) for coeff in LF if coeff != 0] 

keptVals = [i[2] for i in keepScaledIneqs if i[0]] 

if keptVals != []: 

#Determine the valuation to lift until. 

liftval = max(keptVals) 

else: 

#All inequalities are satisfied. 

return True,1 

if S is None: 

S = PolynomialRing(Zmod(p),'b') 

keptScaledIneqs = [S(i[1]) for i in keepScaledIneqs if i[0]] 

#We need a solution for each polynomial on the left hand side of the inequalities, 

#so we need only find a solution for their gcd. 

g = gcd(keptScaledIneqs) 

rts = g.roots(multiplicities = False) 

for r in rts: 

#Recursively try to lift each root 

r_initial = QQ(r) 

newInput = P([r_initial, p]) 

LG = [F(newInput) for F in LF] 

lift,lifted = blift(LG,Li,p,S=S) 

if lift: 

#Lift successful. 

return True,r_initial + p*lifted 

#Lift non successful. 

return False,0 

 

 

def affine_minimal(vp, return_transformation=False, D=None, quick=False): 

r""" 

Determine if given map is affine minimal. 

 

Given vp a scheme morphisms on the projective line over the rationals, 

this procedure determines if `\phi` is minimal. In particular, it determines 

if the map is affine minimal, which is enough to decide if it is minimal 

or not. See Proposition 2.10 in [Bruin-Molnar]_. 

 

INPUT: 

 

- ``vp`` -- dyanmical system on the projective line 

 

- ``D`` -- a list of primes, in case one only wants to check minimality 

at those specific primes 

 

- ``return_transformation`` -- (default: False) a boolean value, default value True. This 

signals a return of the ``PGL_2`` transformation to conjugate ``vp`` to 

the calculated minimal model 

 

- ``quick`` -- a boolean value. If true the algorithm terminates once 

algorithm determines F/G is not minimal, otherwise algorithm only 

terminates once a minimal model has been found 

 

OUTPUT: 

 

- ``newvp`` -- dynamical system on the projective line 

 

- ``conj`` -- linear fractional transformation which conjugates ``vp`` to ``newvp`` 

 

EXAMPLES:: 

 

sage: PS.<X,Y> = ProjectiveSpace(QQ, 1) 

sage: vp = DynamicalSystem_projective([X^2 + 9*Y^2, X*Y]) 

sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import affine_minimal 

sage: affine_minimal(vp, True) 

( 

Dynamical System of Projective Space of dimension 1 over Rational Field 

Defn: Defined on coordinates by sending (X : Y) to 

(X^2 + Y^2 : X*Y) 

, 

[3 0] 

[0 1] 

) 

""" 

from sage.dynamics.arithmetic_dynamics.affine_ds import DynamicalSystem_affine 

BR = vp.domain().base_ring() 

conj = matrix(BR,2,2,1) 

flag = True 

d = vp.degree() 

 

vp.normalize_coordinates(); 

Affvp = vp.dehomogenize(1) 

R = Affvp.coordinate_ring() 

if R.is_field(): 

#want the polynomial ring not the fraction field 

R = R.ring() 

F = R(Affvp[0].numerator()) 

G = R(Affvp[0].denominator()) 

if R(G.degree()) == 0 or R(F.degree()) == 0: 

raise TypeError("affine minimality is only considered for maps not of the form f or 1/f for a polynomial f") 

z = F.parent().gen(0) 

minF,minG = F,G 

#If the valuation of a prime in the resultant is small enough, we can say the 

#map is affine minimal at that prime without using the local minimality loop. See 

#Theorem 3.2.2 in [Molnar, M.Sc. thesis] 

if d%2 == 0: 

g = d 

else: 

g = 2*d 

Res = vp.resultant(); 

 

#Some quantities needed for the local minimization loop, but we compute now 

#since the value is constant, so we do not wish to compute in every local loop. 

#See Theorem 3.3.3 in [Molnar, M.Sc thesis] 

H = F-z*minG 

d1 = F.degree() 

A = AffineSpace(BR,1,H.parent().variable_name()) 

ubRes = DynamicalSystem_affine([H/minG], domain=A).homogenize(1).resultant() 

#Set the primes to check minimality at, if not already prescribed 

if D is None: 

D = ZZ(Res).prime_divisors() 

 

#Check minimality at all primes in D. If D is all primes dividing 

#Res(minF/minG), this is enough to show whether minF/minG is minimal or not. See 

#Propositions 3.2.1 and 3.3.7 in [Molnar, M.Sc. thesis]. 

for p in D: 

while True: 

if Res.valuation(p) < g: 

#The model is minimal at p 

min = True 

else: 

#The model may not be minimal at p. 

newvp,conj = Min(vp,p,ubRes,conj) 

if newvp == vp: 

min = True 

else: 

vp = newvp 

Affvp = vp.dehomogenize(1) 

min = False 

if min: 

#The model is minimal at p 

break 

elif F == Affvp[0].numerator() and G == Affvp[0].denominator(): 

#The model is minimal at p 

break 

else: 

#The model is not minimal at p 

flag = False 

if quick: 

break 

if quick and not flag: 

break 

 

if quick: #only return whether the model is minimal 

return flag 

 

if return_transformation: 

return vp, conj 

return vp 

 

 

def Min(Fun, p, ubRes, conj): 

r""" 

Local loop for Affine_minimal, where we check minimality at the prime p. 

 

First we bound the possible k in our transformations A = zp^k + b. 

See Theorems 3.3.2 and 3.3.3 in [Molnar]_. 

 

INPUT: 

 

- ``Fun`` -- a dynamical systems on projective space 

 

- ``p`` - a prime 

 

- ``ubRes`` -- integer, the upper bound needed for Th. 3.3.3 in [Molnar]_ 

 

- ``conj`` -- a 2x2 matrix keeping track of the conjugation 

 

OUTPUT: 

 

- boolean -- ``True`` if ``Fun`` is minimal at ``p``, ``False`` otherwise 

 

- a dynamical system on projective space minimal at ``p`` 

 

EXAMPLES:: 

 

sage: P.<x,y> = ProjectiveSpace(QQ, 1) 

sage: f = DynamicalSystem_projective([149*x^2 + 39*x*y + y^2, -8*x^2 + 137*x*y + 33*y^2]) 

sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import Min 

sage: Min(f, 3, -27000000, matrix(QQ,[[1, 0],[0, 1]])) 

( 

Dynamical System of Projective Space of dimension 1 over Rational 

Field 

Defn: Defined on coordinates by sending (x : y) to 

(181*x^2 + 313*x*y + 81*y^2 : -24*x^2 + 73*x*y + 151*y^2) 

, 

[3 4] 

[0 1] 

) 

""" 

d = Fun.degree() 

AffFun = Fun.dehomogenize(1) 

R = AffFun.coordinate_ring() 

if R.is_field(): 

#want the polynomial ring not the fraction field 

R = R.ring() 

F = R(AffFun[0].numerator()) 

G = R(AffFun[0].denominator()) 

dG = G.degree() 

if dG > (d+1)/2: 

lowerBound = (-2*(G[dG]).valuation(p)/(2*dG - d + 1) + 1).floor() 

else: 

lowerBound = (-2*(F[d]).valuation(p)/(d-1) + 1).floor() 

upperBound = 2*(ubRes.valuation(p)) 

 

if upperBound < lowerBound: 

#There are no possible transformations to reduce the resultant. 

return Fun,conj 

else: 

#Looping over each possible k, we search for transformations to reduce the 

#resultant of F/G 

k = lowerBound 

Qb = PolynomialRing(QQ,'b') 

b = Qb.gen(0) 

Q = PolynomialRing(Qb,'z') 

z = Q.gen(0) 

while k <= upperBound: 

A = (p**k)*z + b 

Ft = Q(F(A) - b*G(A)) 

Gt = Q((p**k)*G(A)) 

Fcoeffs = Ft.coefficients(sparse=False) 

Gcoeffs = Gt.coefficients(sparse=False) 

coeffs = Fcoeffs + Gcoeffs 

RHS = (d + 1)*k/2 

#If there is some b such that Res(phi^A) < Res(phi), we must have ord_p(c) > 

#RHS for each c in coeffs. 

#Make sure constant coefficients in coeffs satisfy the inequality. 

if all( QQ(c).valuation(p) > RHS for c in coeffs if c.degree() ==0 ): 

#Constant coefficients in coeffs have large enough valuation, so check 

#the rest. We start by checking if simply picking b=0 works 

if all(c(0).valuation(p) > RHS for c in coeffs): 

#A = z*p^k satisfies the inequalities, and F/G is not minimal 

#"Conjugating by", p,"^", k, "*z +", 0 

newconj = matrix(QQ,2,2,[p**k,0,0,1]) 

minFun = Fun.conjugate(newconj) 

conj = conj*newconj 

minFun.normalize_coordinates() 

return minFun, conj 

 

#Otherwise we search if any value of b will work. We start by finding a 

#minimum bound on the valuation of b that is necessary. See Theorem 3.3.5 

#in [Molnar, M.Sc. thesis]. 

bval = max([bCheck(coeff,RHS,p,b) for coeff in coeffs if coeff.degree() > 0]) 

 

#We scale the coefficients in coeffs, so that we may assume ord_p(b) is 

#at least 0 

scaledCoeffs = [coeff(b*(p**bval)) for coeff in coeffs] 

 

#We now scale the inequalities, ord_p(coeff) > RHS, so that coeff is in 

#ZZ[b] 

scale = QQ(max([coeff.denominator() for coeff in scaledCoeffs])) 

normalizedCoeffs = [coeff*scale for coeff in scaledCoeffs] 

scaleRHS = RHS + scale.valuation(p) 

 

#We now search for integers that satisfy the inequality ord_p(coeff) > 

#RHS. See Lemma 3.3.6 in [Molnar, M.Sc. thesis]. 

bound = (scaleRHS+1).floor() 

bool,sol = blift(normalizedCoeffs,bound,p) 

 

#If bool is true after lifting, we have a solution b, and F/G is not 

#minimal. 

if bool: 

#Rescale, conjugate and return new map 

bsol = QQ(sol*(p**bval)) 

#"Conjugating by ", p,"^", k, "*z +", bsol 

newconj = matrix(QQ,2,2,[p**k,bsol,0,1]) 

minFun = Fun.conjugate(newconj) 

conj = conj*newconj 

 

minFun.normalize_coordinates() 

return minFun, conj 

k = k + 1 

return Fun, conj