001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.math4.legacy.linear;
018
019import org.apache.commons.math4.legacy.core.Field;
020import org.apache.commons.math4.legacy.core.FieldElement;
021
022/**
023 * Sparse matrix implementation based on an open addressed map.
024 *
025 * <p>
026 *  Caveat: This implementation assumes that, for any {@code x},
027 *  the equality {@code x * 0d == 0d} holds. But it is is not true for
028 *  {@code NaN}. Moreover, zero entries will lose their sign.
029 *  Some operations (that involve {@code NaN} and/or infinities) may
030 *  thus give incorrect results.
031 * </p>
032 * @param <T> the type of the field elements
033 * @since 2.0
034 */
035public class SparseFieldMatrix<T extends FieldElement<T>> extends AbstractFieldMatrix<T> {
036
037    /** Storage for (sparse) matrix elements. */
038    private final OpenIntToFieldHashMap<T> entries;
039    /** Row dimension. */
040    private final int rows;
041    /** Column dimension. */
042    private final int columns;
043
044    /**
045     * Create a matrix with no data.
046     *
047     * @param field Field to which the elements belong.
048     */
049    public SparseFieldMatrix(final Field<T> field) {
050        super(field);
051        rows = 0;
052        columns= 0;
053        entries = new OpenIntToFieldHashMap<>(field);
054    }
055
056    /**
057     * Create a new {@code SparseFieldMatrix<T>} with the supplied row and column
058     * dimensions.
059     *
060     * @param field Field to which the elements belong.
061     * @param rowDimension Number of rows in the new matrix.
062     * @param columnDimension Number of columns in the new matrix.
063     * @throws org.apache.commons.math4.legacy.exception.NotStrictlyPositiveException
064     * if row or column dimension is not positive.
065     */
066    public SparseFieldMatrix(final Field<T> field,
067                             final int rowDimension, final int columnDimension) {
068        super(field, rowDimension, columnDimension);
069        this.rows = rowDimension;
070        this.columns = columnDimension;
071        entries = new OpenIntToFieldHashMap<>(field);
072    }
073
074    /**
075     * Copy constructor.
076     *
077     * @param other Instance to copy.
078     */
079    public SparseFieldMatrix(SparseFieldMatrix<T> other) {
080        super(other.getField(), other.getRowDimension(), other.getColumnDimension());
081        rows = other.getRowDimension();
082        columns = other.getColumnDimension();
083        entries = new OpenIntToFieldHashMap<>(other.entries);
084    }
085
086    /**
087     * Generic copy constructor.
088     *
089     * @param other Instance to copy.
090     */
091    public SparseFieldMatrix(FieldMatrix<T> other){
092        super(other.getField(), other.getRowDimension(), other.getColumnDimension());
093        rows = other.getRowDimension();
094        columns = other.getColumnDimension();
095        entries = new OpenIntToFieldHashMap<>(getField());
096        for (int i = 0; i < rows; i++) {
097            for (int j = 0; j < columns; j++) {
098                setEntry(i, j, other.getEntry(i, j));
099            }
100        }
101    }
102
103    /** {@inheritDoc} */
104    @Override
105    public void addToEntry(int row, int column, T increment) {
106        checkRowIndex(row);
107        checkColumnIndex(column);
108        final int key = computeKey(row, column);
109        final T value = entries.get(key).add(increment);
110        if (getField().getZero().equals(value)) {
111            entries.remove(key);
112        } else {
113            entries.put(key, value);
114        }
115    }
116
117    /** {@inheritDoc} */
118    @Override
119    public FieldMatrix<T> copy() {
120        return new SparseFieldMatrix<>(this);
121    }
122
123    /** {@inheritDoc} */
124    @Override
125    public FieldMatrix<T> createMatrix(int rowDimension, int columnDimension) {
126        return new SparseFieldMatrix<>(getField(), rowDimension, columnDimension);
127    }
128
129    /** {@inheritDoc} */
130    @Override
131    public int getColumnDimension() {
132        return columns;
133    }
134
135    /** {@inheritDoc} */
136    @Override
137    public T getEntry(int row, int column) {
138        checkRowIndex(row);
139        checkColumnIndex(column);
140        return entries.get(computeKey(row, column));
141    }
142
143    /** {@inheritDoc} */
144    @Override
145    public int getRowDimension() {
146        return rows;
147    }
148
149    /** {@inheritDoc} */
150    @Override
151    public void multiplyEntry(int row, int column, T factor) {
152        checkRowIndex(row);
153        checkColumnIndex(column);
154        final int key = computeKey(row, column);
155        final T value = entries.get(key).multiply(factor);
156        if (getField().getZero().equals(value)) {
157            entries.remove(key);
158        } else {
159            entries.put(key, value);
160        }
161    }
162
163    /** {@inheritDoc} */
164    @Override
165    public void setEntry(int row, int column, T value) {
166        checkRowIndex(row);
167        checkColumnIndex(column);
168        if (getField().getZero().equals(value)) {
169            entries.remove(computeKey(row, column));
170        } else {
171            entries.put(computeKey(row, column), value);
172        }
173    }
174
175    /**
176     * Compute the key to access a matrix element.
177     *
178     * @param row Row index of the matrix element.
179     * @param column Column index of the matrix element.
180     * @return the key within the map to access the matrix element.
181     */
182    private int computeKey(int row, int column) {
183        return row * columns + column;
184    }
185}