/* * Concept profile generation tool suite * Copyright (C) 2015 Biosemantics Group, Erasmus University Medical Center, * Rotterdam, The Netherlands * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/> */ package org.erasmusmc.math.vector; import java.io.Serializable; import java.util.Comparator; import java.util.Iterator; import org.erasmusmc.collections.SortedListMap; import org.erasmusmc.math.space.Space; public class SparseVectorSingleFloat<D> extends Vector<D> implements Serializable { private static final long serialVersionUID = -7684412519029854775L; transient public SortedListMap<D, Float> values; transient public Space<D> space; public SparseVectorSingleFloat() { values = new SortedListMap<D, Float>(new SparseVectorKeyComparator()); } public SparseVectorSingleFloat(Space<D> space) { this.space = space; values = new SortedListMap<D, Float>(new SparseVectorKeyComparator()); } public SparseVectorSingleFloat(Vector<D> vector) { space = vector.getSpace(); values = new SortedListMap<D, Float>(new SparseVectorKeyComparator()); set(vector); } public double sparseInnerProduct(SparseVectorSingleFloat<D> other) { SortedListMap<D, Float> shorter = other.values; SortedListMap<D, Float> longer = values; if (shorter.size() > longer.size()) { shorter = longer; longer = other.values; } double innerproduct = 0f; int longerlowestIndex = 0; int longerhighestIndex = longer.size() - 1; int longerlowest = space.indexOfObject(longer.getKey(longerlowestIndex)); int longerhighest = space.indexOfObject(longer.getKey(longerhighestIndex)); int shorterlowestIndex = 0; int shorterhighestIndex = shorter.size() - 1; while (shorterlowestIndex <= shorterhighestIndex) { D key = shorter.getKey(shorterlowestIndex); int shorterlowest = space.indexOfObject(key); if (shorterlowest >= longerlowest) { int index = longer.guidedGetIndexForKey(key, longerlowestIndex, longerhighestIndex + 1); if (index < longer.size()) { longerlowestIndex = index; if (longer.getKey(index).equals(key)) { innerproduct += longer.getValue(index) * shorter.getValue(shorterlowestIndex); if (longerlowestIndex < longerhighestIndex - 1) longerlowestIndex++; } longerlowest = space.indexOfObject(longer.getKey(longerlowestIndex)); } } shorterlowestIndex++; if (shorterlowestIndex < shorterhighestIndex) { key = shorter.getKey(shorterhighestIndex); int shorterhighest = space.indexOfObject(key); if (shorterhighest <= longerhighest) { int index = longer.guidedGetIndexForKey(key, longerlowestIndex, longerhighestIndex + 1); if (index < longer.size()) { longerhighestIndex = index; if (longer.getKey(index).equals(key)) { innerproduct += longer.getValue(index) * shorter.getValue(shorterhighestIndex); if (longerlowestIndex < longerhighestIndex - 1) longerhighestIndex--; } longerhighest = space.indexOfObject(longer.getKey(longerhighestIndex)); } } shorterhighestIndex--; } } return innerproduct; } /** * superfancy high performance cosine function. Whips Any ass up to now. * * @param other * @return */ public double sparseCosine(SparseVectorSingleFloat<D> other) { double result = 0; double denominator = norm() * other.norm(); if (denominator > 0) { double innerproduct = sparseInnerProduct(other); result = new Double(innerproduct / denominator).floatValue(); } return result; } public double jaccard(SparseVectorSingleFloat<D> other) { double result = 0; double innerproduct = sparseInnerProduct(other); double denominator = getSquaredNorm() + other.getSquaredNorm() - innerproduct; if (denominator > 0) { result = new Double(innerproduct / denominator).floatValue(); } return result; } public void set(D index, double value) { if (value != 0d) values.put(index, new Double(value).floatValue()); else values.remove(index); } public void setFloat(D index, float value) { if (value != 0d) values.put(index, value); else values.remove(index); } public float getFloat(D index) { Float value = values.get(index); if (value != null) return value; else return 0; } public double get(D index) { Float value = values.get(index); if (value != null) return value.doubleValue(); else return 0; } public void set(Vector<D> vector) { values.clear(); VectorCursor<D> cursor = vector.getNonzeroCursor(); while (cursor.isValid()) { set(cursor.dimension(), cursor.get()); cursor.next(); } } public int getStoredValueCount() { return values.size(); } public VectorCursor<D> getCursor() { return new SparseVectorCursor(); } public VectorCursor<D> getNonzeroCursor() { return new SparseVectorNonzeroCursor(); } public VectorSlaveCursor<D> getSlaveCursor() { return new SparseVectorSlaveCursor(); } protected class SparseVectorHandle implements VectorHandle<D> { D dimension; public D dimension() { return dimension; } public int index() { return space.indexOfObject(dimension); } public double get() { Float value = values.get(dimension); if (value != null) return value.doubleValue(); else return 0; } public void set(double value) { if (value != 0d) values.put(dimension, new Double(value).floatValue()); else values.remove(dimension); } } protected class SparseVectorSlaveCursor extends SparseVectorHandle implements VectorSlaveCursor<D> { public void synchronize(VectorHandle<D> vectorHandle) { dimension = vectorHandle.dimension(); } } protected class SparseVectorCursor extends SparseVectorHandle implements VectorCursor<D> { protected Iterator<D> iterator; public SparseVectorCursor() { iterator = space.iterator(); next(); } public boolean isValid() { return dimension != null; } public void next() { if (iterator.hasNext()) dimension = iterator.next(); else dimension = null; } } protected class SparseVectorNonzeroCursor extends SparseVectorHandle implements VectorCursor<D>, Serializable { private static final long serialVersionUID = 2287253547250643918L; protected Iterator<D> iterator; public SparseVectorNonzeroCursor() { iterator = values.keyIterator(); next(); } public boolean isValid() { return dimension != null; } public void next() { if (iterator.hasNext()) dimension = iterator.next(); else dimension = null; } } public Space<D> getSpace() { return space; } public void setSpace(Space<D> space) { this.space = space; } public class SparseVectorKeyComparator implements Serializable, Comparator<D> { private static final long serialVersionUID = 1481833931150717597L; public int compare(D o1, D o2) { return space.indexOfObject(o1) - space.indexOfObject(o2); } } }