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

"""Computations with ideals of polynomial rings.""" 

 

from __future__ import print_function, division 

 

from sympy.polys.polyerrors import CoercionFailed 

 

from sympy.core.compatibility import reduce 

 

 

class Ideal(object): 

""" 

Abstract base class for ideals. 

 

Do not instantiate - use explicit constructors in the ring class instead: 

 

>>> from sympy import QQ 

>>> from sympy.abc import x 

>>> QQ.old_poly_ring(x).ideal(x+1) 

<x + 1> 

 

Attributes 

 

- ring - the ring this ideal belongs to 

 

Non-implemented methods: 

 

- _contains_elem 

- _contains_ideal 

- _quotient 

- _intersect 

- _union 

- _product 

- is_whole_ring 

- is_zero 

- is_prime, is_maximal, is_primary, is_radical 

- is_principal 

- height, depth 

- radical 

 

Methods that likely should be overridden in subclasses: 

 

- reduce_element 

""" 

 

def _contains_elem(self, x): 

"""Implementation of element containment.""" 

raise NotImplementedError 

 

def _contains_ideal(self, I): 

"""Implementation of ideal containment.""" 

raise NotImplementedError 

 

def _quotient(self, J): 

"""Implementation of ideal quotient.""" 

raise NotImplementedError 

 

def _intersect(self, J): 

"""Implementation of ideal intersection.""" 

raise NotImplementedError 

 

def is_whole_ring(self): 

"""Return True if ``self`` is the whole ring.""" 

raise NotImplementedError 

 

def is_zero(self): 

"""Return True if ``self`` is the zero ideal.""" 

raise NotImplementedError 

 

def _equals(self, J): 

"""Implementation of ideal equality.""" 

return self._contains_ideal(J) and J._contains_ideal(self) 

 

def is_prime(self): 

"""Return True if ``self`` is a prime ideal.""" 

raise NotImplementedError 

 

def is_maximal(self): 

"""Return True if ``self`` is a maximal ideal.""" 

raise NotImplementedError 

 

def is_radical(self): 

"""Return True if ``self`` is a radical ideal.""" 

raise NotImplementedError 

 

def is_primary(self): 

"""Return True if ``self`` is a primary ideal.""" 

raise NotImplementedError 

 

def is_principal(self): 

"""Return True if ``self`` is a principal ideal.""" 

raise NotImplementedError 

 

def radical(self): 

"""Compute the radical of ``self``.""" 

raise NotImplementedError 

 

def depth(self): 

"""Compute the depth of ``self``.""" 

raise NotImplementedError 

 

def height(self): 

"""Compute the height of ``self``.""" 

raise NotImplementedError 

 

# TODO more 

 

# non-implemented methods end here 

 

def __init__(self, ring): 

self.ring = ring 

 

def _check_ideal(self, J): 

"""Helper to check ``J`` is an ideal of our ring.""" 

if not isinstance(J, Ideal) or J.ring != self.ring: 

raise ValueError( 

'J must be an ideal of %s, got %s' % (self.ring, J)) 

 

def contains(self, elem): 

""" 

Return True if ``elem`` is an element of this ideal. 

 

>>> from sympy.abc import x 

>>> from sympy import QQ 

>>> QQ.old_poly_ring(x).ideal(x+1, x-1).contains(3) 

True 

>>> QQ.old_poly_ring(x).ideal(x**2, x**3).contains(x) 

False 

""" 

return self._contains_elem(self.ring.convert(elem)) 

 

def subset(self, other): 

""" 

Returns True if ``other`` is is a subset of ``self``. 

 

Here ``other`` may be an ideal. 

 

>>> from sympy.abc import x 

>>> from sympy import QQ 

>>> I = QQ.old_poly_ring(x).ideal(x+1) 

>>> I.subset([x**2 - 1, x**2 + 2*x + 1]) 

True 

>>> I.subset([x**2 + 1, x + 1]) 

False 

>>> I.subset(QQ.old_poly_ring(x).ideal(x**2 - 1)) 

True 

""" 

if isinstance(other, Ideal): 

return self._contains_ideal(other) 

return all(self._contains_elem(x) for x in other) 

 

def quotient(self, J, **opts): 

r""" 

Compute the ideal quotient of ``self`` by ``J``. 

 

That is, if ``self`` is the ideal `I`, compute the set 

`I : J = \{x \in R | xJ \subset I \}`. 

 

>>> from sympy.abc import x, y 

>>> from sympy import QQ 

>>> R = QQ.old_poly_ring(x, y) 

>>> R.ideal(x*y).quotient(R.ideal(x)) 

<y> 

""" 

self._check_ideal(J) 

return self._quotient(J, **opts) 

 

def intersect(self, J): 

""" 

Compute the intersection of self with ideal J. 

 

>>> from sympy.abc import x, y 

>>> from sympy import QQ 

>>> R = QQ.old_poly_ring(x, y) 

>>> R.ideal(x).intersect(R.ideal(y)) 

<x*y> 

""" 

self._check_ideal(J) 

return self._intersect(J) 

 

def saturate(self, J): 

r""" 

Compute the ideal saturation of ``self`` by ``J``. 

 

That is, if ``self`` is the ideal `I`, compute the set 

`I : J^\infty = \{x \in R | xJ^n \subset I \text{ for some } n\}`. 

""" 

raise NotImplementedError 

# Note this can be implemented using repeated quotient 

 

def union(self, J): 

""" 

Compute the ideal generated by the union of ``self`` and ``J``. 

 

>>> from sympy.abc import x 

>>> from sympy import QQ 

>>> QQ.old_poly_ring(x).ideal(x**2 - 1).union(QQ.old_poly_ring(x).ideal((x+1)**2)) == QQ.old_poly_ring(x).ideal(x+1) 

True 

""" 

self._check_ideal(J) 

return self._union(J) 

 

def product(self, J): 

""" 

Compute the ideal product of ``self`` and ``J``. 

 

That is, compute the ideal generated by products `xy`, for `x` an element 

of ``self`` and `y \in J`. 

 

>>> from sympy.abc import x, y 

>>> from sympy import QQ 

>>> QQ.old_poly_ring(x, y).ideal(x).product(QQ.old_poly_ring(x, y).ideal(y)) 

<x*y> 

""" 

self._check_ideal(J) 

return self._product(J) 

 

def reduce_element(self, x): 

""" 

Reduce the element ``x`` of our ring modulo the ideal ``self``. 

 

Here "reduce" has no specific meaning: it could return a unique normal 

form, simplify the expression a bit, or just do nothing. 

""" 

return x 

 

def __add__(self, e): 

if not isinstance(e, Ideal): 

R = self.ring.quotient_ring(self) 

if isinstance(e, R.dtype): 

return e 

if isinstance(e, R.ring.dtype): 

return R(e) 

return R.convert(e) 

self._check_ideal(e) 

return self.union(e) 

 

__radd__ = __add__ 

 

def __mul__(self, e): 

if not isinstance(e, Ideal): 

try: 

e = self.ring.ideal(e) 

except CoercionFailed: 

return NotImplemented 

self._check_ideal(e) 

return self.product(e) 

 

__rmul__ = __mul__ 

 

def __pow__(self, exp): 

if exp < 0: 

raise NotImplementedError 

# TODO exponentiate by squaring 

return reduce(lambda x, y: x*y, [self]*exp, self.ring.ideal(1)) 

 

def __eq__(self, e): 

if not isinstance(e, Ideal) or e.ring != self.ring: 

return False 

return self._equals(e) 

 

def __ne__(self, e): 

return not (self == e) 

 

 

class ModuleImplementedIdeal(Ideal): 

""" 

Ideal implementation relying on the modules code. 

 

Attributes: 

 

- _module - the underlying module 

""" 

 

def __init__(self, ring, module): 

Ideal.__init__(self, ring) 

self._module = module 

 

def _contains_elem(self, x): 

return self._module.contains([x]) 

 

def _contains_ideal(self, J): 

if not isinstance(J, ModuleImplementedIdeal): 

raise NotImplementedError 

return self._module.is_submodule(J._module) 

 

def _intersect(self, J): 

if not isinstance(J, ModuleImplementedIdeal): 

raise NotImplementedError 

return self.__class__(self.ring, self._module.intersect(J._module)) 

 

def _quotient(self, J, **opts): 

if not isinstance(J, ModuleImplementedIdeal): 

raise NotImplementedError 

return self._module.module_quotient(J._module, **opts) 

 

def _union(self, J): 

if not isinstance(J, ModuleImplementedIdeal): 

raise NotImplementedError 

return self.__class__(self.ring, self._module.union(J._module)) 

 

@property 

def gens(self): 

""" 

Return generators for ``self``. 

 

>>> from sympy import QQ 

>>> from sympy.abc import x, y 

>>> list(QQ.old_poly_ring(x, y).ideal(x, y, x**2 + y).gens) 

[x, y, x**2 + y] 

""" 

return (x[0] for x in self._module.gens) 

 

def is_zero(self): 

""" 

Return True if ``self`` is the zero ideal. 

 

>>> from sympy.abc import x 

>>> from sympy import QQ 

>>> QQ.old_poly_ring(x).ideal(x).is_zero() 

False 

>>> QQ.old_poly_ring(x).ideal().is_zero() 

True 

""" 

return self._module.is_zero() 

 

def is_whole_ring(self): 

""" 

Return True if ``self`` is the whole ring, i.e. one generator is a unit. 

 

>>> from sympy.abc import x 

>>> from sympy import QQ, ilex 

>>> QQ.old_poly_ring(x).ideal(x).is_whole_ring() 

False 

>>> QQ.old_poly_ring(x).ideal(3).is_whole_ring() 

True 

>>> QQ.old_poly_ring(x, order=ilex).ideal(2 + x).is_whole_ring() 

True 

""" 

return self._module.is_full_module() 

 

def __repr__(self): 

from sympy import sstr 

return '<' + ','.join(sstr(x) for [x] in self._module.gens) + '>' 

 

# NOTE this is the only method using the fact that the module is a SubModule 

def _product(self, J): 

if not isinstance(J, ModuleImplementedIdeal): 

raise NotImplementedError 

return self.__class__(self.ring, self._module.submodule( 

*[[x*y] for [x] in self._module.gens for [y] in J._module.gens])) 

 

def in_terms_of_generators(self, e): 

""" 

Express ``e`` in terms of the generators of ``self``. 

 

>>> from sympy.abc import x 

>>> from sympy import QQ 

>>> I = QQ.old_poly_ring(x).ideal(x**2 + 1, x) 

>>> I.in_terms_of_generators(1) 

[1, -x] 

""" 

return self._module.in_terms_of_generators([e]) 

 

def reduce_element(self, x, **options): 

return self._module.reduce_element([x], **options)[0]