// Copyright (C) 2010 Steffen Rendle, Zeno Gantner // Copyright (C) 2011 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.IntOpenHashSet; 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.Collections; import java.util.HashMap; import org.mymedialite.datatype.IMatrix; /** * Sparse representation of a boolean matrix, using HashSets. * Fast row-wise access is possible. * Indexes are zero-based. * * TODO Implement the classes below. * If you need a more memory-efficient data structure, try SparseBooleanMatrixBinarySearch * or SparseBooleanMatrixStatic. * @version 2.03 */ public class SparseBooleanMatrix implements IBooleanMatrix { ArrayList<IntSet> row_list = new ArrayList<IntSet>(); /** Default constructor */ public SparseBooleanMatrix() {} @Override public Boolean get(int x, int y) { if (x < row_list.size()) return row_list.get(x).contains(y); else return false; } @Override public void set(int x, int y, Boolean value) { if (value) get(x).add(y); else get(x).remove(y); } /** * Get a row. * @param x the row ID * @return the row */ @Override public IntSet get(int x) { if (x >= row_list.size()) for (int i = row_list.size(); i <= x; i++) row_list.add(new IntOpenHashSet()); return row_list.get(x); } @Override public boolean isSymmetric() { for (int i = 0; i < row_list.size(); i++) for (int j : row_list.get(i).toIntArray()) { if (i > j) continue; // check every pair only once if (!get(j, i)) return false; } return true; } @Override public IMatrix<Boolean> createMatrix(int x, int y) { return new SparseBooleanMatrix(); } @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).size(); } /** * Takes O(N) worst-case time, where N is the number of rows, if the internal hash table can be queried in constant time. */ @Override public IntList getEntriesByColumn(int column_id) { IntList list = new IntArrayList(); for (int row_id = 0; row_id < numberOfRows(); row_id++) if (row_list.get(row_id).contains(column_id)) list.add(row_id); return list; } @Override public int numEntriesByColumn(int column_id) { int count = 0; for (int row_id = 0; row_id < numberOfRows(); row_id++) if (row_list.get(row_id).contains(column_id)) count++; return count; } /** * The non-empty rows of the matrix (the ones that contain at least one true entry), with their IDs. * @return The non-empty rows of the matrix (the ones that contain at least one true entry), with their IDs */ public HashMap<Integer, IntSet> nonEmptyRows() { HashMap<Integer, IntSet> return_list = new HashMap<Integer, IntSet>(); for(int i=0; i < row_list.size(); i++) { IntSet row = get(i); if(row.size() > 0) return_list.put(i, row); } return return_list; } /** * The IDs of the non-empty rows in the matrix (the ones that contain at least one true entry) */ @Override public IntCollection nonEmptyRowIDs() { IntSet row_ids = new IntOpenHashSet(); for (int i = 0; i < row_list.size(); i++) if (row_list.get(i).size() > 0) row_ids.add(i); return row_ids; } /** * Get the IDs of the non-empty columns in the matrix (the ones that contain at least one true entry) */ @Override public IntCollection nonEmptyColumnIDs() { IntSet col_ids = new IntOpenHashSet(); for (int i = 0; i < row_list.size(); i++) for (int id : row_list.get(i).toIntArray()) col_ids.add(id); return col_ids; } @Override public int numberOfRows() { return row_list.size(); } @Override public int numberOfColumns() { int max_column_id = -1; for (IntSet row : row_list) if(row.size() > 0) max_column_id = Math.max(max_column_id, Collections.max(row)); return max_column_id + 1; } /** * Returns the number of (true) entries. */ @Override public int numberOfEntries() { int n = 0; for (IntSet row : row_list) n += row.size(); return n; } @Override public void grow(int num_rows, int num_cols) { // If necessary, grow rows if (num_rows > numberOfRows()) for (int i = row_list.size(); i < num_rows; i++) row_list.add(new IntOpenHashSet()); } /** * Get the transpose of the matrix, i.e. a matrix where rows and columns are interchanged. * @return the transpose of the matrix (copy) */ @Override public IMatrix<Boolean> transpose() { SparseBooleanMatrix transpose = new SparseBooleanMatrix(); for (int i = 0; i < row_list.size(); i++) { for(int j : this.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).toIntArray()) if (s.get(i, j)) c++; return c; } }