com.meliorbis.numerics.Numerics Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of Numerics Show documentation
Show all versions of Numerics Show documentation
A library for working with large multi-dimensional arrays and the functions they represent
/**
*
*/
package com.meliorbis.numerics;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.HashMap;
import java.util.List;
import java.util.PrimitiveIterator;
import java.util.logging.Logger;
import com.meliorbis.numerics.generic.MultiDimensionalArray;
import com.meliorbis.numerics.generic.MultiDimensionalArrayException;
import com.meliorbis.numerics.generic.MultiDimensionalArrayFactory;
import com.meliorbis.numerics.generic.SettableIndexedIterator;
import com.meliorbis.numerics.generic.impl.IntegerArray;
import com.meliorbis.numerics.generic.impl.IntegerArrayFactory;
import com.meliorbis.numerics.generic.primitives.DoubleSettableIndexedIterator;
import com.meliorbis.numerics.generic.primitives.DoubleSettableIterator;
import com.meliorbis.numerics.generic.primitives.impl.DoubleArray;
import com.meliorbis.numerics.generic.primitives.impl.DoubleBlockedArrayFactory;
import com.meliorbis.numerics.threading.CurrentThreadExecutor;
import com.meliorbis.numerics.threading.Executor;
import com.meliorbis.numerics.threading.MultiThreadedExecutor;
import com.meliorbis.utils.Utils;
import au.com.bytecode.opencsv.CSVReader;
/**
* Facade for the numerics system that is configurable to provide different
* types of array etc.
*
* @author Tobias Grasl
*
* @param The numeric type to be used
*/
public class Numerics
{
private static final int THREAD_COUNT = Integer.getInteger("com.meliorbis.numerics.threads", 5);
public static final Logger LOG = Logger.getLogger(Numerics.class.getName());
private static volatile Numerics> _instance;
public static Numerics> instance()
{
Numerics> instance = _instance;
if(instance == null)
{
LOG.warning("Accessing Numerics instance before it is initialised, defaulting to DoubleNumerics");
instance = DoubleNumerics.instance();
_instance = instance;
}
return instance;
}
private final Executor _executor;
private final DoubleBlockedArrayFactory _arrayFactory;
@SuppressWarnings("rawtypes")
final private HashMap _factoriesByClass = new HashMap();
public Numerics(Class class_)
{
this(class_, THREAD_COUNT);
}
public Numerics(Class class_, int threadCount_)
{
_executor = threadCount_ == 1 ? new CurrentThreadExecutor() : new MultiThreadedExecutor(threadCount_);
if(class_.equals(Double.class))
{
_arrayFactory = new DoubleBlockedArrayFactory(_executor/*new CurrentThreadExecutor()*/);
}
else
{
throw new IllegalArgumentException("Presently, only double numerics is supported");
}
_factoriesByClass.put(class_,_arrayFactory);
_factoriesByClass.put(Integer.class,new IntegerArrayFactory(this));
}
public void destroy()
{
_executor.destroy();
}
/**
* @return the arrayFactory
*/
public DoubleBlockedArrayFactory getArrayFactory()
{
return _arrayFactory;
}
/**
* Reads a CSV file at the specified path into a two-dimensional array, which is returned.
*
* @param path_ The path from which to read the file
*
* @return A 2-D array with the same numbers of rows and columns as the CSV file
*/
public DoubleArray readCSV(String path_)
{
return readCSV(path_, ',');
}
/**
* Reads a CSV resource from the provided input stream into a two-dimensional array, which is returned.
*
* @param is_ The stream from which to read the data
*
* @return A 2-D array with the same numbers of rows and columns as the CSV data
*/
public DoubleArray readCSV(InputStream is_)
{
return readCSV(is_, ',');
}
/**
* Writes the provided array to a CSV file that also contains metadata so that the original array
* can be reproduced from the file.
*
* @param array_ The array to be written
* @param path_ The path at which to save the written array
*
* @param The type of numeric array to be written
*/
public void writeFormattedCSV(MultiDimensionalArray array_, String path_)
{
writeFormattedCSV(array_, path_,',');
}
/**
* Writes the provided array to a CSV file that also contains metadata so that the original array
* can be reproduced from the file.
*
* @param array_ The array to be written
* @param path_ The path at which to save the written array
*
* @param The type of numeric array to be written
*/
public void writeFormattedCSV(MultiDimensionalArray array_, File path_)
{
writeFormattedCSV(array_, path_, ',');
}
/**
* Writes the provided array to a CSV file that also contains metadata so that the original array
* can be reproduced from the file. The file will use the provided separator between fields
*
* @param array_ The array to be written
* @param path_ The path at which to save the written array
* @param separator_ The field-separator to use
*
* @param The type of numeric array to be written
*/
public void writeFormattedCSV(MultiDimensionalArray array_, String path_, char separator_)
{
writeFormattedCSV(array_, new File(path_), separator_);
}
/**
* Writes the provided array to a CSV file that also contains metadata so that the original array
* can be reproduced from the file. The file will use the provided separator between fields
*
* @param array_ The array to be written
* @param file_ The path at which to save the written array
* @param separator_ The field-separator to use
*/
private void writeFormattedCSV(MultiDimensionalArray array_, File file_,
char separator_)
{
FileWriter writer = null;
try {
writer = new FileWriter(file_);
writeFormattedCSV(array_, separator_, writer);
} catch (IOException e) {
throw new NumericsException("Error writing CSV", e);
}
finally
{
if(writer != null)
{
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void writeFormattedCSV(MultiDimensionalArray array_,
char separator_, FileWriter writer)
{
try {
if(array_ == null)
{
LOG.warning("Tried to write null array. Ignoring.");
return;
}
// Write the array size in the first row
int[] size = array_.size();
// Write the class of the array by using the first element
// TODO: This means that zero-size arrays can't be written
String type = array_ instanceof DoubleArray ? Double.class.getName() :
Integer.class.getName();
boolean isDouble = array_ instanceof DoubleArray;
writer.write(type);
// Don't forget the separator!
writer.write(separator_);
for (int i = 0; i < size.length; i++) {
writer.write(Integer.toString(size[i]));
if(i < (size.length-1))
{
writer.write(separator_);
}
}
// Now write the array
SettableIndexedIterator arrayIter = array_.iterator();
/* Write the array in one big column!
*/
while(arrayIter.hasNext())
{
// Add a line break
writer.write('\n');
// Write the next array entry
if(isDouble)
{
writer.write(Double.toString(((PrimitiveIterator.OfDouble)arrayIter).nextDouble()));
}
else
{
writer.write(arrayIter.next().toString());
}
}
writer.flush();
} catch (IOException e) {
throw new NumericsException("Error writing array to file",e);
} catch (MultiDimensionalArrayException e) {
throw new NumericsException("Error writing array to file",e);
}
}
/**
* Reads an array from a CSV file that also contains the relevant metdata to reproduce the correct dimensions
*
* @param file_ The file from which to read the array
*
* @param The type of numeric array to be read
*
* @return The array read from the file
*/
public MultiDimensionalArray readFormattedCSV(File file_)
{
FileReader fileReader = null;
try
{
fileReader = new FileReader(file_);
return readFormattedCSV(fileReader);
} catch (FileNotFoundException e)
{
throw new NumericsException(e);
} catch (IOException e)
{
throw new NumericsException(e);
}
finally
{
if(fileReader != null)
{
try {
fileReader.close();
} catch (IOException e) {
System.out.println("Unable to close reader");
}
}
}
}
/**
* Reads an array from a CSV file that also contains the relevant metdata to reproduce the correct dimensions
*
* @param path_ Path to the file from which to read the array
*
* @param The type of numeric array to be read
*
* @return The array read from the file
*/
public com.meliorbis.numerics.generic.MultiDimensionalArray readFormattedCSV(String path_)
{
return readFormattedCSV(new File(path_));
}
/**
* Reads an array from a CSV stream that also contains the relevant metadata to reproduce the correct dimensions
*
* @param is_ The stream from which to read the array
*
* @param The type of numeric array to be read
*
* @return The array read from the file
*/
public com.meliorbis.numerics.generic.MultiDimensionalArray readFormattedCSV(InputStream is_)
{
try
{
return readFormattedCSV(new InputStreamReader(is_));
} catch (IOException e)
{
throw new NumericsException(e);
}
}
private com.meliorbis.numerics.generic.MultiDimensionalArray readFormattedCSV(
Reader reader) throws IOException, NumericsException {
CSVReader csvReader = new CSVReader(reader,',');
List allLines = csvReader.readAll();
csvReader.close();
int rows = allLines.size();
int columns = 0;
if(allLines.size() == 0)
{
LOG.warning("Tried to read empty file. Ignoring.");
return null;
}
// Determine the dimensions of the matrix from the first row
String[] dimStrings = allLines.get(0);
MultiDimensionalArrayFactory> arrayFactory;
try {
@SuppressWarnings("unchecked")
Class arrayClass = (Class) Class.forName(dimStrings[0]);
arrayFactory = getArrayFactory(arrayClass);
} catch (ClassNotFoundException e) {
throw new NumericsException("Badly formed file, unknown array type",e);
}
int[] dims = new int[dimStrings.length-1];
for (int i = 0; i < dims.length; i++) {
dims[i] = Integer.parseInt(dimStrings[i+1]);
}
if(rows > 1)
{
columns = allLines.get(1).length;
}
if(columns == 0)
{
throw new NumericsException("Badly formed file has no rows or columns");
}
MultiDimensionalArray parsedArray;
parsedArray = arrayFactory.newArray(dims);
SettableIndexedIterator arrayIterator = parsedArray.iterator();
int rowIndex = 1;
int colIndex = 0;
for(; rowIndex < rows; rowIndex++)
{
String[] currentRow = allLines.get(rowIndex);
if(currentRow.length != columns)
{
throw new NumericsException("Not all rows the same length");
}
colIndex = 0;
for(; colIndex < columns && arrayIterator.hasNext(); colIndex++)
{
if(arrayIterator instanceof PrimitiveIterator.OfDouble)
{
((PrimitiveIterator.OfDouble)arrayIterator).nextDouble();
}
else
{
arrayIterator.next();
}
V value = arrayFactory.fromString(currentRow[colIndex]);
if(arrayIterator instanceof PrimitiveIterator.OfDouble)
{
((DoubleSettableIterator)arrayIterator).set(((Double)value).doubleValue());
}
else
{
arrayIterator.set(value);
}
}
if(colIndex < columns)
{
break;
}
}
if(arrayIterator.hasNext())
{
System.err.println("Less data than space in the array!");
}
else if(rowIndex < rows || colIndex < columns)
{
System.err.println("More data than space in the array!");
}
return parsedArray;
}
/**
* Retrieves the factory class for creating arrays of the specified type
*
* @param clazz_ The type of numeric array for which to get the factory
*
* @param The type of numeric array for which to get the factory
*
* @return The array factory for the type requested
*/
@SuppressWarnings("unchecked")
public MultiDimensionalArrayFactory> getArrayFactory(Class clazz_)
{
return _factoriesByClass.get(clazz_);
}
/**
* Reads a CSV file at the specified path into a two-dimensional array, which is returned
*
* @param path_ The path from which to read the file
* @param sep_ The separator used in the file
*
* @return A 2-D array with the same numbers of rows and columns as the CSV file
*/
public DoubleArray readCSV(String path_, char sep_)
{
FileInputStream is = null;
try
{
is = new FileInputStream(path_);
return readCSV(is, sep_);
} catch (FileNotFoundException e)
{
throw new NumericsException(e);
}
finally
{
if(is != null)
{
try
{
is.close();
} catch (IOException e){}
}
}
}
/**
* Reads a CSV resource from the specified input stream into a two-dimensional array, which is returned
*
* @param is_ The input stream to read
* @param sep_ The separator to use
*
* @return A two-dimensional array containing the data from the CSV resource
*/
public DoubleArray readCSV(InputStream is_, char sep_) {
DoubleArray parsedArray;
try
{
List allLines = readAllCSV(is_, sep_);
int rows = allLines.size();
int columns = 0;
if(rows > 0)
{
columns = allLines.get(0).length;
}
if(columns == 0)
{
throw new NumericsException("Badly formed file has no rows or columns");
}
parsedArray = getArrayFactory().newArray(rows,columns);
DoubleSettableIndexedIterator iterator = parsedArray.iterator();
for(int rowIndex = 0; rowIndex < rows; rowIndex++)
{
String[] currentRow = allLines.get(rowIndex);
if(currentRow.length != columns)
{
throw new NumericsException("Not all rows the same length");
}
for(int colIndex = 0; colIndex < columns; colIndex++)
{
double value = getArrayFactory().fromString(currentRow[colIndex]);
iterator.nextDouble();
iterator.set(value);
}
}
} catch (IOException e)
{
throw new NumericsException(e);
} catch (MultiDimensionalArrayException e)
{
throw new NumericsException(e);
}
return parsedArray;
}
/**
* Reads a CSV resource from the specified input stream into a two-dimensional array, which is returned
*
* @param is_ The input stream to read
* @param sep_ The separator to use
*
* @return A two-dimensional array containing the data from the CSV resource
*/
public IntegerArray readIntCSV(InputStream is_, char sep_)
{
IntegerArray parsedArray;
try
{
List allLines = readAllCSV(is_, sep_);
int rows = allLines.size();
int columns = 0;
if(rows > 0)
{
columns = allLines.get(0).length;
}
if(columns == 0)
{
throw new NumericsException("Badly formed file has no rows or columns");
}
parsedArray = newIntArray(rows,columns);
SettableIndexedIterator iterator = parsedArray.iterator();
for(int rowIndex = 0; rowIndex < rows; rowIndex++)
{
String[] currentRow = allLines.get(rowIndex);
if(currentRow.length != columns)
{
throw new NumericsException("Not all rows the same length");
}
for(int colIndex = 0; colIndex < columns; colIndex++)
{
Integer value = Integer.parseInt(currentRow[colIndex]);
iterator.next();
iterator.set(value);
}
}
} catch (IOException e)
{
throw new NumericsException(e);
} catch (MultiDimensionalArrayException e)
{
throw new NumericsException(e);
}
return parsedArray;
}
private List readAllCSV(InputStream is_, char sep_) throws IOException
{
Reader isReader = new InputStreamReader(is_);
CSVReader reader = new CSVReader(isReader,sep_);
List allLines;
try
{
allLines = reader.readAll();
}
finally
{
reader.close();
}
return allLines;
}
/**
* Returns a new array of integers with the given dimensions
*
* @param dimensions_ The required dimensions of the array
*
* @return An appropriately sized integer array
*/
public IntegerArray newIntArray(List dimensions_)
{
return new IntegerArray(getExecutor(), Utils.toIntArray(dimensions_));
}
/**
* Returns a new array of integers with the given dimensions
*
* @param dimensions_ The required dimensions of the array
*
* @return An appropriately sized integer array
*/
public IntegerArray newIntArray(int... dimensions_)
{
return new IntegerArray(getExecutor(), dimensions_);
}
// /**
// * Returns a new array of integers with the given dimensions
// *
// * @param dimensions_ The required dimensions of the array
// *
// * @return An appropriately sized integer array
// */
// public GenericArray newGenericArray(Class type_, int... dimensions_)
// {
// return new GenericArray(type_, getExecutor(), dimensions_ );
// }
/**
* @return The task executor of this numerics object
*/
public Executor getExecutor() {
return _executor;
}
}