// Copyright (C) 2010 Steffen Rendle, Zeno Gantner
// Copyright (C) 2011, 2012 Zeno Gantner, Chris Newell
//
// This file is part of MyMediaLite.
//
// MyMediaLite is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// MyMediaLite is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with MyMediaLite. If not, see <http://www.gnu.org/licenses/>.
package org.mymedialite.datatype;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntArraySet;
import it.unimi.dsi.fastutil.ints.IntCollection;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map.Entry;
/**
* Sparse representation of a boolean matrix, using binary search (memory efficient).
*
* This data structure is static, which means that rows are represented as int arrays,
* a can be assigned, but not modified.
*
* Fast row-wise access is possible.
* Indexes are zero-based.
* @version 2.03
*/
public class SparseBooleanMatrixStatic implements IBooleanMatrix {
/**
* Internal representation of this data: list of rows.
*/
protected List<int[]> row_list = new ArrayList<int[]>();
/**
*
*/
@Override
public Boolean get(int x, int y) {
if (x < row_list.size())
return Arrays.binarySearch(row_list.get(x), y) >= 0;
else
return false;
}
@Override
public void set(int x, int y, Boolean value) {
throw new UnsupportedOperationException();
}
/**
*
*/
@Override
public IntSet get(int x) {
if (x >= row_list.size())
return new IntArraySet(0);
return new IntArraySet(row_list.get(x));
}
public void setRow(int x, int[] row) {
if (x >= row_list.size())
for (int i = row_list.size(); i <= x; i++)
row_list.add(new int[0]);
row_list.set(x, row);
}
/**
*
*/
@Override
public boolean isSymmetric() {
for (int i = 0; i < row_list.size(); i++)
for (int j : row_list.get(i))
if (!get(j, i))
return false;
return true;
}
/**
*
*/
@Override
public IMatrix<Boolean> createMatrix(int x, int y) {
return new SparseBooleanMatrixStatic();
}
/**
*
*/
@Override
public IntList getEntriesByRow(int row_id) {
return new IntArrayList(row_list.get(row_id));
}
/**
*
*/
@Override
public int numEntriesByRow(int row_id) {
return row_list.get(row_id).length;
}
/**
* Takes O(N log(M)) worst-case time, where N is the number of rows and M is the number of columns.
*/
@Override
public IntList getEntriesByColumn(int column_id) {
IntList list = new IntArrayList();
for (int row_id = 0; row_id < numberOfRows(); row_id++)
if (Arrays.binarySearch(row_list.get(row_id), column_id) >= 0)
list.add(row_id);
return list;
}
/**
*
*/
@Override
public int numEntriesByColumn(int column_id) {
int counter = 0;
for (int row_id = 0; row_id < numberOfRows(); row_id++)
if (Arrays.binarySearch(row_list.get(row_id), column_id) >= 0)
counter++;
return counter;
}
/**
* The non-empty rows of the matrix (the ones that contain at least one true entry), with their IDs.
*/
public List<Pair<Integer, int[]>> getNonEmptyRows() {
ArrayList<Pair<Integer, int[]>> return_list = new ArrayList<Pair<Integer, int[]>>();
for (int i = 0; i < row_list.size(); i++)
if (row_list.get(i).length > 0)
return_list.add(new Pair<Integer, int[]>(i, row_list.get(i)));
return return_list;
}
/**
*
*/
@Override
public IntCollection nonEmptyRowIDs() {
IntList row_ids = new IntArrayList();
for (int i = 0; i < row_list.size(); i++)
if (row_list.get(i).length > 0)
row_ids.add(i);
return row_ids;
}
/**
*
*/
@Override
public IntCollection nonEmptyColumnIDs() {
IntSet col_ids = new IntArraySet();
// Iterate over the complete data structure to find column IDs
for (int i = 0; i < row_list.size(); i++)
for (int id : row_list.get(i))
col_ids.add(id);
return col_ids;
}
/**
* The number of rows in the matrix.
* @return The number of rows in the matrix
*/
@Override
public int numberOfRows() {
return row_list.size();
}
/**
* The number of columns in the matrix.
* @return The number of columns in the matrix
*/
@Override
public int numberOfColumns() {
int max_column_id = -1;
for (int[] row : row_list)
if (row.length > 0)
for(int y: row)
max_column_id = Math.max(max_column_id, row[y]);
return max_column_id + 1;
}
/**
* The number of (true) entries.
* @return The number of (true) entries
*/
@Override
public int numberOfEntries() {
int n = 0;
for (int[] row : row_list)
n += row.length;
return n;
}
/**
*
*/
public void grow(int num_rows, int num_cols) {
throw new UnsupportedOperationException();
}
/**
* Get the transpose of the matrix, i.e. a matrix where rows and columns are interchanged.
* @return the transpose of the matrix
*/
@Override
public IMatrix<Boolean> transpose() {
SparseBooleanMatrixBinarySearch transpose = new SparseBooleanMatrixBinarySearch();
for (int i = 0; i < row_list.size(); i++)
for (int j : get(i))
transpose.set(j, i, true);
return transpose;
}
/**
*
*/
@Override
public int overlap(IBooleanMatrix s) {
int c = 0;
for (int i = 0; i < row_list.size(); i++)
for (int j : row_list.get(i))
if (s.get(i, j))
c++;
return c;
}
}