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

r""" 

Base class for old-style parent objects with generators 

  

.. NOTE:: 

  

This class is being deprecated, see 

``sage.structure.parent.Parent`` and 

``sage.structure.category_object.CategoryObject`` for the new 

model. 

  

Many parent objects in Sage are equipped with generators, which are 

special elements of the object. For example, the polynomial ring 

`\ZZ[x,y,z]` is generated by `x`, `y`, and `z`. In Sage the `i^{th}` 

generator of an object ``X`` is obtained using the notation 

``X.gen(i)``. From the Sage interactive prompt, the shorthand 

notation ``X.i`` is also allowed. 

  

REQUIRED: A class that derives from ParentWithGens *must* define 

the ngens() and gen(i) methods. 

  

OPTIONAL: It is also good if they define gens() to return all gens, 

but this is not necessary. 

  

The ``gens`` function returns a tuple of all generators, the 

``ngens`` function returns the number of generators. 

  

The ``_assign_names`` functions is for internal use only, and is 

called when objects are created to set the generator names. It can 

only be called once. 

  

The following examples illustrate these functions in the context of 

multivariate polynomial rings and free modules. 

  

EXAMPLES:: 

  

sage: R = PolynomialRing(ZZ, 3, 'x') 

sage: R.ngens() 

3 

sage: R.gen(0) 

x0 

sage: R.gens() 

(x0, x1, x2) 

sage: R.variable_names() 

('x0', 'x1', 'x2') 

  

This example illustrates generators for a free module over `\ZZ`. 

  

:: 

  

sage: M = FreeModule(ZZ, 4) 

sage: M 

Ambient free module of rank 4 over the principal ideal domain Integer Ring 

sage: M.ngens() 

4 

sage: M.gen(0) 

(1, 0, 0, 0) 

sage: M.gens() 

((1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)) 

""" 

  

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

# Copyright (C) 2005, 2006 William Stein <wstein@gmail.com> 

# 

# 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 __future__ import absolute_import, print_function 

  

import sage.misc.defaults 

from sage.misc.latex import latex_variable_name 

from . import gens_py 

cimport sage.structure.parent as parent 

from sage.structure.coerce_dict cimport MonoDict 

cimport sage.structure.category_object as category_object 

  

  

cdef inline check_old_coerce(parent.Parent p): 

if p._element_constructor is not None: 

raise RuntimeError("%s still using old coercion framework" % p) 

  

  

cdef class ParentWithGens(ParentWithBase): 

# Derived class *must* call __init__ and set the base! 

def __init__(self, base, names=None, normalize=True, category = None): 

""" 

EXAMPLES:: 

  

sage: class MyParent(ParentWithGens): 

....: def ngens(self): return 3 

sage: P = MyParent(base = QQ, names = 'a,b,c', normalize = True, category = Groups()) 

sage: P.category() 

Category of groups 

sage: P._names 

('a', 'b', 'c') 

""" 

self._base = base 

self._has_coerce_map_from = MonoDict() 

self._assign_names(names=names, normalize=normalize) 

  

ParentWithBase.__init__(self, base, category=category) 

  

# Derived class *must* define ngens method. 

def ngens(self): 

check_old_coerce(self) 

raise NotImplementedError("Number of generators not known.") 

  

# Derived class *must* define gen method. 

def gen(self, i=0): 

check_old_coerce(self) 

raise NotImplementedError("i-th generator not known.") 

  

def gens(self): 

""" 

Return a tuple whose entries are the generators for this 

object, in order. 

""" 

cdef int i, n 

if self._gens is not None: 

return self._gens 

else: 

v = [] 

n = self.ngens() 

for i from 0 <= i < n: 

v.append(self.gen(i)) 

self._gens = tuple(v) 

return self._gens 

  

def _assign_names(self, names=None, normalize=True): 

""" 

Set the names of the generator of this object. 

  

This can only be done once because objects with generators 

are immutable, and is typically done during creation of the object. 

  

  

EXAMPLES: 

  

When we create this polynomial ring, self._assign_names is called by the constructor: 

  

:: 

  

sage: R = QQ['x,y,abc']; R 

Multivariate Polynomial Ring in x, y, abc over Rational Field 

sage: R.2 

abc 

  

We can't rename the variables:: 

  

sage: R._assign_names(['a','b','c']) 

Traceback (most recent call last): 

... 

ValueError: variable names cannot be changed after object creation. 

""" 

if self._element_constructor is not None: 

return parent.Parent._assign_names(self, names=names, normalize=normalize) 

if names is None: return 

if normalize: 

names = category_object.normalize_names(self.ngens(), names) 

if self._names is not None and names != self._names: 

raise ValueError('variable names cannot be changed after object creation.') 

if isinstance(names, str): 

names = (names, ) # make it a tuple 

elif not isinstance(names, tuple): 

raise TypeError("names must be a tuple of strings") 

self._names = names 

  

################################################################################# 

# Give all objects with generators a dictionary, so that attribute setting 

# works. It would be nice if this functionality were standard in Pyrex, 

# i.e., just define __dict__ as an attribute and all this code gets generated. 

################################################################################# 

def __getstate__(self): 

if self._element_constructor is not None: 

return parent.Parent.__getstate__(self) 

d = [] 

try: 

d = list(self.__dict__.copy().iteritems()) # so we can add elements 

except AttributeError: 

pass 

d = dict(d) 

d['_base'] = self._base 

d['_gens'] = self._gens 

d['_list'] = self._list 

d['_names'] = self._names 

d['_latex_names'] = self._latex_names 

  

return d 

  

def __setstate__(self, d): 

if '_element_constructor' in d: 

return parent.Parent.__setstate__(self, d) 

try: 

self.__dict__.update(d) 

except (AttributeError,KeyError): 

pass 

self._base = d['_base'] 

self._gens = d['_gens'] 

self._list = d['_list'] 

self._names = d['_names'] 

self._latex_names = d['_latex_names'] 

  

  

################################################################################# 

# Morphisms of objects with generators 

################################################################################# 

  

def hom(self, im_gens, codomain=None, check=True): 

r""" 

Return the unique homomorphism from self to codomain that 

sends ``self.gens()`` to the entries of ``im_gens``. 

Raises a TypeError if there is no such homomorphism. 

  

INPUT: 

  

- ``im_gens`` - the images in the codomain of the generators of 

this object under the homomorphism 

  

- ``codomain`` - the codomain of the homomorphism 

  

- ``check`` - whether to verify that the images of generators extend 

to define a map (using only canonical coercions). 

  

OUTPUT: 

  

- a homomorphism self --> codomain 

  

.. NOTE:: 

  

As a shortcut, one can also give an object X instead of 

``im_gens``, in which case return the (if it exists) 

natural map to X. 

  

EXAMPLES: Polynomial Ring 

We first illustrate construction of a few homomorphisms 

involving a polynomial ring. 

  

:: 

  

sage: R.<x> = PolynomialRing(ZZ) 

sage: f = R.hom([5], QQ) 

sage: f(x^2 - 19) 

6 

  

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

sage: f = R.hom([5], GF(7)) 

Traceback (most recent call last): 

... 

ValueError: relations do not all (canonically) map to 0 under map determined by images of generators 

  

sage: R.<x> = PolynomialRing(GF(7)) 

sage: f = R.hom([3], GF(49,'a')) 

sage: f 

Ring morphism: 

From: Univariate Polynomial Ring in x over Finite Field of size 7 

To: Finite Field in a of size 7^2 

Defn: x |--> 3 

sage: f(x+6) 

2 

sage: f(x^2+1) 

3 

  

EXAMPLES: Natural morphism 

  

:: 

  

sage: f = ZZ.hom(GF(5)) 

sage: f(7) 

2 

sage: f 

Natural morphism: 

From: Integer Ring 

To: Finite Field of size 5 

  

There might not be a natural morphism, in which case a TypeError exception is raised. 

  

:: 

  

sage: QQ.hom(ZZ) 

Traceback (most recent call last): 

... 

TypeError: natural coercion morphism from Rational Field to Integer Ring not defined 

""" 

if self._element_constructor is not None: 

return parent.Parent.hom(self, im_gens, codomain, check) 

if isinstance(im_gens, parent.Parent): 

return self.Hom(im_gens).natural_map() 

if codomain is None: 

from sage.structure.all import Sequence 

im_gens = Sequence(im_gens) 

codomain = im_gens.universe() 

return self.Hom(codomain)(im_gens, check=check) 

  

  

cdef class localvars: 

r""" 

Context manager for safely temporarily changing the variables 

names of an object with generators. 

  

Objects with named generators are globally unique in Sage. 

Sometimes, though, it is very useful to be able to temporarily 

display the generators differently. The new Python ``with`` 

statement and the localvars context manager make this easy and 

safe (and fun!) 

  

Suppose X is any object with generators. Write 

  

:: 

  

with localvars(X, names[, latex_names] [,normalize=False]): 

some code 

... 

  

and the indented code will be run as if the names in X are changed 

to the new names. If you give normalize=True, then the names are 

assumed to be a tuple of the correct number of strings. 

  

EXAMPLES:: 

  

sage: R.<x,y> = PolynomialRing(QQ,2) 

sage: with localvars(R, 'z,w'): 

....: print(x^3 + y^3 - x*y) 

z^3 + w^3 - z*w 

  

.. NOTE:: 

  

I wrote this because it was needed to print elements of the 

quotient of a ring R by an ideal I using the print function for 

elements of R. See the code in 

``quotient_ring_element.pyx``. 

  

AUTHOR: 

  

- William Stein (2006-10-31) 

""" 

cdef object _obj 

cdef object _names 

cdef object _latex_names 

cdef object _orig 

  

def __init__(self, obj, names, latex_names=None, normalize=True): 

self._obj = obj 

if normalize: 

self._names = category_object.normalize_names(obj.ngens(), names) 

self._latex_names = latex_names 

else: 

self._names = names 

self._latex_names = latex_names 

  

def __enter__(self): 

self._orig = self._obj.__temporarily_change_names(self._names, self._latex_names) 

  

def __exit__(self, type, value, traceback): 

self._obj.__temporarily_change_names(self._orig[0], self._orig[1])