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  }