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

r""" 

Wrapper class for abelian groups 

 

This class is intended as a template for anything in Sage that needs the 

functionality of abelian groups. One can create an AdditiveAbelianGroupWrapper 

object from any given set of elements in some given parent, as long as an 

``_add_`` method has been defined. 

 

 

EXAMPLES: 

 

We create a toy example based on the Mordell-Weil group of an elliptic curve over `\QQ`:: 

 

sage: E = EllipticCurve('30a2') 

sage: pts = [E(4,-7,1), E(7/4, -11/8, 1), E(3, -2, 1)] 

sage: M = AdditiveAbelianGroupWrapper(pts[0].parent(), pts, [3, 2, 2]) 

sage: M 

Additive abelian group isomorphic to Z/3 + Z/2 + Z/2 embedded in Abelian 

group of points on Elliptic Curve defined by y^2 + x*y + y = x^3 - 19*x + 26 

over Rational Field 

sage: M.gens() 

((4 : -7 : 1), (7/4 : -11/8 : 1), (3 : -2 : 1)) 

sage: 3*M.0 

(0 : 1 : 0) 

sage: 3000000000000001 * M.0 

(4 : -7 : 1) 

sage: M == loads(dumps(M)) # known bug, see http://trac.sagemath.org/sage_trac/ticket/11599#comment:7 

True 

 

We check that ridiculous operations are being avoided:: 

 

sage: set_verbose(2, 'additive_abelian_wrapper.py') 

sage: 300001 * M.0 

verbose 1 (...: additive_abelian_wrapper.py, _discrete_exp) Calling discrete exp on (1, 0, 0) 

(4 : -7 : 1) 

sage: set_verbose(0, 'additive_abelian_wrapper.py') 

 

 

TODO: 

 

- Implement proper black-box discrete logarithm (using baby-step giant-step). 

The discrete_exp function can also potentially be speeded up substantially 

via caching. 

 

- Think about subgroups and quotients, which probably won't work in the current 

implementation -- some fiddly adjustments will be needed in order to be able 

to pass extra arguments to the subquotient's init method. 

""" 

from __future__ import absolute_import 

 

from . import additive_abelian_group as addgp 

from sage.rings.all import ZZ 

from sage.misc.misc import verbose 

from sage.categories.morphism import Morphism 

from sage.structure.element import parent 

 

class UnwrappingMorphism(Morphism): 

r""" 

The embedding into the ambient group. Used by the coercion framework. 

""" 

def __init__(self, domain): 

r""" 

EXAMPLES:: 

 

sage: G = AdditiveAbelianGroupWrapper(QQbar, [sqrt(QQbar(2)), sqrt(QQbar(3))], [0, 0]) 

sage: F = QQbar.coerce_map_from(G); F 

Generic morphism: 

From: Additive abelian group isomorphic to Z + Z embedded in Algebraic Field 

To: Algebraic Field 

sage: type(F) 

<class 'sage.groups.additive_abelian.additive_abelian_wrapper.UnwrappingMorphism'> 

""" 

Morphism.__init__(self, domain.Hom(domain.universe())) 

 

def _call_(self, x): 

r""" 

TESTS:: 

 

sage: E = EllipticCurve("65a1") 

sage: G = E.torsion_subgroup() 

sage: isinstance(G, sage.groups.additive_abelian.additive_abelian_wrapper.AdditiveAbelianGroupWrapper) 

True 

sage: P1 = E([1,-1,1]) 

sage: P2 = E([0,1,0]) 

sage: P1 in G # indirect doctest 

False 

sage: P2 in G 

True 

sage: (G(P2) + P1) in G 

False 

sage: (G(P2) + P1).parent() 

Abelian group of points on Elliptic Curve defined by y^2 + x*y = x^3 - x over Rational Field 

""" 

return self.codomain()(x.element()) 

 

 

class AdditiveAbelianGroupWrapperElement(addgp.AdditiveAbelianGroupElement): 

""" 

An element of an :class:`AdditiveAbelianGroupWrapper`. 

""" 

 

def __init__(self, parent, vector, element=None, check=False): 

r""" 

EXAMPLES: 

 

sage: from sage.groups.additive_abelian.additive_abelian_wrapper import AdditiveAbelianGroupWrapper 

sage: G = AdditiveAbelianGroupWrapper(QQbar, [sqrt(QQbar(2)), sqrt(QQbar(3))], [0, 0]) 

sage: G.0 # indirect doctest 

1.414213562373095? 

""" 

addgp.AdditiveAbelianGroupElement.__init__(self, parent, vector, check) 

if element is not None: 

element = self.parent().universe()(element) 

self._element = element 

 

def element(self): 

r""" 

Return the underlying object that this element wraps. 

 

EXAMPLES:: 

 

sage: T = EllipticCurve('65a').torsion_subgroup().gen(0) 

sage: T; type(T) 

(0 : 0 : 1) 

<class 'sage.schemes.elliptic_curves.ell_torsion.EllipticCurveTorsionSubgroup_with_category.element_class'> 

sage: T.element(); type(T.element()) 

(0 : 0 : 1) 

<class 'sage.schemes.elliptic_curves.ell_point.EllipticCurvePoint_number_field'> 

""" 

if self._element is None: 

self._element = self.parent()._discrete_exp(self._hermite_lift()) 

return self._element 

 

def _repr_(self): 

r""" 

String representation of self. 

 

EXAMPLES:: 

 

sage: T = EllipticCurve('65a').torsion_subgroup().gen(0) 

sage: repr(T) # indirect doctest 

'(0 : 0 : 1)' 

""" 

return repr(self.element()) 

 

 

class AdditiveAbelianGroupWrapper(addgp.AdditiveAbelianGroup_fixed_gens): 

""" 

The parent of :class:`AdditiveAbelianGroupWrapperElement` 

""" 

 

Element = AdditiveAbelianGroupWrapperElement 

 

def __init__(self, universe, gens, invariants): 

r""" 

EXAMPLES:: 

 

sage: AdditiveAbelianGroupWrapper(QQbar, [sqrt(QQbar(2)), sqrt(QQbar(3))], [0, 0]) # indirect doctest 

Additive abelian group isomorphic to Z + Z embedded in Algebraic Field 

""" 

self._universe = universe 

self._gen_elements = tuple(universe(x) for x in gens) 

self._gen_orders = invariants 

cover,rels = addgp.cover_and_relations_from_invariants(invariants) 

addgp.AdditiveAbelianGroup_fixed_gens.__init__(self, cover, rels, cover.gens()) 

self._unset_coercions_used() 

self.register_embedding(UnwrappingMorphism(self)) 

 

def universe(self): 

r""" 

The ambient group in which this abelian group lives. 

 

EXAMPLES:: 

 

sage: G = AdditiveAbelianGroupWrapper(QQbar, [sqrt(QQbar(2)), sqrt(QQbar(3))], [0, 0]) 

sage: G.universe() 

Algebraic Field 

""" 

return self._universe 

 

def generator_orders(self): 

r""" 

The orders of the generators with which this group was initialised. 

(Note that these are not necessarily a minimal set of generators.) 

Generators of infinite order are returned as 0. Compare 

``self.invariants()``, which returns the orders of a minimal set of 

generators. 

 

EXAMPLES:: 

 

sage: V = Zmod(6)**2 

sage: G = AdditiveAbelianGroupWrapper(V, [2*V.0, 3*V.1], [3, 2]) 

sage: G.generator_orders() 

(3, 2) 

sage: G.invariants() 

(6,) 

""" 

return tuple(self._gen_orders) 

 

def _repr_(self): 

r""" 

EXAMPLES:: 

 

sage: G = AdditiveAbelianGroupWrapper(QQbar, [sqrt(QQbar(2)), sqrt(QQbar(3))], [0, 0]) 

sage: repr(G) # indirect doctest 

'Additive abelian group isomorphic to Z + Z embedded in Algebraic Field' 

""" 

return addgp.AdditiveAbelianGroup_fixed_gens._repr_(self) + " embedded in " + self.universe()._repr_() 

 

def _discrete_exp(self, v): 

r""" 

Given a list (or other iterable) of length equal to the number of 

generators of this group, compute the element of the ambient group with 

those exponents in terms of the generators of self. 

 

EXAMPLES:: 

 

sage: G = AdditiveAbelianGroupWrapper(QQbar, [sqrt(QQbar(2)), -1], [0, 0]) 

sage: v = G._discrete_exp([3, 5]); v 

-0.7573593128807148? 

sage: v.parent() is QQbar 

True 

""" 

from six.moves import range 

v = self.V()(v) 

verbose("Calling discrete exp on %s" % v) 

# DUMB IMPLEMENTATION! 

return sum([self._gen_elements[i] * ZZ(v[i]) for i in range(len(v))], self.universe()(0)) 

 

def _discrete_log(self,x): 

r""" 

Given an element of the ambient group, attempt to express it in terms of the 

generators of self. 

 

EXAMPLES:: 

 

sage: V = Zmod(8)**2; G = AdditiveAbelianGroupWrapper(V, [[2,2],[4,0]], [4, 2]) 

sage: G._discrete_log(V([6, 2])) 

(1, 1) 

sage: G._discrete_log(V([6, 4])) 

Traceback (most recent call last): 

... 

TypeError: Not in group 

sage: G = AdditiveAbelianGroupWrapper(QQbar, [sqrt(2)], [0]) 

sage: G._discrete_log(QQbar(2*sqrt(2))) 

Traceback (most recent call last): 

... 

NotImplementedError: No black-box discrete log for infinite abelian groups 

""" 

# EVEN DUMBER IMPLEMENTATION! 

from sage.rings.infinity import Infinity 

if self.order() == Infinity: 

raise NotImplementedError("No black-box discrete log for infinite abelian groups") 

u = [y for y in self.list() if y.element() == x] 

if len(u) == 0: raise TypeError("Not in group") 

if len(u) > 1: raise NotImplementedError 

return u[0].vector() 

 

def _element_constructor_(self, x, check=False): 

r""" 

Create an element from x. This may be either an element of self, an element of the 

ambient group, or an iterable (in which case the result is the corresponding 

product of the generators of self). 

 

EXAMPLES:: 

 

sage: V = Zmod(8)**2; G = AdditiveAbelianGroupWrapper(V, [[2,2],[4,0]], [4, 2]) 

sage: G(V([6,2])) 

(6, 2) 

sage: G([1,1]) 

doctest:...: DeprecationWarning: The default behaviour changed! 

If you *really* want a linear combination of smith generators, 

use .linear_combination_of_smith_form_gens. 

See http://trac.sagemath.org/16261 for details. 

(6, 2) 

sage: G(G([1,1])) 

(6, 2) 

""" 

if parent(x) is self.universe(): 

return self.element_class(self, self._discrete_log(x), element = x) 

return addgp.AdditiveAbelianGroup_fixed_gens._element_constructor_(self, x, check)