Package edu.ucla.sspace.matrix

Source Code of edu.ucla.sspace.matrix.AtomicGrowingMatrix

/*
* Copyright 2009 David Jurgens
*
* This file is part of the S-Space package and is covered under the terms and
* conditions therein.
*
* The S-Space package is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation and distributed hereunder to you.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND NO REPRESENTATIONS OR WARRANTIES,
* EXPRESS OR IMPLIED ARE MADE.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, WE MAKE
* NO REPRESENTATIONS OR WARRANTIES OF MERCHANT- ABILITY OR FITNESS FOR ANY
* PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE OR DOCUMENTATION
* WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER
* RIGHTS.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package edu.ucla.sspace.matrix;

import edu.ucla.sspace.util.IntegerMap;

import edu.ucla.sspace.vector.AtomicVector;
import edu.ucla.sspace.vector.CompactSparseVector;
import edu.ucla.sspace.vector.DenseVector;
import edu.ucla.sspace.vector.DoubleVector;
import edu.ucla.sspace.vector.Vectors;

import java.util.Arrays;
import java.util.Map;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;


/**
* A concurrent, thread-safe, growable {@code Matrix} class.  This class allows
* multiple threads to operate on the same matrix where all methods are
* concurrent to the fullest extent possible.
*
* @author David Jurgens
*/
public class AtomicGrowingMatrix implements AtomicMatrix, java.io.Serializable {

    private static final long serialVersionUID = 1L;
   
    /**
     * The read lock for reading rows from this {@code AtomicGrowingMatrix}.
     */
    private Lock rowReadLock;

    /**
     * The write lock for adding rows to this {@code AtomicGrowingMatrix}.
     */
    private Lock rowWriteLock;

    /**
     * The read lock for reading from the internal rows.
     */
    private Lock denseArrayReadLock;

    /**
     * The write lock for writing to internal rows.
     */
    private Lock denseArrayWriteLock;

    /**
     * The number of rows represented in this {@code AtomicGrowingMatrix}.
     */
    private AtomicInteger rows;

    /**
     * The number of columns represented in this {@code AtomicGrowingMatrix}.
     */
    private AtomicInteger cols;
 
    /**
     * Each row is defined as a {@link AtomicVector} which does most of the
     * work.
     */
    private final Map<Integer, AtomicVector> sparseMatrix;

    /**
     * Create an {@code AtomicGrowingMatrix} with 0 rows and 0 columns.
     */
    public AtomicGrowingMatrix() {
        this.rows = new AtomicInteger(0);
        this.cols = new AtomicInteger(0);
        sparseMatrix = new IntegerMap<AtomicVector>();
       
        ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
        rowReadLock = rwLock.readLock();
        rowWriteLock = rwLock.writeLock();

        rwLock = new ReentrantReadWriteLock();
        denseArrayReadLock = rwLock.readLock();
        denseArrayWriteLock = rwLock.writeLock();
    }
   
    /**
     * {@inheritDoc}
     */
    public double addAndGet(int row, int col, double delta) {
        checkIndices(row, col);
        AtomicVector rowEntry = getRow(row, col, true);
        return rowEntry.addAndGet(col, delta);   
    }

    /**
     * Verify that the given row and column value is non-negative
     *
     * @param row The row index to check.
     * @param the The column index to check.
     */   
    private void checkIndices(int row, int col) {
         if (row < 0 || col < 0) {
             throw new ArrayIndexOutOfBoundsException();
         }
    }

    /**
     * {@inheritDoc}
     */
    public int columns() {
        return cols.get();
    }

    /**
     * {@inheritDoc}
     */
    public double get(int row, int col) {
        checkIndices(row, col);
        AtomicVector rowEntry = getRow(row, col, false);
        return (rowEntry == null) ? 0d : rowEntry.get(col);
    }

    /**
     * {@inheritDoc}
     */
    public double getAndAdd(int row, int col, double delta) {
        checkIndices(row, col);
        AtomicVector rowEntry = getRow(row, col, true);
        return rowEntry.getAndAdd(col, delta);
    }


    /**
     * {@inheritDoc} The length of the returned column reflects the size of
     * matrix at the time of the call, which may be different from earlier calls
     * to {@link #rows()}
     */
    public double[] getColumn(int column) {
        checkIndices(0, column);
        rowReadLock.lock();
        double[] values = new double[rows.get()];
        for (int row = 0; row < rows.get(); ++row)
            values[row] = get(row, column);
        rowReadLock.unlock();
        return values;
    }

    /**
     * {@inheritDoc} The length of the returned row vector reflects the size of
     * matrix at the time of the call, which may be different from earlier calls
     * to {@link #rows()}
     */
    public DoubleVector getColumnVector(int column) {
        checkIndices(0, column);
        rowReadLock.lock();
        DoubleVector values = new DenseVector(rows.get());
        for (int row = 0; row < rows.get(); ++row) {
            double value = get(row, column);
            if (value != 0d)
                values.set(row, value);
        }
        rowReadLock.unlock();
        return values;
    }

    /**
     * Returns an immutable view of the columns's data as a non-atomic vector,
     * which may present an inconsistent view of the data if this matrix is
     * being concurrently modified.  This method should only be used in special
     * cases where the vector is being accessed at a time when the matrix (or
     * this particular row) will not be modified.
     */
    public DoubleVector getColumnVectorUnsafe(int column) {
        checkIndices(0, column);
        DoubleVector values = new DenseVector(rows.get());
        for (int row = 0; row < rows.get(); ++row) {
            AtomicVector rowEntry = getRow(row, -1, false);           
            double value = 0;
            if (rowEntry != null && (value = rowEntry.get(column)) != 0)
                values.set(row, value);
        }
        return values;
    }

    /**
     * {@inheritDoc} The length of the returned row reflects the size of matrix
     * at the time of the call, which may be different from earlier calls to
     * {@link #columns()}.
     */
    public double[] getRow(int row) {
        checkIndices(row, 0);
        AtomicVector rowEntry = getRow(row, -1, false);
        return (rowEntry == null)
            ? new double[cols.get()]
            : toArray(rowEntry, cols.get());
    }

    /**
     * Gets the {@code AtomicVector} associated with the index, or {@code null}
     * if no row entry is present, or if {@code createIfAbsent} is {@code true},
     * creates the missing row and returns that.
     *
     * @param row the row to get
     * @param col the column in the row that will be accessed or {@code -1} if
     *        the entire row is needed.  This value is only used to resize the
     *        matrix dimensions if the row is to be created.
     * @param createIfAbsent {@true} if a row that is requested but not present
     *        should be created
     *
     * @return the row at the entry or {@code null} if the row is not present
     *         and it was not to be created if absent
     */
    private AtomicVector getRow(int row, int col, boolean createIfAbsent) {
        rowReadLock.lock();
        AtomicVector rowEntry = sparseMatrix.get(row);
        rowReadLock.unlock();
        
        // If no row existed, create one
        if (rowEntry == null && createIfAbsent) {
            rowWriteLock.lock();
            // ensure that another thread has not already added this row while
            // this thread was waiting on the lock
            rowEntry = sparseMatrix.get(row);
            if (rowEntry == null) {
                rowEntry = new AtomicVector(new CompactSparseVector());

                // update the bounds as necessary
                if (row >= rows.get()) {
                    rows.set(row + 1);
                }
                if (col >= cols.get()) {
                    cols.set(col + 1);
                }
                sparseMatrix.put(row, rowEntry);
            }
            rowWriteLock.unlock();
        }
        return rowEntry;
    }

    /**
     * {@inheritDoc} The length of the returned row vector reflects the size of
     * matrix at the time of the call, which may be different from earlier calls
     * to {@link #columns()}.
     */
    public DoubleVector getRowVector(int row) {
        DoubleVector v = getRow(row, -1, false);
        // If no row was currently assigned in the matrix, then return an empty
        // vector in its place.  Otherwise, return a view on top of the vector
        // with its current length
        return (v == null)
            ? new CompactSparseVector(cols.get())
            : Vectors.subview(v, 0, cols.get());
    }

    /**
     * Returns an immutable view of the row's data as a non-atomic vector, which
     * may present an inconsistent view of the data if this matrix is being
     * concurrently modified.  This method should only be used in special cases
     * where the vector is being accessed at a time when the matrix (or this
     * particular row) will not be modified.
     *
     * @param row the row whose values should be returned
     *
     * @return an unsafe, non-atomic view of the row's data
     */
    public DoubleVector getRowVectorUnsafe(int row) {
        AtomicVector rowEntry = sparseMatrix.get(row);
        return (rowEntry == null)
            ? new CompactSparseVector(cols.get())
            : Vectors.immutable(Vectors.subview(rowEntry.getVector(),
                                                0, cols.get()));
    }

    /**
     * {@inheritDoc}
     */
    public int rows() {
        return rows.get();
    }

    /**
     * {@inheritDoc}
     */
    public void set(int row, int col, double val) {
        checkIndices(row, col);

        AtomicVector rowEntry = getRow(row, col, true);
        denseArrayReadLock.lock();
        rowEntry.set(col, val);
        denseArrayReadLock.unlock();
    }

    /**
     * {@inheritDoc}
     */
    public void setColumn(int column, double[] values) {
        checkIndices(0, column);
        for (int row = 0; row < rows.get(); ++row)
            set(row, column, values[row]);
    }

    /**
     * {@inheritDoc}
     */
    public void setColumn(int column, DoubleVector values) {
        checkIndices(0, column);
        for (int row = 0; row < rows.get(); ++row)
            set(row, column, values.get(row));
    }
 
    /**
     * {@inheritDoc}
     */
    public void setRow(int row, double[] columns) {
        checkIndices(row, 0);
        AtomicVector rowEntry = getRow(row, columns.length - 1, true);
        denseArrayReadLock.lock();
        for (int i = 0; i < columns.length; ++i)
            rowEntry.set(i, columns[i]);
        denseArrayReadLock.unlock();
    }

    /**
     * {@inheritDoc}
     */
    public void setRow(int row, DoubleVector values) {
        checkIndices(row, 0);
        AtomicVector rowEntry = getRow(row, values.length() - 1, true);
        denseArrayReadLock.lock();
        Vectors.copy(rowEntry, values);
        denseArrayReadLock.unlock();
    }
 
    /**
     * {@inheritDoc}
     */
    public double[][] toDenseArray() {
        // Grab the write lock to prevent any new rows from being updated
        rowWriteLock.lock();
        // Then grab the whole matrix lock to prevent any values from being set
        // while this method converts the rows into arrays.
        denseArrayWriteLock.lock();
        int c = cols.get();
        double[][] m = new double[rows.get()][c];
        for (Map.Entry<Integer, AtomicVector> e : sparseMatrix.entrySet()) {
            m[e.getKey()] = toArray(e.getValue(), c);
        }
        denseArrayWriteLock.unlock();
        rowWriteLock.unlock();
        return m;
    }

    /**
     * Returns an array of the specified length using the data in the provided
     * vector.  This method allows row vectors to be converted to arrays based
     * on the size of the matrix at the time of the call, thereby prevent
     * changes in length due to external vector modifications.
     */
    private static double[] toArray(DoubleVector v, int length) {
        double[] arr = new double[length];
        for (int i = 0; i < arr.length; ++i) {
            arr[i] = v.get(i);
        }
        return arr;
    }
}
TOP

Related Classes of edu.ucla.sspace.matrix.AtomicGrowingMatrix

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.