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

""" 

Dense Matrices over a general ring 

""" 

from __future__ import absolute_import 

  

cimport cython 

from cpython.list cimport * 

from cpython.number cimport * 

from cpython.ref cimport * 

  

cimport sage.matrix.matrix_dense as matrix_dense 

from . import matrix_dense 

  

cimport sage.matrix.matrix as matrix 

  

from sage.structure.element cimport parent as parent_c 

  

cdef class Matrix_generic_dense(matrix_dense.Matrix_dense): 

r""" 

The ``Matrix_generic_dense`` class derives from 

``Matrix``, and defines functionality for dense 

matrices over any base ring. Matrices are represented by a list of 

elements in the base ring, and element access operations are 

implemented in this class. 

  

EXAMPLES:: 

  

sage: A = random_matrix(Integers(25)['x'],2); A 

[ x^2 + 12*x + 2 4*x^2 + 13*x + 8] 

[ 22*x^2 + 2*x + 17 19*x^2 + 22*x + 14] 

sage: type(A) 

<type 'sage.matrix.matrix_generic_dense.Matrix_generic_dense'> 

sage: TestSuite(A).run() 

  

Test comparisons:: 

  

sage: A = random_matrix(Integers(25)['x'],2) 

sage: A == A 

True 

sage: A < A + 1 

True 

sage: A+1 < A 

False 

  

Test hashing:: 

  

sage: A = random_matrix(Integers(25)['x'], 2) 

sage: hash(A) 

Traceback (most recent call last): 

... 

TypeError: mutable matrices are unhashable 

sage: A.set_immutable() 

sage: hash(A) 

6226886770042072326 # 64-bit 

-1594888954 # 32-bit 

""" 

def __init__(self, parent, entries, copy, coerce): 

r""" 

See :class:`Matrix_generic_dense` for documentation. 

  

TESTS: 

  

We check that the problem related to :trac:`9049` is not an issue any 

more:: 

  

sage: S.<t>=PolynomialRing(QQ) 

sage: F.<q>=QQ.extension(t^4+1) 

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

sage: M = MatrixSpace(R, 1, 2) 

sage: from sage.matrix.matrix_generic_dense import Matrix_generic_dense 

sage: Matrix_generic_dense(M, (x, y), True, True) 

[x y] 

""" 

matrix.Matrix.__init__(self, parent) 

  

cdef Py_ssize_t i,j 

cdef bint is_list 

  

R = parent.base_ring() 

zero = R.zero() 

  

# determine if entries is a list or a scalar 

if entries is None: 

entries = zero 

is_list = False 

elif parent_c(entries) is R: 

is_list = False 

elif type(entries) is list: 

# here we do a strong type checking as we potentially want to 

# assign entries to self._entries without copying it 

self._entries = entries 

is_list = True 

elif isinstance(entries, (list,tuple)): 

# it is needed to check for list here as for example Sequence 

# inherits from it but fails the strong type checking above 

self._entries = list(entries) 

is_list = True 

copy = False 

else: 

# not sure what entries is at this point... try scalar first 

try: 

entries = R(entries) 

is_list = False 

except TypeError: 

try: 

self._entries = list(entries) 

is_list = True 

copy = False 

except TypeError: 

raise TypeError("entries must be coercible to a list or the base ring") 

  

# now set self._entries 

if is_list: 

if len(self._entries) != self._nrows * self._ncols: 

raise TypeError("entries has the wrong length") 

if coerce: 

self._entries = [R(x) for x in self._entries] 

elif copy: 

self._entries = self._entries[:] 

elif self._nrows == self._ncols: 

self._entries = [zero]*(self._nrows*self._nrows) 

for i in range(self._nrows): 

self._entries[i+self._ncols*i]=entries 

elif entries == zero: 

self._entries = [zero]*(self._nrows*self._ncols) 

else: 

raise TypeError("nonzero scalar matrix must be square") 

  

cdef Matrix_generic_dense _new(self, Py_ssize_t nrows, Py_ssize_t ncols): 

r""" 

Return a new dense matrix with no entries set. 

""" 

cdef Matrix_generic_dense res 

res = self.__class__.__new__(self.__class__, 0, 0, 0) 

  

if nrows == self._nrows and ncols == self._ncols: 

res._parent = self._parent 

else: 

res._parent = self.matrix_space(nrows, ncols) 

res._ncols = ncols 

res._nrows = nrows 

res._base_ring = self._base_ring 

return res 

  

cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, value): 

self._entries[i*self._ncols + j] = value 

  

cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j): 

return self._entries[i*self._ncols + j] 

  

  

def _reverse_unsafe(self): 

r""" 

TESTS:: 

  

sage: m = matrix(ZZ['x,y'], 2, 3, range(6)) 

sage: m._reverse_unsafe() 

sage: m 

[5 4 3] 

[2 1 0] 

""" 

self._entries.reverse() 

  

def _pickle(self): 

""" 

EXAMPLES: 

sage: R.<x> = Integers(25)['x']; A = matrix(R, [1,x,x^3+1,2*x]) 

sage: A._pickle() 

([1, x, x^3 + 1, 2*x], 0) 

""" 

return self._entries, 0 

  

def _unpickle(self, data, int version): 

""" 

EXAMPLES: 

sage: R.<x> = Integers(25)['x']; A = matrix(R, [1,x,x^3+1,2*x]); B = A.parent()(0) 

sage: v = A._pickle() 

sage: B._unpickle(v[0], v[1]) 

sage: B 

[ 1 x x^3 + 1 2*x] 

""" 

if version == 0: 

self._entries = data 

else: 

raise RuntimeError("unknown matrix version") 

  

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

# LEVEL 2 functionality 

# X * cdef _add_ 

# * cdef _mul_ 

# * cpdef _cmp_ 

# * __neg__ 

# * __invert__ 

# x * __copy__ 

# x * _multiply_classical 

# x * _list -- copy of the list of underlying elements 

# * _dict -- copy of the sparse dictionary of underlying elements 

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

  

def __copy__(self): 

""" 

Creates a copy of self, which may be changed without altering 

self. 

  

EXAMPLES:: 

  

sage: A = matrix(ZZ[['t']], 2,3,range(6)); A 

[0 1 2] 

[3 4 5] 

sage: A.subdivide(1,1); A 

[0|1 2] 

[-+---] 

[3|4 5] 

sage: B = A.__copy__(); B 

[0|1 2] 

[-+---] 

[3|4 5] 

sage: B == A 

True 

sage: B[0,0] = 100 

sage: B 

[100| 1 2] 

[---+-------] 

[ 3| 4 5] 

sage: A 

[0|1 2] 

[-+---] 

[3|4 5] 

sage: R.<x> = QQ['x'] 

sage: a = matrix(R,2,[x+1,2/3, x^2/2, 1+x^3]); a 

[ x + 1 2/3] 

[1/2*x^2 x^3 + 1] 

sage: b = copy(a) 

sage: b[0,0] = 5 

sage: b 

[ 5 2/3] 

[1/2*x^2 x^3 + 1] 

sage: a 

[ x + 1 2/3] 

[1/2*x^2 x^3 + 1] 

  

:: 

  

sage: b = copy(a) 

sage: f = b[0,0]; f[0] = 10 

Traceback (most recent call last): 

... 

IndexError: polynomials are immutable 

""" 

cdef Matrix_generic_dense A 

A = self._new(self._nrows, self._ncols) 

A._entries = self._entries[:] 

if self._subdivisions is not None: 

A.subdivide(*self.subdivisions()) 

return A 

  

@cython.boundscheck(False) 

@cython.wraparound(False) 

cpdef _add_(self, right): 

""" 

Add two generic dense matrices with the same parent. 

  

EXAMPLES:: 

  

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

sage: a = matrix(R, 2, 2, [1,2,x*y,y*x]) 

sage: b = matrix(R, 2, 2, [1,2,y*x,y*x]) 

sage: a._add_(b) 

[ 2 4] 

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

""" 

cdef Py_ssize_t k 

cdef Matrix_generic_dense other = <Matrix_generic_dense> right 

cdef Matrix_generic_dense res = self._new(self._nrows, self._ncols) 

res._entries = [None]*(self._nrows*self._ncols) 

for k in range(self._nrows*self._ncols): 

res._entries[k] = self._entries[k] + other._entries[k] 

return res 

  

@cython.boundscheck(False) 

@cython.wraparound(False) 

cpdef _sub_(self, right): 

""" 

Subtract two generic dense matrices with the same parent. 

  

EXAMPLES:: 

  

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

sage: a = matrix(R, 2, 2, [1,2,x*y,y*x]) 

sage: b = matrix(R, 2, 2, [1,2,y*x,y*x]) 

sage: a._sub_(b) 

[ 0 0] 

[x*y - y*x 0] 

""" 

cdef Py_ssize_t k 

cdef Matrix_generic_dense other = <Matrix_generic_dense> right 

cdef Matrix_generic_dense res = self._new(self._nrows, self._ncols) 

res._entries = [None]*(self._nrows*self._ncols) 

for k in range(self._nrows*self._ncols): 

res._entries[k] = self._entries[k] - other._entries[k] 

return res 

  

@cython.boundscheck(False) 

@cython.wraparound(False) 

@cython.overflowcheck(False) 

def _multiply_classical(left, matrix.Matrix _right): 

""" 

Multiply the matrices left and right using the classical 

`O(n^3)` algorithm. 

  

EXAMPLES: 

  

We multiply two matrices over a fairly general ring:: 

  

sage: R.<x,y> = Integers(8)['x,y'] 

sage: a = matrix(R,2,[x,y,x^2,y^2]); a 

[ x y] 

[x^2 y^2] 

sage: type(a) 

<type 'sage.matrix.matrix_generic_dense.Matrix_generic_dense'> 

sage: a*a 

[ x^2*y + x^2 y^3 + x*y] 

[x^2*y^2 + x^3 y^4 + x^2*y] 

sage: a.det()^2 == (a*a).det() 

True 

sage: a._multiply_classical(a) 

[ x^2*y + x^2 y^3 + x*y] 

[x^2*y^2 + x^3 y^4 + x^2*y] 

  

sage: A = matrix(QQ['x,y'], 2, [0,-1,2,-2]) 

sage: B = matrix(QQ['x,y'], 2, [-1,-1,-2,-2]) 

sage: A*B 

[2 2] 

[2 2] 

  

Sage fully supports degenerate matrices with 0 rows or 0 columns:: 

  

sage: A = matrix(QQ['x,y'], 0, 4, []); A 

[] 

sage: B = matrix(QQ['x,y'], 4,0, []); B 

[] 

sage: A*B 

[] 

sage: B*A 

[0 0 0 0] 

[0 0 0 0] 

[0 0 0 0] 

[0 0 0 0] 

""" 

cdef Py_ssize_t i, j, k, m, nr, nc, snc, p 

cdef Matrix_generic_dense right = _right 

  

if left._ncols != right._nrows: 

raise IndexError("Number of columns of left must equal number of rows of other.") 

  

nr = left._nrows 

nc = right._ncols 

snc = left._ncols 

  

R = left.base_ring() 

cdef list v = [None] * (left._nrows * right._ncols) 

zero = R.zero() 

p = 0 

for i in range(nr): 

for j in range(nc): 

z = zero 

m = i*snc 

for k in range(snc): 

z += left._entries[m+k]._mul_(right._entries[k*nc+j]) 

v[p] = z 

p += 1 

  

cdef Matrix_generic_dense A = left._new(nr, nc) 

A._entries = v 

return A 

  

def _list(self): 

""" 

Return reference to list of entries of self. For internal use 

only, since this circumvents immutability. 

  

EXAMPLES:: 

  

sage: A = random_matrix(Integers(25)['x'],2); A.set_immutable() 

sage: A._list()[0] = 0 

sage: A._list()[0] 

0 

""" 

return self._entries 

  

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

# LEVEL 3 functionality (Optional) 

# * cdef _sub_ 

# * __deepcopy__ 

# * __invert__ 

# * _multiply_classical 

# * Matrix windows -- only if you need strassen for that base 

# * Other functions (list them here): 

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