/*
* Copyright 2011-2013, by Vladimir Kostyukov and Contributors.
*
* This file is part of la4j project (http://la4j.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Contributor(s): Wajdy Essam
*
*/
package org.la4j.matrix.dense;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Random;
import org.la4j.Matrices;
import org.la4j.Matrix;
import org.la4j.matrix.DenseMatrix;
import org.la4j.matrix.MatrixFactory;
import org.la4j.Vector;
import org.la4j.vector.dense.BasicVector;
public class Basic1DMatrix extends DenseMatrix {
private static final byte MATRIX_TAG = (byte) 0x00;
private double[] self;
public Basic1DMatrix() {
this(0, 0);
}
public Basic1DMatrix(int rows, int columns) {
this(rows, columns, new double[rows * columns]);
}
public Basic1DMatrix(int rows, int columns, double[] array) {
super(rows, columns);
this.self = array;
}
/**
* Creates a zero {@link Basic1DMatrix} of the given shape:
* {@code rows} x {@code columns}.
*/
public static Basic1DMatrix zero(int rows, int columns) {
return new Basic1DMatrix(rows, columns);
}
/**
* Creates a constant {@link Basic1DMatrix} of the given shape and {@code value}.
*/
public static Basic1DMatrix constant(int rows, int columns, double constant) {
double[] array = new double[rows * columns];
Arrays.fill(array, constant);
return new Basic1DMatrix(rows, columns, array);
}
/**
* Creates a diagonal {@link Basic1DMatrix} of the given {@code size} whose
* diagonal elements are equal to {@code diagonal}.
*/
public static Basic1DMatrix diagonal(int size, double diagonal) {
double[] array = new double[size * size];
for (int i = 0; i < size; i++) {
array[i * size + i] = diagonal;
}
return new Basic1DMatrix(size, size, array);
}
/**
* Creates an unit {@link Basic1DMatrix} of the given shape:
* {@code rows} x {@code columns}.
*/
public static Basic1DMatrix unit(int rows, int columns) {
return Basic1DMatrix.constant(rows, columns, 1.0);
}
/**
* Creates an identity {@link Basic1DMatrix} of the given {@code size}.
*/
public static Basic1DMatrix identity(int size) {
return Basic1DMatrix.diagonal(size, 1.0);
}
/**
* Creates a random {@link Basic1DMatrix} of the given shape:
* {@code rows} x {@code columns}.
*/
public static Basic1DMatrix random(int rows, int columns, Random random) {
double[] array = new double[rows * columns];
for (int i = 0; i < rows * columns; i++) {
array[i] = random.nextDouble();
}
return new Basic1DMatrix(rows, columns, array);
}
/**
* Creates a random symmetric {@link Basic1DMatrix} of the given {@code size}.
*/
public static Basic1DMatrix randomSymmetric(int size, Random random) {
double[] array = new double[size * size];
for (int i = 0; i < size; i++) {
for (int j = i; j < size; j++) {
double value = random.nextDouble();
array[i * size + j] = value;
array[j * size + i] = value;
}
}
return new Basic1DMatrix(size, size, array);
}
/**
* Creates a {@link Basic1DMatrix} of the given 1D {@code array} w/o
* copying the underlying array.
*/
public static Basic1DMatrix from1DArray(int rows, int columns, double[] array) {
return new Basic1DMatrix(rows, columns, array);
}
/**
* Creates a {@link Basic1DMatrix} of the given 2D {@code array} with
* copying the underlying array.
*/
public static Basic1DMatrix from2DArray(double[][] array) {
int rows = array.length;
int columns = array[0].length;
double[] array1D = new double[rows * columns];
int offset = 0;
for (int i = 0; i < rows; i++) {
System.arraycopy(array[i], 0, array1D, offset, columns);
offset += columns;
}
return new Basic1DMatrix(rows, columns, array1D);
}
/**
* Creates a block {@link Basic1DMatrix} of the given blocks {@code a},
* {@code b}, {@code c} and {@code d}.
*/
public static Basic1DMatrix block(Matrix a, Matrix b, Matrix c, Matrix d) {
if ((a.rows() != b.rows()) || (a.columns() != c.columns()) ||
(c.rows() != d.rows()) || (b.columns() != d.columns())) {
throw new IllegalArgumentException("Sides of blocks are incompatible!");
}
int rows = a.rows() + c.rows();
int columns = a.columns() + b.columns();
double[] array = new double[rows * columns];
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
if ((i < a.rows()) && (j < a.columns())) {
array[i * rows + j] = a.get(i, j);
}
if ((i < a.rows()) && (j > a.columns())) {
array[i * rows + j] = b.get(i, j);
}
if ((i > a.rows()) && (j < a.columns())) {
array[i * rows + j] = c.get(i, j);
}
if ((i > a.rows()) && (j > a.columns())) {
array[i * rows + j] = d.get(i, j);
}
}
}
return new Basic1DMatrix(rows, columns, array);
}
/**
* Decodes {@link Basic1DMatrix} from the given byte {@code array}.
*
* @param array the byte array representing a matrix
*
* @return a decoded matrix
*/
public static Basic1DMatrix fromBinary(byte[] array) {
ByteBuffer buffer = ByteBuffer.wrap(array);
if (buffer.get() != MATRIX_TAG) {
throw new IllegalArgumentException("Can not decode Basic1DMatrix from the given byte array.");
}
int rows = buffer.getInt();
int columns = buffer.getInt();
int capacity = rows * columns;
double[] values = new double[capacity];
for (int i = 0; i < capacity; i++) {
values[i] = buffer.getDouble();
}
return new Basic1DMatrix(rows, columns, values);
}
/**
* Parses {@link Basic1DMatrix} from the given CSV string.
*
* @param csv the CSV string representing a matrix
*
* @return a parsed matrix
*/
public static Basic1DMatrix fromCSV(String csv) {
return Matrix.fromCSV(csv).to(Matrices.BASIC_1D);
}
/**
* Parses {@link Basic1DMatrix} from the given Matrix Market string.
*
* @param mm the string in Matrix Market format
*
* @return a parsed matrix
*/
public static Basic1DMatrix fromMatrixMarket(String mm) {
return Matrix.fromMatrixMarket(mm).to(Matrices.BASIC_1D);
}
@Override
public double get(int i, int j) {
ensureIndexesAreInBounds(i, j);
return self[i * columns + j];
}
@Override
public void set(int i, int j, double value) {
ensureIndexesAreInBounds(i, j);
self[i * columns + j] = value;
}
@Override
public void setAll(double value) {
Arrays.fill(self, value);
}
@Override
public void swapRows(int i, int j) {
if (i != j) {
for (int k = 0; k < columns; k++) {
double tmp = self[i * columns + k];
self[i * columns + k] = self[j * columns + k];
self[j * columns + k] = tmp;
}
}
}
@Override
public void swapColumns(int i, int j) {
if (i != j) {
for (int k = 0; k < rows; k++) {
double tmp = self[k * columns + i];
self[k * columns + i] = self[k * columns + j];
self[k * columns + j] = tmp;
}
}
}
@Override
public Vector getRow(int i) {
double[] result = new double[columns];
System.arraycopy(self, i * columns , result, 0, columns);
return new BasicVector(result);
}
@Override
public Matrix copyOfShape(int rows, int columns) {
ensureDimensionsAreCorrect(rows, columns);
if (this.rows < rows && this.columns == columns) {
double[] $self = new double[rows * columns];
System.arraycopy(self, 0, $self, 0, this.rows * columns);
return new Basic1DMatrix(rows, columns, $self);
}
double[] $self = new double[rows * columns];
int columnSize = columns < this.columns ? columns : this.columns;
int rowSize = rows < this.rows ? rows : this.rows;
for (int i = 0; i < rowSize; i++) {
System.arraycopy(self, i * this.columns, $self, i * columns,
columnSize);
}
return new Basic1DMatrix(rows, columns, $self);
}
@Override
public double[][] toArray() {
double[][] result = new double[rows][columns];
int offset = 0;
for (int i = 0; i < rows; i++) {
System.arraycopy(self, offset, result[i], 0, columns);
offset += columns;
}
return result;
}
@Override
public <T extends Matrix> T to(MatrixFactory<T> factory) {
if (factory.outputClass == Basic1DMatrix.class) {
return factory.outputClass.cast(this);
}
return super.to(factory);
}
@Override
public Matrix blankOfShape(int rows, int columns) {
return Basic1DMatrix.zero(rows, columns);
}
@Override
public byte[] toBinary() {
int size = 1 + // 1 byte: class tag
4 + // 4 bytes: rows
4 + // 4 bytes: columns
(8 * rows * columns); // 8 * rows * columns bytes: values
ByteBuffer buffer = ByteBuffer.allocate(size);
buffer.put(MATRIX_TAG);
buffer.putInt(rows);
buffer.putInt(columns);
for (double value: self) {
buffer.putDouble(value);
}
return buffer.array();
}
}