------------------------------------------
------------------------------------------
----- A System Matrix Implementation -----
----- using Sparse Matrix Methods,   -----
----- and Energy Flow Processing     -----
-----                                -----
----- John Ringland                  -----
----- 2004/11/21                     -----
------------------------------------------
------------------------------------------

-- this file uses sparse matrix energy flow methods
-- to implement an ergodic pure metric sysWrapped SMN framework
-- using System Transforms.
-- Sparse matrix elements are either scalar multipliers or they are function Id's
-- and these are stored in both row and column entries of the sparse matrix.

with trace
include misc.e -- for the pretty_print function
include SparseSM.e -- for sparse system matrices

--without type_check
--with profile_time
--with profile

-- some constants to make the data accessing more human readable
--
-- System Model components
global constant model_         = 1
    global constant ST_        = 1 -- operator list
    global constant SM_        = 2 -- system matrix
    global constant SV_        = 3 -- state vector
    global constant CU_        = 4 -- update list
    --global constant MD_      = 4
-- matrix element components
global constant con_    = 1 -- converging
global constant div_    = 2 -- diverging


function union_SET(sequence set1, sequence set2)
    -- set1 and set2 are ordered lists of atoms with no duplicates
    -- this function performs the operation of the Union of the two sets by adding the 
    -- elements of the smallest set to to the largest set if they are not already present.
    -- It takes advantage of the ordering of the sets to optimise the procedure.
    sequence result, set, data
    integer rId, slen1, slen2, slen, dlen
    rId = 1
    slen1 = length(set1)
    slen2 = length(set2)
    if slen1 < slen2 then
        -- so that we iterate over the smallest set for efficiency
        data = set1
        set = set2
        slen = slen2
        dlen = slen1
    else
        data = set2
        set = set1
        slen = slen1
        dlen = slen2
    end if
    for i = 1 to dlen do
        -- uses rId so we search only that part of the set that the datum may be in
        -- this is possible because of the ordering of both sets.
        if rId > slen then
            set = set & data[i..dlen]
            exit
        end if
        result = BSearch(set[rId..slen],data[i])
        rId += result[index_]-1
        if not result[found_] then -- insert data[i]
            -- if new head
            if rId = 0 then
                set = {data[i]} & set
                slen += 1
                rId += 2
            -- if tail
            elsif rId = slen then
                -- because data is ordered the remainder can be appended
                set = set & data[i..dlen]
                exit
            -- if body
            else
                set = set[1..rId] & {data[i]} & set[rId+1..slen]
                slen += 1
                rId += 2
            end if
        else -- data[i] already in set
            rId += 1
        end if
    end for
	return set
end function

global function step_Model(sequence mdl, integer nSteps)
    -- steps the model forwards by nSteps
    sequence st, sm, sv, rUpdate, cUpdate, row, rowData, new_sv
    object acc, col, data, rUr, interfaces, output, input
    
    st = mdl[ST_]
    sm = mdl[SM_]
    sv = mdl[SV_]
    new_sv = sv
    cUpdate = mdl[CU_]
    -- for each moment in time
    for n = 1 to nSteps do
        rUpdate = {}
        -- cUpdate is an ordered list of colIdís which require updating
        -- for each element of cUpdate
        for c = 1 to length(cUpdate) do
            -- find the matrix column
            col = get_col_SM(sm,cUpdate[c])
            -- if the column existed in the matrix; for global outputs col=0
            if sequence(col) then
                col = col[elemId_] -- to keep only the rowIds
                -- form the union of each column with rUpdate
                rUpdate = union_SET(rUpdate,col)
            end if
        end for
        cUpdate = {} -- empty cUpdate now that it has been used
        -- rUpdate is now an ordered list of rowIdís which require updating
        -- for each element of rUpdate
        for r = 1 to length(rUpdate) do
            -- retrieve the Row from the sm
            rUr = rUpdate[r]
            row = get_row_SM(sm, rUr)
            acc = {}    
            -- for each matrix element in that row
            rowData= row[Data_]
            for e = 1 to length(rowData) do
                interfaces = rowData[e]
                if sequence(interfaces) then
                    -- process the interface function pipelines
                    -- separate input from output pipelines
                    output = interfaces[div_]
                    input = interfaces[con_]
                    if sequence(output) then
                        data = sv[row[elemId_][e]]
                        for f = length(output) to 1 by -1 do
                            data = call_func(output[f], {data})
                        end for
                    else
                        data = output * sv[row[elemId_][e]]
                    end if
                    if sequence(input) then
                        for f = length(input) to 1 by -1 do
                            data = call_func(input[f], {data})
                        end for
                    else
                        data = input * data
                    end if
                else
                    data = interfaces * sv[row[elemId_][e]]
                end if
                acc = acc & {data}
            end for 
            -- here we apply the system transform to the merged input
            if st[rUr] >= 0 then
                acc = call_func(st[rUr], {acc})
            elsif length(acc)=1 then
            -- prevent the accumulation of brackets around single datums
                acc = acc[1]
            end if
            -- compare the accumulated result with the sv element
            -- if different then
            if not equal(acc, sv[rUr]) then
                -- store the accumulated result in the corresponding sv element
                new_sv[rUr] = acc
                -- insert the rowId into cUpdate to indicate that the sv element has changed
                cUpdate = cUpdate & rUr
            end if
        end for
        sv = new_sv
    end for
    return {{st,sm,sv,cUpdate},nSteps}
end function

global procedure print_sv(sequence sv, sequence sv_names)
    for i = 1 to length(sv) do
        puts(1,"\n# ")
        puts(1, sv_names[i])
        puts(1, " #####\n")
        pretty_print(1,sv[i],{})
    end for
end procedure

www.Anandavala.info