github.com/gopherd/gonum@v0.0.4/mat/cdense.go (about) 1 // Copyright ©2019 The Gonum Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package mat 6 7 import ( 8 "math/cmplx" 9 10 "github.com/gopherd/gonum/blas/cblas128" 11 ) 12 13 var ( 14 cDense *CDense 15 16 _ CMatrix = cDense 17 _ allMatrix = cDense 18 ) 19 20 // CDense is a dense matrix representation with complex data. 21 type CDense struct { 22 mat cblas128.General 23 24 capRows, capCols int 25 } 26 27 // Dims returns the number of rows and columns in the matrix. 28 func (m *CDense) Dims() (r, c int) { 29 return m.mat.Rows, m.mat.Cols 30 } 31 32 // Caps returns the number of rows and columns in the backing matrix. 33 func (m *CDense) Caps() (r, c int) { return m.capRows, m.capCols } 34 35 // H performs an implicit conjugate transpose by returning the receiver inside a 36 // ConjTranspose. 37 func (m *CDense) H() CMatrix { 38 return ConjTranspose{m} 39 } 40 41 // T performs an implicit transpose by returning the receiver inside a 42 // CTranspose. 43 func (m *CDense) T() CMatrix { 44 return CTranspose{m} 45 } 46 47 // Conj calculates the element-wise conjugate of a and stores the result in the 48 // receiver. 49 // Conj will panic if m and a do not have the same dimension unless m is empty. 50 func (m *CDense) Conj(a CMatrix) { 51 ar, ac := a.Dims() 52 aU, aTrans, aConj := untransposeExtractCmplx(a) 53 m.reuseAsNonZeroed(ar, ac) 54 55 if arm, ok := a.(*CDense); ok { 56 amat := arm.mat 57 if m != aU { 58 m.checkOverlap(amat) 59 } 60 for ja, jm := 0, 0; ja < ar*amat.Stride; ja, jm = ja+amat.Stride, jm+m.mat.Stride { 61 for i, v := range amat.Data[ja : ja+ac] { 62 m.mat.Data[i+jm] = cmplx.Conj(v) 63 } 64 } 65 return 66 } 67 68 m.checkOverlapMatrix(aU) 69 if aTrans != aConj && m == aU { 70 // Only make workspace if the destination is transposed 71 // with respect to the source and they are the same 72 // matrix. 73 var restore func() 74 m, restore = m.isolatedWorkspace(aU) 75 defer restore() 76 } 77 78 for r := 0; r < ar; r++ { 79 for c := 0; c < ac; c++ { 80 m.set(r, c, cmplx.Conj(a.At(r, c))) 81 } 82 } 83 } 84 85 // Slice returns a new CMatrix that shares backing data with the receiver. 86 // The returned matrix starts at {i,j} of the receiver and extends k-i rows 87 // and l-j columns. The final row in the resulting matrix is k-1 and the 88 // final column is l-1. 89 // Slice panics with ErrIndexOutOfRange if the slice is outside the capacity 90 // of the receiver. 91 func (m *CDense) Slice(i, k, j, l int) CMatrix { 92 return m.slice(i, k, j, l) 93 } 94 95 func (m *CDense) slice(i, k, j, l int) *CDense { 96 mr, mc := m.Caps() 97 if i < 0 || mr <= i || j < 0 || mc <= j || k < i || mr < k || l < j || mc < l { 98 if i == k || j == l { 99 panic(ErrZeroLength) 100 } 101 panic(ErrIndexOutOfRange) 102 } 103 t := *m 104 t.mat.Data = t.mat.Data[i*t.mat.Stride+j : (k-1)*t.mat.Stride+l] 105 t.mat.Rows = k - i 106 t.mat.Cols = l - j 107 t.capRows -= i 108 t.capCols -= j 109 return &t 110 } 111 112 // NewCDense creates a new complex Dense matrix with r rows and c columns. 113 // If data == nil, a new slice is allocated for the backing slice. 114 // If len(data) == r*c, data is used as the backing slice, and changes to the 115 // elements of the returned CDense will be reflected in data. 116 // If neither of these is true, NewCDense will panic. 117 // NewCDense will panic if either r or c is zero. 118 // 119 // The data must be arranged in row-major order, i.e. the (i*c + j)-th 120 // element in the data slice is the {i, j}-th element in the matrix. 121 func NewCDense(r, c int, data []complex128) *CDense { 122 if r <= 0 || c <= 0 { 123 if r == 0 || c == 0 { 124 panic(ErrZeroLength) 125 } 126 panic("mat: negative dimension") 127 } 128 if data != nil && r*c != len(data) { 129 panic(ErrShape) 130 } 131 if data == nil { 132 data = make([]complex128, r*c) 133 } 134 return &CDense{ 135 mat: cblas128.General{ 136 Rows: r, 137 Cols: c, 138 Stride: c, 139 Data: data, 140 }, 141 capRows: r, 142 capCols: c, 143 } 144 } 145 146 // ReuseAs changes the receiver if it IsEmpty() to be of size r×c. 147 // 148 // ReuseAs re-uses the backing data slice if it has sufficient capacity, 149 // otherwise a new slice is allocated. The backing data is zero on return. 150 // 151 // ReuseAs panics if the receiver is not empty, and panics if 152 // the input sizes are less than one. To empty the receiver for re-use, 153 // Reset should be used. 154 func (m *CDense) ReuseAs(r, c int) { 155 if r <= 0 || c <= 0 { 156 if r == 0 || c == 0 { 157 panic(ErrZeroLength) 158 } 159 panic(ErrNegativeDimension) 160 } 161 if !m.IsEmpty() { 162 panic(ErrReuseNonEmpty) 163 } 164 m.reuseAsZeroed(r, c) 165 } 166 167 // reuseAs resizes an empty matrix to a r×c matrix, 168 // or checks that a non-empty matrix is r×c. 169 // 170 // reuseAs must be kept in sync with reuseAsZeroed. 171 func (m *CDense) reuseAsNonZeroed(r, c int) { 172 if m.mat.Rows > m.capRows || m.mat.Cols > m.capCols { 173 // Panic as a string, not a mat.Error. 174 panic(badCap) 175 } 176 if r == 0 || c == 0 { 177 panic(ErrZeroLength) 178 } 179 if m.IsEmpty() { 180 m.mat = cblas128.General{ 181 Rows: r, 182 Cols: c, 183 Stride: c, 184 Data: useC(m.mat.Data, r*c), 185 } 186 m.capRows = r 187 m.capCols = c 188 return 189 } 190 if r != m.mat.Rows || c != m.mat.Cols { 191 panic(ErrShape) 192 } 193 } 194 195 func (m *CDense) reuseAsZeroed(r, c int) { 196 // This must be kept in-sync with reuseAs. 197 if m.mat.Rows > m.capRows || m.mat.Cols > m.capCols { 198 // Panic as a string, not a mat.Error. 199 panic(badCap) 200 } 201 if r == 0 || c == 0 { 202 panic(ErrZeroLength) 203 } 204 if m.IsEmpty() { 205 m.mat = cblas128.General{ 206 Rows: r, 207 Cols: c, 208 Stride: c, 209 Data: useZeroedC(m.mat.Data, r*c), 210 } 211 m.capRows = r 212 m.capCols = c 213 return 214 } 215 if r != m.mat.Rows || c != m.mat.Cols { 216 panic(ErrShape) 217 } 218 m.Zero() 219 } 220 221 // isolatedWorkspace returns a new dense matrix w with the size of a and 222 // returns a callback to defer which performs cleanup at the return of the call. 223 // This should be used when a method receiver is the same pointer as an input argument. 224 func (m *CDense) isolatedWorkspace(a CMatrix) (w *CDense, restore func()) { 225 r, c := a.Dims() 226 if r == 0 || c == 0 { 227 panic(ErrZeroLength) 228 } 229 w = getCDenseWorkspace(r, c, false) 230 return w, func() { 231 m.Copy(w) 232 putCDenseWorkspace(w) 233 } 234 } 235 236 // Reset zeros the dimensions of the matrix so that it can be reused as the 237 // receiver of a dimensionally restricted operation. 238 // 239 // Reset should not be used when the matrix shares backing data. 240 // See the Reseter interface for more information. 241 func (m *CDense) Reset() { 242 // Row, Cols and Stride must be zeroed in unison. 243 m.mat.Rows, m.mat.Cols, m.mat.Stride = 0, 0, 0 244 m.capRows, m.capCols = 0, 0 245 m.mat.Data = m.mat.Data[:0] 246 } 247 248 // IsEmpty returns whether the receiver is empty. Empty matrices can be the 249 // receiver for size-restricted operations. The receiver can be zeroed using Reset. 250 func (m *CDense) IsEmpty() bool { 251 // It must be the case that m.Dims() returns 252 // zeros in this case. See comment in Reset(). 253 return m.mat.Stride == 0 254 } 255 256 // Zero sets all of the matrix elements to zero. 257 func (m *CDense) Zero() { 258 r := m.mat.Rows 259 c := m.mat.Cols 260 for i := 0; i < r; i++ { 261 zeroC(m.mat.Data[i*m.mat.Stride : i*m.mat.Stride+c]) 262 } 263 } 264 265 // Copy makes a copy of elements of a into the receiver. It is similar to the 266 // built-in copy; it copies as much as the overlap between the two matrices and 267 // returns the number of rows and columns it copied. If a aliases the receiver 268 // and is a transposed Dense or VecDense, with a non-unitary increment, Copy will 269 // panic. 270 // 271 // See the Copier interface for more information. 272 func (m *CDense) Copy(a CMatrix) (r, c int) { 273 r, c = a.Dims() 274 if a == m { 275 return r, c 276 } 277 r = min(r, m.mat.Rows) 278 c = min(c, m.mat.Cols) 279 if r == 0 || c == 0 { 280 return 0, 0 281 } 282 // TODO(btracey): Check for overlap when complex version exists. 283 // TODO(btracey): Add fast-paths. 284 for i := 0; i < r; i++ { 285 for j := 0; j < c; j++ { 286 m.set(i, j, a.At(i, j)) 287 } 288 } 289 return r, c 290 } 291 292 // SetRawCMatrix sets the underlying cblas128.General used by the receiver. 293 // Changes to elements in the receiver following the call will be reflected 294 // in b. 295 func (m *CDense) SetRawCMatrix(b cblas128.General) { 296 m.capRows, m.capCols = b.Rows, b.Cols 297 m.mat = b 298 } 299 300 // RawCMatrix returns the underlying cblas128.General used by the receiver. 301 // Changes to elements in the receiver following the call will be reflected 302 // in returned cblas128.General. 303 func (m *CDense) RawCMatrix() cblas128.General { return m.mat } 304 305 // Grow returns the receiver expanded by r rows and c columns. If the dimensions 306 // of the expanded matrix are outside the capacities of the receiver a new 307 // allocation is made, otherwise not. Note the receiver itself is not modified 308 // during the call to Grow. 309 func (m *CDense) Grow(r, c int) CMatrix { 310 if r < 0 || c < 0 { 311 panic(ErrIndexOutOfRange) 312 } 313 if r == 0 && c == 0 { 314 return m 315 } 316 317 r += m.mat.Rows 318 c += m.mat.Cols 319 320 var t CDense 321 switch { 322 case m.mat.Rows == 0 || m.mat.Cols == 0: 323 t.mat = cblas128.General{ 324 Rows: r, 325 Cols: c, 326 Stride: c, 327 // We zero because we don't know how the matrix will be used. 328 // In other places, the mat is immediately filled with a result; 329 // this is not the case here. 330 Data: useZeroedC(m.mat.Data, r*c), 331 } 332 case r > m.capRows || c > m.capCols: 333 cr := max(r, m.capRows) 334 cc := max(c, m.capCols) 335 t.mat = cblas128.General{ 336 Rows: r, 337 Cols: c, 338 Stride: cc, 339 Data: make([]complex128, cr*cc), 340 } 341 t.capRows = cr 342 t.capCols = cc 343 // Copy the complete matrix over to the new matrix. 344 // Including elements not currently visible. Use a temporary structure 345 // to avoid modifying the receiver. 346 var tmp CDense 347 tmp.mat = cblas128.General{ 348 Rows: m.mat.Rows, 349 Cols: m.mat.Cols, 350 Stride: m.mat.Stride, 351 Data: m.mat.Data, 352 } 353 tmp.capRows = m.capRows 354 tmp.capCols = m.capCols 355 t.Copy(&tmp) 356 return &t 357 default: 358 t.mat = cblas128.General{ 359 Data: m.mat.Data[:(r-1)*m.mat.Stride+c], 360 Rows: r, 361 Cols: c, 362 Stride: m.mat.Stride, 363 } 364 } 365 t.capRows = r 366 t.capCols = c 367 return &t 368 }