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

"""Tools for constructing domains for expressions. """ 

 

from __future__ import print_function, division 

 

from sympy.polys.polyutils import parallel_dict_from_basic 

from sympy.polys.polyoptions import build_options 

from sympy.polys.polyerrors import GeneratorsNeeded 

from sympy.polys.domains import ZZ, QQ, RR, EX 

from sympy.polys.domains.realfield import RealField 

from sympy.utilities import public 

from sympy.core import sympify 

 

 

def _construct_simple(coeffs, opt): 

"""Handle simple domains, e.g.: ZZ, QQ, RR and algebraic domains. """ 

result, rationals, reals, algebraics = {}, False, False, False 

 

if opt.extension is True: 

is_algebraic = lambda coeff: coeff.is_number and coeff.is_algebraic 

else: 

is_algebraic = lambda coeff: False 

 

# XXX: add support for a + b*I coefficients 

for coeff in coeffs: 

if coeff.is_Rational: 

if not coeff.is_Integer: 

rationals = True 

elif coeff.is_Float: 

if not algebraics: 

reals = True 

else: 

# there are both reals and algebraics -> EX 

return False 

elif is_algebraic(coeff): 

if not reals: 

algebraics = True 

else: 

# there are both algebraics and reals -> EX 

return False 

else: 

# this is a composite domain, e.g. ZZ[X], EX 

return None 

 

if algebraics: 

domain, result = _construct_algebraic(coeffs, opt) 

else: 

if reals: 

# Use the maximum precision of all coefficients for the RR's 

# precision 

max_prec = max([c._prec for c in coeffs]) 

domain = RealField(prec=max_prec) 

else: 

if opt.field or rationals: 

domain = QQ 

else: 

domain = ZZ 

 

result = [] 

 

for coeff in coeffs: 

result.append(domain.from_sympy(coeff)) 

 

return domain, result 

 

 

def _construct_algebraic(coeffs, opt): 

"""We know that coefficients are algebraic so construct the extension. """ 

from sympy.polys.numberfields import primitive_element 

 

result, exts = [], set([]) 

 

for coeff in coeffs: 

if coeff.is_Rational: 

coeff = (None, 0, QQ.from_sympy(coeff)) 

else: 

a = coeff.as_coeff_add()[0] 

coeff -= a 

 

b = coeff.as_coeff_mul()[0] 

coeff /= b 

 

exts.add(coeff) 

 

a = QQ.from_sympy(a) 

b = QQ.from_sympy(b) 

 

coeff = (coeff, b, a) 

 

result.append(coeff) 

 

exts = list(exts) 

 

g, span, H = primitive_element(exts, ex=True, polys=True) 

root = sum([ s*ext for s, ext in zip(span, exts) ]) 

 

domain, g = QQ.algebraic_field((g, root)), g.rep.rep 

 

for i, (coeff, a, b) in enumerate(result): 

if coeff is not None: 

coeff = a*domain.dtype.from_list(H[exts.index(coeff)], g, QQ) + b 

else: 

coeff = domain.dtype.from_list([b], g, QQ) 

 

result[i] = coeff 

 

return domain, result 

 

 

def _construct_composite(coeffs, opt): 

"""Handle composite domains, e.g.: ZZ[X], QQ[X], ZZ(X), QQ(X). """ 

numers, denoms = [], [] 

 

for coeff in coeffs: 

numer, denom = coeff.as_numer_denom() 

 

numers.append(numer) 

denoms.append(denom) 

 

try: 

polys, gens = parallel_dict_from_basic(numers + denoms) # XXX: sorting 

except GeneratorsNeeded: 

return None 

 

if opt.composite is None: 

if any(gen.is_number for gen in gens): 

return None # generators are number-like so lets better use EX 

 

all_symbols = set([]) 

 

for gen in gens: 

symbols = gen.free_symbols 

 

if all_symbols & symbols: 

return None # there could be algebraic relations between generators 

else: 

all_symbols |= symbols 

 

n = len(gens) 

k = len(polys)//2 

 

numers = polys[:k] 

denoms = polys[k:] 

 

if opt.field: 

fractions = True 

else: 

fractions, zeros = False, (0,)*n 

 

for denom in denoms: 

if len(denom) > 1 or zeros not in denom: 

fractions = True 

break 

 

coeffs = set([]) 

 

if not fractions: 

for numer, denom in zip(numers, denoms): 

denom = denom[zeros] 

 

for monom, coeff in numer.items(): 

coeff /= denom 

coeffs.add(coeff) 

numer[monom] = coeff 

else: 

for numer, denom in zip(numers, denoms): 

coeffs.update(list(numer.values())) 

coeffs.update(list(denom.values())) 

 

rationals, reals = False, False 

 

for coeff in coeffs: 

if coeff.is_Rational: 

if not coeff.is_Integer: 

rationals = True 

elif coeff.is_Float: 

reals = True 

break 

 

if reals: 

ground = RR 

elif rationals: 

ground = QQ 

else: 

ground = ZZ 

 

result = [] 

 

if not fractions: 

domain = ground.poly_ring(*gens) 

 

for numer in numers: 

for monom, coeff in numer.items(): 

numer[monom] = ground.from_sympy(coeff) 

 

result.append(domain(numer)) 

else: 

domain = ground.frac_field(*gens) 

 

for numer, denom in zip(numers, denoms): 

for monom, coeff in numer.items(): 

numer[monom] = ground.from_sympy(coeff) 

 

for monom, coeff in denom.items(): 

denom[monom] = ground.from_sympy(coeff) 

 

result.append(domain((numer, denom))) 

 

return domain, result 

 

 

def _construct_expression(coeffs, opt): 

"""The last resort case, i.e. use the expression domain. """ 

domain, result = EX, [] 

 

for coeff in coeffs: 

result.append(domain.from_sympy(coeff)) 

 

return domain, result 

 

 

@public 

def construct_domain(obj, **args): 

"""Construct a minimal domain for the list of coefficients. """ 

opt = build_options(args) 

 

if hasattr(obj, '__iter__'): 

if isinstance(obj, dict): 

if not obj: 

monoms, coeffs = [], [] 

else: 

monoms, coeffs = list(zip(*list(obj.items()))) 

else: 

coeffs = obj 

else: 

coeffs = [obj] 

 

coeffs = list(map(sympify, coeffs)) 

result = _construct_simple(coeffs, opt) 

 

if result is not None: 

if result is not False: 

domain, coeffs = result 

else: 

domain, coeffs = _construct_expression(coeffs, opt) 

else: 

if opt.composite is False: 

result = None 

else: 

result = _construct_composite(coeffs, opt) 

 

if result is not None: 

domain, coeffs = result 

else: 

domain, coeffs = _construct_expression(coeffs, opt) 

 

if hasattr(obj, '__iter__'): 

if isinstance(obj, dict): 

return domain, dict(list(zip(monoms, coeffs))) 

else: 

return domain, coeffs 

else: 

return domain, coeffs[0]