------------------------------------------ ------------------------------------------ ----- An Implementation of ----- ----- Sparse Matrix Methods. ----- ----- ----- ----- By John Ringland ----- ----- 2004/11/21 ----- ----- modified 2005/08/27 for the ----- ----- SOME project ----- ------------------------------------------ ------------------------------------------ -- SparseSM.e -- some constants to make the data accessing more human readable -- -- metadata components global constant dim_ = 1 global constant nElems_ = 1 global constant nRows_ = 2 global constant nCols_ = 3 -- matrix row components global constant Rows_ = 2 global constant rowId_ = 1 global constant elemId_ = 2 global constant Data_ = 3 -- matrix column components global constant Cols_ = 3 global constant colId_ = 1 --constant elemId_ = 2 --constant Data_ = 3 -- matrix index components global constant row_ = 1 global constant col_ = 2 -- BSearch result components global constant index_ = 1 global constant found_ = 2 global type SparseSMRow(object row) if sequence(row) and length(row)=3 and integer(row[rowId_]) then -- and length(row[elemId_])=length(row[Data_]) then if length(row[elemId_])=length(row[Data_]) then for i = 1 to length(row[elemId_]) do if not (integer(row[elemId_][i])and SMElement(row[Data_][i])) then return 0 end if end for --else it maybe in the process of being constructed so let it go for now end if else return 0 end if return 1 end type global type SparseSMCol(object col) if sequence(col) and length(col)=2 and integer(col[colId_]) then for i = 1 to length(col[elemId_]) do if not integer(col[elemId_][i]) then -- no data stored in column data return 0 end if end for else return 0 end if return 1 end type global type SparseSystemMatrix(object sm) if sequence(sm) and length(sm)=3 and length(sm[dim_])=3 and integer(sm[dim_][nElems_]) -- detect sparseSM metadata and integer(sm[dim_][nRows_]) and integer(sm[dim_][nCols_]) then -- detect sparseSM metadata for i = 1 to length(sm[Rows_]) do if not SparseSMRow(sm[Rows_][i]) then return 0 end if end for for i = 1 to length(sm[Cols_]) do if not SparseSMCol(sm[Cols_][i]) then return 0 end if end for return 1 end if return 0 end type global type SparseSMIndex(object id) return sequence(id) and length(id)=2 and integer(id[row_]) and integer(id[col_]) end type global function createSparseSM(integer nRows, integer nCols) -- creates an empty sparse system matrix with the following structure -- sm = { {nElems_, nRows_,nCols_}, <--metadata -- { {rowId_,{ {colId_,..}, {data,..} }, <--list of Rows -- { {colId_,{ {rowId_,..}, {data,..} } } <--list of Columns return {{ 0 , nRows, nCols}, {} , {} } end function function is_SM_index(SparseSystemMatrix sm, SparseSMIndex id) -- verifies that the index id={row,col} is valid for the matrix sm if length(id)=2 then if integer(id[row_]) and integer(id[col_]) then if id[row_] > 0 and id[col_] > 0 then if id[row_] <= sm[dim_][nRows_] and id[col_] <= sm[dim_][nCols_] then return 1 end if end if end if end if return 0 end function global function BSearch(sequence olist, integer target) -- This function performs a binary search looking for the integer target, -- olist must either be an ordered list of integers or a list of sequences -- where the first element of each sequence is an integer and the sequences are ordered. -- It returns {index, found} where if found=0 then index indicates the element prior to -- where the target should have been, this allows for insertion if desired. If found=1 -- then index indicates the location of the target. integer left, right, mid, excluded left = 1 right = length(olist) excluded = 0 mid = left + floor((right-left) / 2) -- if olist is a list of integers if integer(olist[1]) then while mid != left do if olist[mid] >= target then -- search left half right = mid elsif olist[mid+1] <= target then -- serch right half left = mid+1 else -- target not in list excluded = 1 exit -- mid is element prior to insertion point end if mid = left + floor((right-left) / 2) end while -- check left then right if not yet excluded if not excluded then if olist[left] = target then return {left,1} elsif olist[right] = target then return {right,1} else -- not in list if olist[left] < target then if olist[right] > target then return {left, 0} -- insert between left and right else return {right, 0} -- insert after right, e.g as new tail end if else return {left-1,0} -- insert to the left of left, e.g as new head end if end if else return {mid, 0} end if else -- if olist is a list of sequences while mid != left do if olist[mid][1] >= target then -- search left half right = mid elsif olist[mid+1][1] <= target then -- serch right half left = mid+1 else -- target not in list excluded = 1 exit -- mid is element prior to insertion point end if mid = left + floor((right-left) / 2) end while -- check left then right if not yet excluded if not excluded then if olist[left][1] = target then return {left,1} elsif olist[right][1] = target then return {right,1} else -- not in list if olist[left][1] < target then if olist[right][1] > target then return {left, 0} -- insert between left and right else return {right, 0} -- insert after right, e.g as new tail end if else return {left-1,0} -- insert to the left of left, e.g as new head end if end if else return {mid, 0} end if end if end function global function set_SM(SparseSystemMatrix sm, SparseSMIndex id, SMElement data) -- sets the sm matrix element specified by id={row,col} to data -- to remove a matrix element set data=0 only, don't use {0,0} etc -- This particular implementation stores data twice by both row and column. -- This is assuming that data is a function Id which is just an atom -- and it also assumes that systems will require efficient access to these id's -- by both row and column, row when acquiring input, and column when changing -- output transforms to create non-ergodic system networks. integer rId,rcId, cId, crId, isNULL sequence rSearch, rcSearch, cSearch, crSearch -- verify that the index is valid if not is_SM_index(sm,id) then puts(1,"The SM index was invalid: id = ") print(1, id) puts(1,"\n") --abort(2) return sm end if -- this function assumes that the only NULL data is a zero valued SMReal -- this is adequate because of the functioning of an SM -- must include this file after PNData.e for this to work if SMReal(data[con_]) and SMReal(data[div_]) then --atom(data) then if equal(data[con_][1], fdZero) and equal(data[div_][1], fdZero) then isNULL = 1 else isNULL = 0 end if else -- SMElement contains operators isNULL = 0 end if -- find where to put the data if length(sm[Rows_]) then -- search for row rSearch = BSearch(sm[Rows_], id[row_]) rId = rSearch[index_] rcSearch = {0,0} -- for a later 'if' test when we begin the column search -- if row exists then if rSearch[found_] then -- search for element rcSearch = BSearch(sm[Rows_][rId][elemId_], id[col_]) rcId = rcSearch[index_] -- if element exists then modify or remove it if rcSearch[found_] then if isNULL then -- remove element sm[dim_][nElems_] -= 1 -- if not last element then remove from sequence if length(sm[Rows_][rId][elemId_]) > 1 then -- if head if rcId = 1 then sm[Rows_][rId][elemId_] = sm[Rows_][rId][elemId_][2..length(sm[Rows_][rId][elemId_])] sm[Rows_][rId][Data_] = sm[Rows_][rId][Data_][2..length(sm[Rows_][rId][Data_])] -- if tail elsif rcId = length(sm[Rows_][rId][elemId_]) then sm[Rows_][rId][elemId_] = sm[Rows_][rId][elemId_][1..length(sm[Rows_][rId][elemId_])-1] sm[Rows_][rId][Data_] = sm[Rows_][rId][Data_][1..length(sm[Rows_][rId][Data_])-1] -- if body else sm[Rows_][rId][elemId_] = sm[Rows_][rId][elemId_][1..rcId-1] & sm[Rows_][rId][elemId_][rcId+1..length(sm[Rows_][rId][elemId_])] sm[Rows_][rId][Data_] = sm[Rows_][rId][Data_][1..rcId-1] & sm[Rows_][rId][Data_][rcId+1..length(sm[Rows_][rId][Data_])] end if else -- it is last element so remove whole sequence -- if last row then set sm[Rows_] to empty -- if head if rId = 1 then sm[Rows_] = sm[Rows_][2..length(sm[Rows_])] -- if tail elsif rId = length(sm[Rows_]) then sm[Rows_] = sm[Rows_][1..length(sm[Rows_])-1] -- if body else sm[Rows_] = sm[Rows_][1..rId-1] & sm[Rows_][rId+1..length(sm[Rows_])] end if end if else -- modify element sm[Rows_][rId][Data_][rcId] = data end if else -- no element so create element if necessary if not isNULL then -- create element sm[dim_][nElems_] += 1 -- if new head if rcId = 0 then sm[Rows_][rId][elemId_] = {id[col_]} & sm[Rows_][rId][elemId_] sm[Rows_][rId][Data_] = {data} & sm[Rows_][rId][Data_] -- if tail elsif rcId = length(sm[Rows_][rId][elemId_]) then sm[Rows_][rId][elemId_] = sm[Rows_][rId][elemId_] & {id[col_]} sm[Rows_][rId][Data_] = sm[Rows_][rId][Data_] & {data} -- if body else sm[Rows_][rId][elemId_] = sm[Rows_][rId][elemId_][1..rcId] & {id[col_]} & sm[Rows_][rId][elemId_][rcId+1..length(sm[Rows_][rId][elemId_])] sm[Rows_][rId][Data_] = sm[Rows_][rId][Data_][1..rcId] & {data} & sm[Rows_][rId][Data_][rcId+1..length(sm[Rows_][rId][Data_])] end if end if end if else -- no row so create row if necessary if not isNULL then sm[dim_][nElems_] += 1 -- if new head if rId = 0 then sm[Rows_] = {{id[row_],{id[col_]}, {data}}} & sm[Rows_] -- if tail elsif rId = length(sm[Rows_]) then sm[Rows_] = sm[Rows_] & {{id[row_],{id[col_]}, {data}}} -- if body else sm[Rows_] = sm[Rows_][1..rId] & {{id[row_],{id[col_]}, {data}}} & sm[Rows_][rId+1..length(sm[Rows_])] end if end if end if -- only if you created an entry or you removed an entry then -- you chould check the columns if not rcSearch[found_] xor isNULL then -- search for column cSearch = BSearch(sm[Cols_], id[col_]) cId = cSearch[index_] -- if column exists then if cSearch[found_] then -- search for element crSearch = BSearch(sm[Cols_][cId][elemId_], id[row_]) crId = crSearch[index_] -- if element exists then modify or remove it if crSearch[found_] then if isNULL then -- remove element -- if not last element then remove from sequence if length(sm[Cols_][cId][elemId_]) > 1 then -- if head if crId = 1 then sm[Cols_][cId][elemId_] = sm[Cols_][cId][elemId_][2..length(sm[Cols_][cId][elemId_])] -- if tail elsif crId = length(sm[Cols_][cId][elemId_]) then sm[Cols_][cId][elemId_] = sm[Cols_][cId][elemId_][1..length(sm[Cols_][cId][elemId_])-1] -- if body else sm[Cols_][cId][elemId_] = sm[Cols_][cId][elemId_][1..crId-1] & sm[Cols_][cId][elemId_][crId+1..length(sm[Cols_][cId][elemId_])] end if else -- it is last element so remove whole sequence -- if last column then set sm[Cols_] to empty -- if head if cId = 1 then sm[Cols_] = sm[Cols_][2..length(sm[Cols_])] -- if tail elsif cId = length(sm[Cols_]) then sm[Cols_] = sm[Cols_][1..length(sm[Cols_])-1] -- if body else sm[Cols_] = sm[Cols_][1..cId-1] & sm[Cols_][cId+1..length(sm[Cols_])] end if end if end if else -- no element so create element if necessary if not isNULL then -- create element -- if new head if crId = 0 then sm[Cols_][cId][elemId_] = {id[row_]} & sm[Cols_][cId][elemId_] -- if tail elsif crId = length(sm[Cols_][cId][elemId_]) then sm[Cols_][cId][elemId_] = sm[Cols_][cId][elemId_] & {id[row_]} -- if body else sm[Cols_][cId][elemId_] = sm[Cols_][cId][elemId_][1..crId] & {id[row_]} & sm[Cols_][cId][elemId_][crId+1..length(sm[Cols_][cId][elemId_])] end if end if end if else -- no column so create column if necessary if not isNULL then -- if new head if cId = 0 then sm[Cols_] = {{id[col_],{id[row_]}}} & sm[Cols_] -- if tail elsif cId = length(sm[Cols_]) then sm[Cols_] = sm[Cols_] & {{id[col_],{id[row_]}}} -- if body else sm[Cols_] = sm[Cols_][1..cId] & {{id[col_],{id[row_]}}} & sm[Cols_][cId+1..length(sm[Cols_])] end if end if end if end if else -- matrix is empty so simply insert the data if not isNULL then sm[dim_][nElems_] += 1 -- sm[Rows_][rowId_] = id[row_] -- sm[Rows_][elemId_] = {id[col_]} -- sm[Rows_][Data_] = {data} sm[Rows_] = {{id[row_],{id[col_]}, {data}}} -- sm[Cols_][colId_] = id[col_] -- sm[Cols_][elemId_] = {id[row_]} -- sm[Cols_][Data_] = {data} sm[Cols_] = {{id[col_],{id[row_]}}} end if -- else if isNULL then do nothing end if return sm end function global function get_SM(SparseSystemMatrix sm, SparseSMIndex id) -- retrieves the element of sm specified by the id={row,col} -- assumes the index is valid to save time during runtime processing -- but returns zero if index is not valid. integer rId sequence rSearch, rcSearch, smRowsrId -- search for row rSearch = BSearch(sm[Rows_], id[row_]) rId = rSearch[index_] -- if row exists then if rSearch[found_] then smRowsrId = sm[Rows_][rId] -- search for element rcSearch = BSearch(smRowsrId[elemId_], id[col_]) -- if element exists then return its data if rcSearch[found_] then return smRowsrId[Data_][rcSearch[index_]] end if end if return 0 end function global function get_col_SM(SparseSystemMatrix sm, integer col) -- retrieves the column of sm specified by col -- assumes the index is valid to save time during runtime processing -- but returns zero if index is not valid. sequence cSearch, smCols -- search for the column smCols = sm[Cols_] cSearch = BSearch(smCols, col) -- if column exists then if cSearch[found_] then return smCols[cSearch[index_]] end if return 0 end function global function get_row_SM(SparseSystemMatrix sm, integer row) -- retrieves the row of sm specified by row -- assumes the index is valid to save time during runtime processing -- but returns zero if index is not valid. sequence rSearch, smRows -- search for row smRows = sm[Rows_] rSearch = BSearch(smRows, row) -- if row exists then if rSearch[found_] then return smRows[rSearch[index_]] end if return 0 end function