diff options
15 files changed, 178 insertions, 540 deletions
diff --git a/document/src/main/java/com/yahoo/document/json/JsonSerializationHelper.java b/document/src/main/java/com/yahoo/document/json/JsonSerializationHelper.java index 8c6ee898c0e..e1444c5145f 100644 --- a/document/src/main/java/com/yahoo/document/json/JsonSerializationHelper.java +++ b/document/src/main/java/com/yahoo/document/json/JsonSerializationHelper.java @@ -99,7 +99,7 @@ public class JsonSerializationHelper { private static void serializeTensorAddress(JsonGenerator generator, TensorAddress address, TensorType type) throws IOException { generator.writeObjectFieldStart(JsonReader.TENSOR_ADDRESS); for (int i = 0; i < type.dimensions().size(); i++) - generator.writeStringField(type.dimensions().get(i).name(), address.label(i)); + generator.writeStringField(type.dimensions().get(i).name(), address.labels().get(i)); generator.writeEndObject(); } diff --git a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/TensorValue.java b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/TensorValue.java index 6d038113691..6b67fca791d 100644 --- a/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/TensorValue.java +++ b/searchlib/src/main/java/com/yahoo/searchlib/rankingexpression/evaluation/TensorValue.java @@ -23,9 +23,16 @@ public class TensorValue extends Value { /** The tensor value of this */ private final Tensor value; - + private final Optional<TensorType> type; + public TensorValue(Tensor value) { this.value = value; + this.type = Optional.empty(); + } + + public TensorValue(Tensor value, TensorType type) { + this.value = value; + this.type = Optional.of(type); } @Override @@ -90,6 +97,10 @@ public class TensorValue extends Value { public Tensor asTensor() { return value; } + public Optional<TensorType> getType() { + return type; + } + @Override public Value compare(TruthOperator operator, Value argument) { return new TensorValue(compareTensor(operator, asTensor(argument, operator.toString()))); @@ -139,13 +150,18 @@ public class TensorValue extends Value { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - TensorValue other = (TensorValue) o; - return value.equals(other.value); + TensorValue that = (TensorValue) o; + + if (!type.equals(that.type)) return false; + if (!value.equals(that.value)) return false; + + return true; } @Override public int hashCode() { - return value.hashCode(); + int result = value.hashCode(); + result = 31 * result + type.hashCode(); + return result; } - } diff --git a/vespajlib/src/main/java/com/yahoo/tensor/IndexedTensor.java b/vespajlib/src/main/java/com/yahoo/tensor/IndexedTensor.java index 1ebd6c4179d..4091fd97a16 100644 --- a/vespajlib/src/main/java/com/yahoo/tensor/IndexedTensor.java +++ b/vespajlib/src/main/java/com/yahoo/tensor/IndexedTensor.java @@ -12,7 +12,6 @@ import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Optional; -import java.util.Set; /** * An indexed (dense) tensor backed by a double array. @@ -61,20 +60,6 @@ public class IndexedTensor implements Tensor { return new ValueIterator(); } - /** - * Returns an iterator over value iterators where the outer iterator is over each unique value of the dimensions - * given and the inner iterator is over each unique value of the rest of the dimensions, in the same order as - * other iterator. - * - * @param dimensions the names of the dimensions of the superspace - * @param dimensionSizes the size of each dimension in the space we are returning values for, containing - * one value per dimension of this tensor (in order). Each size may be the same or smaller - * than the corresponding size of this tensor - */ - public Iterator<SubspaceIterator> subspaceIterator(Set<String> dimensions, int[] dimensionSizes) { - return new SuperspaceIterator(dimensions, dimensionSizes); - } - /** * Returns the value at the given indexes * @@ -97,12 +82,7 @@ public class IndexedTensor implements Tensor { return Double.NaN; } } - - /** Returns the value at these indexes */ - private double get(Indexes indexes) { - return values[toValueIndex(indexes.indexesForReading(), dimensionSizes)]; - } - + private static int toValueIndex(int[] indexes, int[] dimensionSizes) { if (indexes.length == 1) return indexes[0]; // for speed if (indexes.length == 0) return 0; // for speed @@ -112,13 +92,13 @@ public class IndexedTensor implements Tensor { valueIndex += productOfDimensionsAfter(i, dimensionSizes) * indexes[i]; return valueIndex; } - + private static int toValueIndex(TensorAddress address, int[] dimensionSizes) { - if (address.isEmpty()) return 0; + if (address.labels().isEmpty()) return 0; int valueIndex = 0; - for (int i = 0; i < address.size(); i++) - valueIndex += productOfDimensionsAfter(i, dimensionSizes) * address.intLabel(i); + for (int i = 0; i < address.labels().size(); i++) + valueIndex += productOfDimensionsAfter(i, dimensionSizes) * Integer.parseInt(address.labels().get(i)); return valueIndex; } @@ -133,11 +113,11 @@ public class IndexedTensor implements Tensor { public TensorType type() { return type; } /** - * Returns the length of this in the nth dimension + * Returns the lenght of this in the nth dimension * * @throws IndexOutOfBoundsException if the index is larger than the number of dimensions in this tensor minus one */ - public int size(int dimension) { + public int length(int dimension) { return dimensionSizes[dimension]; } @@ -147,15 +127,30 @@ public class IndexedTensor implements Tensor { return values.length == 0 ? Collections.emptyMap() : Collections.singletonMap(TensorAddress.empty, values[0]); ImmutableMap.Builder<TensorAddress, Double> builder = new ImmutableMap.Builder<>(); - Indexes indexes = new Indexes(dimensionSizes, values.length); + int[] tensorIndexes = new int[dimensionSizes.length]; for (int i = 0; i < values.length; i++) { - builder.put(indexes.toAddress(), values[i]); + builder.put(new TensorAddress(tensorIndexes), values[i]); if (i < values.length -1) - indexes.next(); + next(tensorIndexes); } return builder.build(); } + private void next(int[] tensorIndexes) { + nextRecursively(tensorIndexes.length - 1, tensorIndexes); + } + + // TODO: Tail recursion -> loop + private void nextRecursively(int dimension, int[] tensorIndexes) { + if (tensorIndexes[dimension] + 1 == dimensionSizes[dimension]) { + tensorIndexes[dimension] = 0; + nextRecursively(dimension - 1, tensorIndexes); + } + else { + tensorIndexes[dimension]++; + } + } + @Override public int hashCode() { return Arrays.hashCode(values); } @@ -195,9 +190,9 @@ public class IndexedTensor implements Tensor { " for " + type); for (int i = 0; i < dimensionSizes.length; i++ ) { Optional<Integer> size = type.dimensions().get(i).size(); - if (size.isPresent() && size.get() < dimensionSizes[i]) - throw new IllegalArgumentException("Size of " + type.dimensions() + " is " + dimensionSizes[i] + - " but cannot be larger than " + size.get()); + if (size.isPresent() && size.get() != dimensionSizes[i]) + throw new IllegalArgumentException("Size of " + type.dimensions() + " must be " + size.get() + + ", not " + dimensionSizes[i]); } return new BoundBuilder(type, dimensionSizes); @@ -347,9 +342,14 @@ public class IndexedTensor implements Tensor { @Override public Builder cell(TensorAddress address, double value) { - int[] indexes = new int[address.size()]; - for (int i = 0; i < address.size(); i++) { - indexes[i] = address.intLabel(i); + int[] indexes = new int[address.labels().size()]; + for (int i = 0; i < address.labels().size(); i++) { + try { + indexes[i] = Integer.parseInt(address.labels().get(i)); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Labels in an indexed tensor must be integers, not '" + + address.labels().get(i) + "'"); + } } cell(value, indexes); return this; @@ -397,251 +397,71 @@ public class IndexedTensor implements Tensor { } - // TODO: Generalize to vector cell iterator? private final class CellIterator implements Iterator<Map.Entry<TensorAddress, Double>> { - private int count = 0; - private final Indexes indexes = new Indexes(dimensionSizes, values.length); + private int cursor = 0; + private final int[] tensorIndexes = new int[dimensionSizes.length]; @Override public boolean hasNext() { - return count < indexes.size(); + return cursor < values.length; } @Override public Map.Entry<TensorAddress, Double> next() { - if ( ! hasNext()) throw new NoSuchElementException("No cell at " + indexes); + if ( ! hasNext()) throw new NoSuchElementException(); - Map.Entry<TensorAddress, Double> current = new Cell(indexes.toAddress(), get(indexes)); + Map.Entry<TensorAddress, Double> current = new Cell(new TensorAddress(tensorIndexes), values[cursor]); - count++; + cursor++; if (hasNext()) - indexes.next(); + IndexedTensor.this.next(tensorIndexes); return current; } - } - - private class Cell implements Map.Entry<TensorAddress, Double> { - - private final TensorAddress address; - private final Double value; + private class Cell implements Map.Entry<TensorAddress, Double> { - private Cell(TensorAddress address, Double value) { - this.address = address; - this.value = value; - } + private final TensorAddress address; + private final Double value; + + private Cell(TensorAddress address, Double value) { + this.address = address; + this.value = value; + } + + @Override + public TensorAddress getKey() { return address; } - @Override - public TensorAddress getKey() { return address; } + @Override + public Double getValue() { return value; } - @Override - public Double getValue() { return value; } + @Override + public Double setValue(Double value) { + throw new UnsupportedOperationException("A tensor cannot be modified"); + } - @Override - public Double setValue(Double value) { - throw new UnsupportedOperationException("A tensor cannot be modified"); } } private final class ValueIterator implements Iterator<Double> { - private int count = 0; + private int cursor = 0; @Override public boolean hasNext() { - return count < values.length; + return cursor < values.length; } @Override public Double next() { try { - return values[count++]; + return values[cursor++]; } catch (IndexOutOfBoundsException e) { - throw new NoSuchElementException("No element at position " + count); - } - } - - } - - private final class SuperspaceIterator implements Iterator<SubspaceIterator> { - - private final Indexes superindexes; - - /** true at indexes whose dimension subspaces iterate over */ - private final boolean[] subdimensionIndexes; - - /** - * The sizes of the space we'll return values of, one value for each dimension of this tensor, - * which may be equal to or smaller than the sizes of this tensor - */ - private final int[] dimensionSizes; - - private int count = 0; - - private SuperspaceIterator(Set<String> superdimensionNames, int[] dimensionSizes) { - this.dimensionSizes = dimensionSizes; - - boolean[] superdimensionIndexes = new boolean[dimensionSizes.length]; // for outer iterator - subdimensionIndexes = new boolean [dimensionSizes.length]; // for inner iterator - for (int i = 0; i < type.dimensions().size(); i++ ) { - boolean superDimension = superdimensionNames.contains(type.dimensions().get(i).name()); - superdimensionIndexes[i] = superDimension; - subdimensionIndexes[i] = ! superDimension; + throw new NoSuchElementException("No element at position " + cursor); } - - superindexes = new Indexes(dimensionSizes, superdimensionIndexes); - } - - @Override - public boolean hasNext() { - return count < superindexes.size(); - } - - @Override - public SubspaceIterator next() { - if ( ! hasNext()) throw new NoSuchElementException("No cell at " + superindexes); - SubspaceIterator subspace = new SubspaceIterator(subdimensionIndexes, superindexes.indexesCopy(), dimensionSizes); - count++; - if (hasNext()) - superindexes.next(); - return subspace; - } - - } - - /** - * An iterator over a subspace of this tensor. This is exposed to allow clients to query the size. - */ - public final class SubspaceIterator implements Iterator<Map.Entry<TensorAddress, Double>> { - - private final Indexes indexes; - private int count = 0; - - /** - * Creates a new subspace iterator - * - * @param dimensionIndexes a boolean array with a true entry for dimensions we should iterate over and false - * entries for all other dimensions - * @param address the address of the first cell of this subspace. - */ - private SubspaceIterator(boolean[] dimensionIndexes, int[] address, int[] dimensionSizes) { - this.indexes = new Indexes(dimensionSizes, dimensionIndexes, address); - } - - /** Returns the total number of cells in this subspace */ - public int size() { - return indexes.size(); - } - - @Override - public boolean hasNext() { return count < indexes.size(); } - - @Override - public Map.Entry<TensorAddress, Double> next() { - if ( ! hasNext()) throw new NoSuchElementException("No cell at " + indexes); - - Map.Entry<TensorAddress, Double> current = new Cell(indexes.toAddress(), get(indexes)); - - count++; - if (hasNext()) - indexes.next(); - - return current; - } - - } - - /** An array of indexes into this tensor which are able to find the next index in the value order */ - private static class Indexes { - - private final int size; - private final int[] indexes; - - private final int[] dimensionSizes; - - /** Only mutate (take next in) the dimension indexes which are true */ - private final boolean[] iteratingDimensions; - - private Indexes(int[] dimensionSizes, int size) { - this(dimensionSizes, trueArray(dimensionSizes.length), size); - } - - private Indexes(int[] dimensionSizes, boolean[] iteratingDimensions) { - this(dimensionSizes, iteratingDimensions, computeSize(dimensionSizes, iteratingDimensions)); - } - - private Indexes(int[] dimensionSizes, boolean[] iteratingDimensionIndexes, int size) { - this(dimensionSizes, iteratingDimensionIndexes, new int[dimensionSizes.length], size); - } - - private Indexes(int[] dimensionSizes, boolean[] iteratingDimensions, int[] initialIndexes) { - this(dimensionSizes, iteratingDimensions, initialIndexes, computeSize(dimensionSizes, iteratingDimensions)); - } - - private Indexes(int[] dimensionSizes, boolean[] iteratingDimensions, int[] initialIndexes, int size) { - this.dimensionSizes = dimensionSizes; - this.iteratingDimensions = iteratingDimensions; - this.indexes = initialIndexes; - this.size = size; - } - - private static boolean[] trueArray(int size) { - boolean[] array = new boolean[size]; - Arrays.fill(array, true); - return array; - } - - private static int computeSize(int[] dimensionSizes, boolean[] iteratingDimensions) { - int size = 1; - for (int dimensionIndex = 0; dimensionIndex < dimensionSizes.length; dimensionIndex++) - if (iteratingDimensions[dimensionIndex]) - size *= dimensionSizes[dimensionIndex]; - return size; - } - - private static boolean anyTrue(boolean[] values) { - for (boolean value : values) - if (value) return true; - return false; - } - - /** Returns the number of values this will iterate over - i.e the product if the iterating dimension sizes */ - public int size() { - return size; - } - - private void next() { - int currentDimension = indexes.length - 1; - while ( ! iteratingDimensions[currentDimension] || - indexes[currentDimension] + 1 == dimensionSizes[currentDimension]) { - if ( iteratingDimensions[currentDimension]) - indexes[currentDimension--] = 0; // carry over - else // leave this dimension as-is - currentDimension--; - } - indexes[currentDimension]++; - } - - /** Returns the address of the current position of these indexes */ - private TensorAddress toAddress() { - // TODO: We may avoid the array copy by issuing a one-time-use address? - return TensorAddress.of(indexes); - } - - private int[] indexesCopy() { - return Arrays.copyOf(indexes, indexes.length); - } - - /** Returns a copy of the indexes of this which must not be modified */ - private int[] indexesForReading() { return indexes; } - - @Override - public String toString() { - return "indexes " + Arrays.toString(indexes); } } diff --git a/vespajlib/src/main/java/com/yahoo/tensor/MappedTensor.java b/vespajlib/src/main/java/com/yahoo/tensor/MappedTensor.java index 6e169b8347f..f87a97cb44e 100644 --- a/vespajlib/src/main/java/com/yahoo/tensor/MappedTensor.java +++ b/vespajlib/src/main/java/com/yahoo/tensor/MappedTensor.java @@ -81,7 +81,7 @@ public class MappedTensor implements Tensor { @Override public Builder cell(double value, int... labels) { - cells.put(TensorAddress.of(labels), value); + cells.put(new TensorAddress(labels), value); return this; } diff --git a/vespajlib/src/main/java/com/yahoo/tensor/Tensor.java b/vespajlib/src/main/java/com/yahoo/tensor/Tensor.java index 6f655fd5860..260c48ace7f 100644 --- a/vespajlib/src/main/java/com/yahoo/tensor/Tensor.java +++ b/vespajlib/src/main/java/com/yahoo/tensor/Tensor.java @@ -73,6 +73,8 @@ public interface Tensor { if (type().dimensions().size() > 0) throw new IllegalStateException("This tensor is not dimensionless. Dimensions: " + type().dimensions().size()); if (size() == 0) return Double.NaN; + if (size() > 1) + throw new IllegalStateException("This tensor does not have a single value, it has " + size()); return valueIterator().next(); } @@ -211,7 +213,9 @@ public interface Tensor { /** Returns true if the two given tensors are mathematically equivalent, that is whether both have the same content */ static boolean equals(Tensor a, Tensor b) { - return a == b || a.cells().equals(b.cells()); + if (a == b) return true; + if ( ! a.cells().equals(b.cells())) return false; + return true; } // ----------------- Factories @@ -246,8 +250,9 @@ public interface Tensor { } interface Builder { - + /** Creates a suitable builder for the given type */ + // TODO: Create version of this which takes size info and use it when possible static Builder of(TensorType type) { boolean containsIndexed = type.dimensions().stream().anyMatch(d -> d.isIndexed()); boolean containsMapped = type.dimensions().stream().anyMatch( d -> ! d.isIndexed()); @@ -258,19 +263,7 @@ public interface Tensor { else // indexed or empty return IndexedTensor.Builder.of(type); } - - /** Creates a suitable builder for the given type */ - static Builder of(TensorType type, int[] dimensionSizes) { - boolean containsIndexed = type.dimensions().stream().anyMatch(d -> d.isIndexed()); - boolean containsMapped = type.dimensions().stream().anyMatch( d -> ! d.isIndexed()); - if (containsIndexed && containsMapped) - throw new IllegalArgumentException("Combining indexed and mapped dimensions is not supported yet"); - if (containsMapped) - return MappedTensor.Builder.of(type); - else // indexed or empty - return IndexedTensor.Builder.of(type, dimensionSizes); - } - + /** Returns the type this is building */ TensorType type(); diff --git a/vespajlib/src/main/java/com/yahoo/tensor/TensorAddress.java b/vespajlib/src/main/java/com/yahoo/tensor/TensorAddress.java index 5224d9632ec..abcaee2dfa5 100644 --- a/vespajlib/src/main/java/com/yahoo/tensor/TensorAddress.java +++ b/vespajlib/src/main/java/com/yahoo/tensor/TensorAddress.java @@ -20,73 +20,55 @@ import java.util.Set; * @author bratseth */ @Beta -public abstract class TensorAddress implements Comparable<TensorAddress> { +public final class TensorAddress implements Comparable<TensorAddress> { public static final TensorAddress empty = new TensorAddress.Builder(TensorType.empty).build(); - public static TensorAddress of(String[] labels) { - return new StringTensorAddress(labels); + private final ImmutableList<String> labels; + + public TensorAddress(String ... labels) { + this.labels = ImmutableList.copyOf(labels); } - public static TensorAddress of(int ... labels) { - return new IntTensorAddress(labels); + public TensorAddress(int ... labels) { + ImmutableList.Builder<String> builder = new ImmutableList.Builder<>(); + for (int label : labels) + builder.add(String.valueOf(label)); + this.labels = builder.build(); } - /** Returns the number of labels in this */ - public abstract int size(); - - /** - * Returns the i'th label in this - * - * @throws IllegalArgumentException if there is no label at this index - */ - public abstract String label(int i); - - /** - * Returns the i'th label in this as an int. - * Prefer this if you know that this is an integer address, but not otherwise. - * - * @throws IllegalArgumentException if there is no label at this index - */ - public abstract int intLabel(int i); - - public final boolean isEmpty() { return size() == 0; } + /** Returns the labels of this as an immutable list in the order of the dimensions of the tensor type of this */ + public List<String> labels() { return labels; } @Override public int compareTo(TensorAddress other) { - // TODO: Formal issue (only): Ordering with different address sizes - for (int i = 0; i < size(); i++) { - int elementComparison = this.label(i).compareTo(other.label(i)); + for (int i = 0; i < labels.size(); i++) { + int elementComparison = this.labels.get(i).compareTo(other.labels.get(i)); if (elementComparison != 0) return elementComparison; } return 0; } @Override - public int hashCode() { - int result = 1; - for (int i = 0; i < size(); i++) - result = 31 * result + label(i).hashCode(); - return result; + public int hashCode() { return labels.hashCode(); } + + @Override + public boolean equals(Object other) { + if (other == this) return true; + if ( ! (other instanceof TensorAddress)) return false; + return ((TensorAddress)other).labels.equals(this.labels); } @Override - public boolean equals(Object o) { - if (o == this) return true; - if ( ! (o instanceof TensorAddress)) return false; - TensorAddress other = (TensorAddress)o; - if (other.size() != this.size()) return false; - for (int i = 0; i < this.size(); i++) - if ( ! this.label(i).equals(other.label(i))) - return false; - return true; + public String toString() { + return labels.toString(); } - /** Returns this as a string on the appropriate form given the type */ - public final String toString(TensorType type) { + /** Returns this on the appropriate form given the type */ + public String toString(TensorType type) { StringBuilder b = new StringBuilder("{"); - for (int i = 0; i < size(); i++) { - b.append(type.dimensions().get(i).name()).append(":").append(label(i)); + for (int i = 0; i < labels.size(); i++) { + b.append(type.dimensions().get(i).name()).append(":").append(labels.get(i)); b.append(","); } if (b.length() > 1) @@ -95,61 +77,6 @@ public abstract class TensorAddress implements Comparable<TensorAddress> { return b.toString(); } - private static final class StringTensorAddress extends TensorAddress { - - private final String[] labels; - - private StringTensorAddress(String ... labels) { - this.labels = Arrays.copyOf(labels, labels.length); - } - - @Override - public int size() { return labels.length; } - - @Override - public String label(int i) { return labels[i]; } - - @Override - public int intLabel(int i) { - try { - return Integer.parseInt(labels[i]); - } - catch (NumberFormatException e) { - throw new IllegalArgumentException("Expected an int label in " + this + " at position " + i); - } - } - - @Override - public String toString() { - return Arrays.toString(labels); - } - - } - - private static final class IntTensorAddress extends TensorAddress { - - private final int[] labels; - - private IntTensorAddress(int ... labels) { - this.labels = Arrays.copyOf(labels, labels.length); - } - - @Override - public int size() { return labels.length; } - - @Override - public String label(int i) { return String.valueOf(labels[i]); } - - @Override - public int intLabel(int i) { return labels[i]; } - - @Override - public String toString() { - return Arrays.toString(labels); - } - - } - /** Supports building of a tensor address */ public static class Builder { @@ -190,7 +117,7 @@ public abstract class TensorAddress implements Comparable<TensorAddress> { if (labels[i] == null) throw new IllegalArgumentException("Missing a value for dimension " + type.dimensions().get(i).name() + " for " + type); - return TensorAddress.of(labels); + return new TensorAddress(labels); } } diff --git a/vespajlib/src/main/java/com/yahoo/tensor/TensorType.java b/vespajlib/src/main/java/com/yahoo/tensor/TensorType.java index e829f4c909b..0c726efc047 100644 --- a/vespajlib/src/main/java/com/yahoo/tensor/TensorType.java +++ b/vespajlib/src/main/java/com/yahoo/tensor/TensorType.java @@ -53,9 +53,6 @@ public class TensorType { return TensorTypeParser.fromSpec(specString); } - /** Returns true if all dimensions of this are indexed */ - public boolean isIndexed() { return dimensions().stream().allMatch(d -> d.isIndexed()); } - private static final boolean supportsMixedTypes = false; /** @@ -179,7 +176,7 @@ public class TensorType { // both are indexed bound IndexedBoundDimension thisIb = (IndexedBoundDimension)this; IndexedBoundDimension otherIb = (IndexedBoundDimension)other.get(); - return thisIb.size().get() < otherIb.size().get() ? thisIb : otherIb; + return thisIb.size().get() > otherIb.size().get() ? thisIb : otherIb; } @Override diff --git a/vespajlib/src/main/java/com/yahoo/tensor/functions/Join.java b/vespajlib/src/main/java/com/yahoo/tensor/functions/Join.java index 6128611302f..19b4ad39af3 100644 --- a/vespajlib/src/main/java/com/yahoo/tensor/functions/Join.java +++ b/vespajlib/src/main/java/com/yahoo/tensor/functions/Join.java @@ -2,20 +2,18 @@ package com.yahoo.tensor.functions; import com.google.common.annotations.Beta; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.yahoo.tensor.IndexedTensor; +import com.yahoo.tensor.MappedTensor; import com.yahoo.tensor.Tensor; import com.yahoo.tensor.TensorAddress; import com.yahoo.tensor.TensorType; import com.yahoo.tensor.evaluation.EvaluationContext; -import java.util.Arrays; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Optional; -import java.util.Set; import java.util.function.DoubleBinaryOperator; /** @@ -71,7 +69,7 @@ public class Join extends PrimitiveTensorFunction { TensorType joinedType = a.type().combineWith(b.type()); // Choose join algorithm - if (hasSingleIndexedDimension(a) && hasSingleIndexedDimension(b) && a.type().dimensions().get(0).name().equals(b.type().dimensions().get(0).name())) + if (a.type().equals(b.type()) && a.type().dimensions().size() == 1 && a.type().dimensions().get(0).isIndexed()) return indexedVectorJoin((IndexedTensor)a, (IndexedTensor)b, joinedType); else if (joinedType.dimensions().size() == a.type().dimensions().size() && joinedType.dimensions().size() == b.type().dimensions().size()) return singleSpaceJoin(a, b, joinedType); @@ -83,12 +81,8 @@ public class Join extends PrimitiveTensorFunction { return generalJoin(a, b, joinedType); } - private boolean hasSingleIndexedDimension(Tensor tensor) { - return tensor.type().dimensions().size() == 1 && tensor.type().dimensions().get(0).isIndexed(); - } - private Tensor indexedVectorJoin(IndexedTensor a, IndexedTensor b, TensorType type) { - int joinedLength = Math.min(a.size(0), b.size(0)); + int joinedLength = Math.min(a.length(0), b.length(0)); Iterator<Double> aIterator = a.valueIterator(); Iterator<Double> bIterator = b.valueIterator(); IndexedTensor.Builder builder = IndexedTensor.Builder.of(type, new int[] { joinedLength}); @@ -111,42 +105,6 @@ public class Join extends PrimitiveTensorFunction { /** Join a tensor into a superspace */ private Tensor subspaceJoin(Tensor subspace, Tensor superspace, TensorType joinedType, boolean reversedArgumentOrder) { - if (subspace.type().isIndexed() && superspace.type().isIndexed()) - return indexedSubspaceJoin((IndexedTensor) subspace, (IndexedTensor) superspace, joinedType, reversedArgumentOrder); - else - return generalSubspaceJoin(subspace, superspace, joinedType, reversedArgumentOrder); - } - - private Tensor indexedSubspaceJoin(IndexedTensor subspace, IndexedTensor superspace, TensorType joinedType, boolean reversedArgumentOrder) { - if (subspace.size() == 0 || superspace.size() == 0) // special case empty here to avoid doing it when finding sizes - return Tensor.Builder.of(joinedType, new int[joinedType.dimensions().size()]).build(); - - // Find size of joined tensor - int[] joinedSizes = new int[joinedType.dimensions().size()]; - for (int i = 0; i < joinedSizes.length; i++) { - Optional<Integer> subspaceIndex = subspace.type().indexOfDimension(joinedType.dimensions().get(i).name()); - if (subspaceIndex.isPresent()) - joinedSizes[i] = Math.min(superspace.size(i), subspace.size(subspaceIndex.get())); - else - joinedSizes[i] = superspace.size(i); - } - - Tensor.Builder builder = Tensor.Builder.of(joinedType, joinedSizes); - - // Find dimensions which are only in the supertype - Set<String> superDimensionNames = new HashSet<>(superspace.type().dimensionNames()); - superDimensionNames.removeAll(subspace.type().dimensionNames()); - - for (Iterator<IndexedTensor.SubspaceIterator> i = superspace.subspaceIterator(superDimensionNames, joinedSizes); i.hasNext(); ) { - IndexedTensor.SubspaceIterator subspaceInSuper = i.next(); - joinSubspaces(subspace.valueIterator(), subspace.size(), - subspaceInSuper, subspaceInSuper.size(), - reversedArgumentOrder, builder); - } - return builder.build(); - } - - private Tensor generalSubspaceJoin(Tensor subspace, Tensor superspace, TensorType joinedType, boolean reversedArgumentOrder) { int[] subspaceIndexes = subspaceIndexes(superspace.type(), subspace.type()); Tensor.Builder builder = Tensor.Builder.of(joinedType); for (Iterator<Map.Entry<TensorAddress, Double>> i = superspace.cellIterator(); i.hasNext(); ) { @@ -154,26 +112,13 @@ public class Join extends PrimitiveTensorFunction { TensorAddress subaddress = mapAddressToSubspace(supercell.getKey(), subspaceIndexes); double subspaceValue = subspace.get(subaddress); if ( ! Double.isNaN(subspaceValue)) - builder.cell(supercell.getKey(), + builder.cell(supercell.getKey(), reversedArgumentOrder ? combinator.applyAsDouble(supercell.getValue(), subspaceValue) : combinator.applyAsDouble(subspaceValue, supercell.getValue())); } return builder.build(); } - - private void joinSubspaces(Iterator<Double> subspace, int subspaceSize, - Iterator<Map.Entry<TensorAddress, Double>> superspace, int superspaceSize, - boolean reversedArgumentOrder, Tensor.Builder builder) { - int joinedLength = Math.min(subspaceSize, superspaceSize); - for (int i = 0; i < joinedLength; i++) { - Double subvalue = subspace.next(); - Map.Entry<TensorAddress, Double> supercell = superspace.next(); - builder.cell(supercell.getKey(), - reversedArgumentOrder ? combinator.applyAsDouble(supercell.getValue(), subvalue) - : combinator.applyAsDouble(subvalue, supercell.getValue())); - } - } - + /** Returns the indexes in the superspace type which should be retained to create the subspace type */ private int[] subspaceIndexes(TensorType supertype, TensorType subtype) { int[] subspaceIndexes = new int[subtype.dimensions().size()]; @@ -185,8 +130,8 @@ public class Join extends PrimitiveTensorFunction { private TensorAddress mapAddressToSubspace(TensorAddress superAddress, int[] subspaceIndexes) { String[] subspaceLabels = new String[subspaceIndexes.length]; for (int i = 0; i < subspaceIndexes.length; i++) - subspaceLabels[i] = superAddress.label(subspaceIndexes[i]); - return TensorAddress.of(subspaceLabels); + subspaceLabels[i] = superAddress.labels().get(subspaceIndexes[i]); + return new TensorAddress(subspaceLabels); } /** Slow join which works for any two tensors */ @@ -224,10 +169,10 @@ public class Join extends PrimitiveTensorFunction { private TensorAddress combineAddresses(TensorAddress a, int[] aToIndexes, TensorAddress b, int[] bToIndexes, TensorType joinedType) { String[] joinedLabels = new String[joinedType.dimensions().size()]; - mapContent(a, joinedLabels, aToIndexes); - boolean compatible = mapContent(b, joinedLabels, bToIndexes); + mapContent(a.labels(), joinedLabels, aToIndexes); + boolean compatible = mapContent(b.labels(), joinedLabels, bToIndexes); if ( ! compatible) return null; - return TensorAddress.of(joinedLabels); + return new TensorAddress(joinedLabels); } /** @@ -236,11 +181,11 @@ public class Join extends PrimitiveTensorFunction { * @return true if the mapping was successful, false if one of the destination positions was * occupied by a different value */ - private boolean mapContent(TensorAddress from, String[] to, int[] indexMap) { + private boolean mapContent(List<String> from, String[] to, int[] indexMap) { for (int i = 0; i < from.size(); i++) { int toIndex = indexMap[i]; - if (to[toIndex] != null && ! to[toIndex].equals(from.label(i))) return false; - to[toIndex] = from.label(i); + if (to[toIndex] != null && ! to[toIndex].equals(from.get(i))) return false; + to[toIndex] = from.get(i); } return true; } diff --git a/vespajlib/src/main/java/com/yahoo/tensor/functions/Reduce.java b/vespajlib/src/main/java/com/yahoo/tensor/functions/Reduce.java index d528b4d3dc5..7595d18fb18 100644 --- a/vespajlib/src/main/java/com/yahoo/tensor/functions/Reduce.java +++ b/vespajlib/src/main/java/com/yahoo/tensor/functions/Reduce.java @@ -2,7 +2,9 @@ package com.yahoo.tensor.functions; import com.google.common.annotations.Beta; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.yahoo.tensor.IndexedTensor; +import com.yahoo.tensor.MappedTensor; import com.yahoo.tensor.Tensor; import com.yahoo.tensor.TensorAddress; import com.yahoo.tensor.TensorType; @@ -132,10 +134,10 @@ public class Reduce extends PrimitiveTensorFunction { String[] reducedLabels = new String[reducedType.dimensions().size()]; int reducedLabelIndex = 0; - for (int i = 0; i < address.size(); i++) + for (int i = 0; i < address.labels().size(); i++) if ( ! indexesToRemove.contains(i)) - reducedLabels[reducedLabelIndex++] = address.label(i); - return TensorAddress.of(reducedLabels); + reducedLabels[reducedLabelIndex++] = address.labels().get(i); + return new TensorAddress(reducedLabels); } private Tensor reduceAllGeneral(Tensor argument) { @@ -147,7 +149,7 @@ public class Reduce extends PrimitiveTensorFunction { private Tensor reduceIndexedVector(IndexedTensor argument) { ValueAggregator valueAggregator = ValueAggregator.ofType(aggregator); - for (int i = 0; i < argument.size(0); i++) + for (int i = 0; i < argument.length(0); i++) valueAggregator.aggregate(argument.get(i)); return Tensor.Builder.of(TensorType.empty).cell((valueAggregator.aggregatedValue())).build(); } diff --git a/vespajlib/src/main/java/com/yahoo/tensor/functions/Rename.java b/vespajlib/src/main/java/com/yahoo/tensor/functions/Rename.java index 29dcb218843..aabe38d1824 100644 --- a/vespajlib/src/main/java/com/yahoo/tensor/functions/Rename.java +++ b/vespajlib/src/main/java/com/yahoo/tensor/functions/Rename.java @@ -89,8 +89,8 @@ public class Rename extends PrimitiveTensorFunction { private TensorAddress rename(TensorAddress address, int[] toIndexes) { String[] reorderedLabels = new String[toIndexes.length]; for (int i = 0; i < toIndexes.length; i++) - reorderedLabels[toIndexes[i]] = address.label(i); - return TensorAddress.of(reorderedLabels); + reorderedLabels[toIndexes[i]] = address.labels().get(i); + return new TensorAddress(reorderedLabels); } @Override diff --git a/vespajlib/src/main/java/com/yahoo/tensor/serialization/SparseBinaryFormat.java b/vespajlib/src/main/java/com/yahoo/tensor/serialization/SparseBinaryFormat.java index 8e15a994ed9..cb9a93fd233 100644 --- a/vespajlib/src/main/java/com/yahoo/tensor/serialization/SparseBinaryFormat.java +++ b/vespajlib/src/main/java/com/yahoo/tensor/serialization/SparseBinaryFormat.java @@ -48,8 +48,9 @@ class SparseBinaryFormat implements BinaryFormat { } private static void encodeAddress(GrowableByteBuffer buffer, TensorAddress address) { - for (int i = 0; i < address.size(); i++) - encodeString(buffer, address.label(i)); + for (String label : address.labels()) { + encodeString(buffer, label); + } } private static void encodeString(GrowableByteBuffer buffer, String value) { diff --git a/vespajlib/src/test/java/com/yahoo/tensor/IndexedTensorTestCase.java b/vespajlib/src/test/java/com/yahoo/tensor/IndexedTensorTestCase.java index 9183ad68956..5e91679c412 100644 --- a/vespajlib/src/test/java/com/yahoo/tensor/IndexedTensorTestCase.java +++ b/vespajlib/src/test/java/com/yahoo/tensor/IndexedTensorTestCase.java @@ -56,10 +56,10 @@ public class IndexedTensorTestCase { assertEquals(emptyWithDimensions, emptyWithDimensionsFromString); IndexedTensor emptyWithDimensionsIndexed = (IndexedTensor)emptyWithDimensions; - assertEquals(0, emptyWithDimensionsIndexed.size(0)); - assertEquals(0, emptyWithDimensionsIndexed.size(1)); + assertEquals(0, emptyWithDimensionsIndexed.length(0)); + assertEquals(0, emptyWithDimensionsIndexed.length(1)); } - + @Test public void testBoundBuilding() { TensorType type = new TensorType.Builder().indexed("v", vSize) @@ -108,7 +108,7 @@ public class IndexedTensorTestCase { for (int y = 0; y < ySize; y++) for (int x = xSize - 1; x >= 0; x--) for (int z = 0; z < zSize; z++) - assertEquals(value(v, w, x, y, z), (int) tensor.get(TensorAddress.of(v, w, x, y, z))); + assertEquals(value(v, w, x, y, z), (int) tensor.get(new TensorAddress(v, w, x, y, z))); // Lookup from cells Map<TensorAddress, Double> cells = tensor.cells(); @@ -118,7 +118,7 @@ public class IndexedTensorTestCase { for (int y = 0; y < ySize; y++) for (int x = xSize - 1; x >= 0; x--) for (int z = 0; z < zSize; z++) - assertEquals(value(v, w, x, y, z), cells.get(TensorAddress.of(v, w, x, y, z)).intValue()); + assertEquals(value(v, w, x, y, z), cells.get(new TensorAddress(v, w, x, y, z)).intValue()); // Lookup from iterator Map<TensorAddress, Double> cellsOfIterator = new HashMap<>(); @@ -132,7 +132,7 @@ public class IndexedTensorTestCase { for (int y = 0; y < ySize; y++) for (int x = xSize - 1; x >= 0; x--) for (int z = 0; z < zSize; z++) - assertEquals(value(v, w, x, y, z), cellsOfIterator.get(TensorAddress.of(v, w, x, y, z)).intValue()); + assertEquals(value(v, w, x, y, z), cellsOfIterator.get(new TensorAddress(v, w, x, y, z)).intValue()); } diff --git a/vespajlib/src/test/java/com/yahoo/tensor/JoinTestCase.java b/vespajlib/src/test/java/com/yahoo/tensor/JoinTestCase.java deleted file mode 100644 index 63dd4a4a644..00000000000 --- a/vespajlib/src/test/java/com/yahoo/tensor/JoinTestCase.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.yahoo.tensor; - -import org.junit.Test; - -import static org.junit.Assert.assertEquals; - -/** - * @author bratseth - */ -public class JoinTestCase { - - /** Test the indexed subspace join optimization */ - @Test - public void testJoinIndexedSubspace() { - Tensor t1, t2; - - t1 = Tensor.from("tensor(x[]):{{x:0}:1.0,{x:1}:2.0}"); - t2 = Tensor.from("tensor(x[],y[],z[]):{{x:0,y:0,z:0}:6,{x:0,y:1,z:0}:0.0,{x:1,y:0,z:0}:10,{x:1,y:1,z:0}:0.0}"); - assertEquals(Tensor.from("tensor(x[],y[],z[]):{{x:0,y:0,z:0}:6,{x:0,y:1,z:0}:0.0,{x:1,y:0,z:0}:20.0,{x:1,y:1,z:0}:0.0}"), - t1.multiply(t2)); - t1 = Tensor.from("tensor(x[]):{{x:0}:1.0,{x:1}:2.0}"); - t2 = Tensor.from("tensor(x[],y[],z[]):{{x:0,y:0,z:0}:6,{x:0,y:1,z:0}:0.0,{x:1,y:0,z:0}:10,{x:1,y:1,z:0}:0.0}"); - assertEquals(Tensor.from("tensor(x[],y[],z[]):{{x:0,y:0,z:0}:6,{x:0,y:1,z:0}:0.0,{x:1,y:0,z:0}:5.0,{x:1,y:1,z:0}:0.0}"), - t2.divide(t1)); - - t1 = Tensor.from("tensor(y[]):{{y:0}:1.0,{y:1}:2.0}"); - t2 = Tensor.from("tensor(x[],y[],z[]):{{x:0,y:0,z:0}:6,{x:0,y:1,z:0}:0.0,{x:1,y:0,z:0}:10,{x:1,y:1,z:0}:0.0}"); - assertEquals(Tensor.from("tensor(x[],y[],z[]):{{x:0,y:0,z:0}:6,{x:0,y:1,z:0}:0.0,{x:1,y:0,z:0}:10.0,{x:1,y:1,z:0}:0.0}"), - t1.multiply(t2)); - t1 = Tensor.from("tensor(y[]):{{y:0}:1.0,{y:1}:2.0}"); - t2 = Tensor.from("tensor(x[],y[],z[]):{{x:0,y:0,z:0}:6,{x:0,y:1,z:0}:0.0,{x:1,y:0,z:0}:10,{x:1,y:1,z:0}:0.0}"); - assertEquals(Tensor.from("tensor(x[],y[],z[]):{{x:0,y:0,z:0}:6,{x:0,y:1,z:0}:0.0,{x:1,y:0,z:0}:10.0,{x:1,y:1,z:0}:0.0}"), - t2.divide(t1)); - } - -} diff --git a/vespajlib/src/test/java/com/yahoo/tensor/TensorFunctionBenchmark.java b/vespajlib/src/test/java/com/yahoo/tensor/TensorFunctionBenchmark.java index 9b34780a6fc..bbab92fc16d 100644 --- a/vespajlib/src/test/java/com/yahoo/tensor/TensorFunctionBenchmark.java +++ b/vespajlib/src/test/java/com/yahoo/tensor/TensorFunctionBenchmark.java @@ -106,13 +106,11 @@ public class TensorFunctionBenchmark { // ---------------- Mapped with extra space (sidesteps current special-case optimizations): // Initial: 450 ms - // - Now: 350 ms - time = new TensorFunctionBenchmark().benchmark(20, vectors(100, 300, TensorType.Dimension.Type.mapped), TensorType.Dimension.Type.mapped, true); - System.out.printf("Mapped vectors, x space time per join: %1$8.3f ms\n", time); + //time = new TensorFunctionBenchmark().benchmark(20, vectors(100, 300, TensorType.Dimension.Type.mapped), TensorType.Dimension.Type.mapped, true); + //System.out.printf("Mapped vectors, x space time per join: %1$8.3f ms\n", time); // Initial: 900 ms - // - Now: 700 ms - time = new TensorFunctionBenchmark().benchmark(20, matrix(100, 300, TensorType.Dimension.Type.mapped), TensorType.Dimension.Type.mapped, true); - System.out.printf("Mapped matrix, x space time per join: %1$8.3f ms\n", time); + //time = new TensorFunctionBenchmark().benchmark(20, matrix(100, 300, TensorType.Dimension.Type.mapped), TensorType.Dimension.Type.mapped, true); + //System.out.printf("Mapped matrix, x space time per join: %1$8.3f ms\n", time); // ---------------- Mapped: // Initial: 150 ms @@ -123,51 +121,40 @@ public class TensorFunctionBenchmark { System.out.printf("Mapped vectors, time per join: %1$8.3f ms\n", time); // Initial: 760 ms // - After special-casing subspace: 13 ms - // - Now: 7 ms time = new TensorFunctionBenchmark().benchmark(500, matrix(100, 300, TensorType.Dimension.Type.mapped), TensorType.Dimension.Type.mapped, false); System.out.printf("Mapped matrix, time per join: %1$8.3f ms\n", time); // ---------------- Indexed (unbound) with extra space (sidesteps current special-case optimizations): // Initial: 1900 ms // - After moving to cell iterators: 1100 - // - Now: 1300 - time = new TensorFunctionBenchmark().benchmark(20, vectors(100, 300, TensorType.Dimension.Type.indexedUnbound), TensorType.Dimension.Type.indexedUnbound, true); - System.out.printf("Indexed vectors, x space time per join: %1$8.3f ms\n", time); + //time = new TensorFunctionBenchmark().benchmark(20, vectors(100, 300, TensorType.Dimension.Type.indexedUnbound), TensorType.Dimension.Type.indexedUnbound, true); + //System.out.printf("Indexed vectors, x space time per join: %1$8.3f ms\n", time); // Initial: 2200 ms // - After moving to cell iterators: 1300 - // - Now: 1550 - time = new TensorFunctionBenchmark().benchmark(20, matrix(100, 300, TensorType.Dimension.Type.indexedUnbound), TensorType.Dimension.Type.indexedUnbound, true); - System.out.printf("Indexed matrix, x space time per join: %1$8.3f ms\n", time); + //time = new TensorFunctionBenchmark().benchmark(20, matrix(100, 300, TensorType.Dimension.Type.indexedUnbound), TensorType.Dimension.Type.indexedUnbound, true); + //System.out.printf("Indexed matrix, x space time per join: %1$8.3f ms\n", time); // ---------------- Indexed unbound: // Initial: 718 ms // - After special casing join: 3.6 ms // - After special-casing reduce: 0.80 ms // - After create IndexedTensor without builder: 0.4 ms - // - After double-array backing: 0.09 ms - time = new TensorFunctionBenchmark().benchmark(50000, vectors(100, 300, TensorType.Dimension.Type.indexedUnbound), TensorType.Dimension.Type.indexedUnbound, false); + // - After double-array backing: 0.1 ms + time = new TensorFunctionBenchmark().benchmark(10000, vectors(100, 300, TensorType.Dimension.Type.indexedUnbound), TensorType.Dimension.Type.indexedUnbound, false); System.out.printf("Indexed unbound vectors, time per join: %1$8.3f ms\n", time); // Initial: 3500 ms // - After special-casing subspace: 25 ms - // - After moving to iterators: 7.7 ms - // - After indexed subspace join algorithm: 6 - // - After passing sized: 3.7 ms - // - After creating int tensor address: 2.5 ms - // - After using int address to get ints directly: 0.93 ms - time = new TensorFunctionBenchmark().benchmark(5000, matrix(100, 300, TensorType.Dimension.Type.indexedUnbound), TensorType.Dimension.Type.indexedUnbound, false); + // - After moving to iterators: 10 ms + time = new TensorFunctionBenchmark().benchmark(500, matrix(100, 300, TensorType.Dimension.Type.indexedUnbound), TensorType.Dimension.Type.indexedUnbound, false); System.out.printf("Indexed unbound matrix, time per join: %1$8.3f ms\n", time); // ---------------- Indexed bound: - // Initial: 0.09 ms - time = new TensorFunctionBenchmark().benchmark(50000, vectors(100, 300, TensorType.Dimension.Type.indexedBound), TensorType.Dimension.Type.indexedBound, false); + // Initial: 0.1 ms + time = new TensorFunctionBenchmark().benchmark(10000, vectors(100, 300, TensorType.Dimension.Type.indexedBound), TensorType.Dimension.Type.indexedBound, false); System.out.printf("Indexed bound vectors, time per join: %1$8.3f ms\n", time); // Initial: 25 ms - // - After moving to iterators: 7.7 ms - // - After indexed subspace join algorithm: 6 - // - After passing sized: 3.7 ms - // - After creating int tensor address: 2.8 ms - // - After using int address to get ints directly: 0.93 ms - time = new TensorFunctionBenchmark().benchmark(5000, matrix(100, 300, TensorType.Dimension.Type.indexedBound), TensorType.Dimension.Type.indexedBound, false); + // - After moving to iterators: 10 ms + time = new TensorFunctionBenchmark().benchmark(500, matrix(100, 300, TensorType.Dimension.Type.indexedBound), TensorType.Dimension.Type.indexedBound, false); System.out.printf("Indexed bound matrix, time per join: %1$8.3f ms\n", time); } diff --git a/vespajlib/src/test/java/com/yahoo/tensor/TensorTestCase.java b/vespajlib/src/test/java/com/yahoo/tensor/TensorTestCase.java index eaf26fd82cd..aa220e93258 100644 --- a/vespajlib/src/test/java/com/yahoo/tensor/TensorTestCase.java +++ b/vespajlib/src/test/java/com/yahoo/tensor/TensorTestCase.java @@ -79,15 +79,11 @@ public class TensorTestCase { @Test public void testOptimizedComputation() { assertEquals("Mapped vector", 42, (int)dotProduct(vector(Type.mapped), vectors(Type.mapped, 2))); - assertEquals("Indexed unbound vector", 42, (int)dotProduct(vector(3, Type.indexedUnbound), vectors(5, Type.indexedUnbound, 2))); - assertEquals("Indexed unbound vector", 42, (int)dotProduct(vector(5, Type.indexedUnbound), vectors(3, Type.indexedUnbound, 2))); - assertEquals("Indexed bound vector", 42, (int)dotProduct(vector(3, Type.indexedBound), vectors(5, Type.indexedBound, 2))); - assertEquals("Indexed bound vector", 42, (int)dotProduct(vector(5, Type.indexedBound), vectors(3, Type.indexedBound, 2))); + assertEquals("Indexed unbound vector", 42, (int)dotProduct(vector(Type.indexedUnbound), vectors(Type.indexedUnbound, 2))); + assertEquals("Indexed bound vector", 42, (int)dotProduct(vector(Type.indexedBound), vectors(Type.indexedBound, 2))); assertEquals("Mapped matrix", 42, (int)dotProduct(vector(Type.mapped), matrix(Type.mapped, 2))); - assertEquals("Indexed unbound matrix", 42, (int)dotProduct(vector(3, Type.indexedUnbound), matrix(5, Type.indexedUnbound, 2))); - assertEquals("Indexed unbound matrix", 42, (int)dotProduct(vector(5, Type.indexedUnbound), matrix(3, Type.indexedUnbound, 2))); - assertEquals("Indexed bound matrix", 42, (int)dotProduct(vector(3, Type.indexedBound), matrix(5, Type.indexedBound, 2))); - assertEquals("Indexed bound matrix", 42, (int)dotProduct(vector(5, Type.indexedBound), matrix(3, Type.indexedBound, 2))); + assertEquals("Indexed unbound matrix", 42, (int)dotProduct(vector(Type.indexedUnbound), matrix(Type.indexedUnbound, 2))); + assertEquals("Indexed bound matrix", 42, (int)dotProduct(vector(Type.indexedBound), matrix(Type.indexedBound, 2))); assertEquals("Mixed vector", 42, (int)dotProduct(vector(Type.mapped), vectors(Type.indexedUnbound, 2))); assertEquals("Mixed vector", 42, (int)dotProduct(vector(Type.mapped), vectors(Type.indexedUnbound, 2))); assertEquals("Mixed matrix", 42, (int)dotProduct(vector(Type.mapped), matrix(Type.indexedUnbound, 2))); @@ -104,7 +100,7 @@ public class TensorTestCase { Tensor matrixInKSpace = matrix(Type.mapped, 2).get(0).multiply(unitK); assertEquals("Generic computation implementation", 42, (int)dotProduct(vectorInJSpace, Collections.singletonList(matrixInKSpace))); } - + private double dotProduct(Tensor tensor, List<Tensor> tensors) { double sum = 0; TensorFunction dotProductFunction = new Reduce(new Join(new ConstantTensor(tensor), @@ -123,17 +119,10 @@ public class TensorTestCase { private Tensor vector(TensorType.Dimension.Type dimensionType) { return vectors(dimensionType, 1).get(0); } - - private Tensor vector(int vectorSize, TensorType.Dimension.Type dimensionType) { - return vectors(vectorSize, dimensionType, 1).get(0); - } /** Create a list of vectors having a single dimension x */ private List<Tensor> vectors(TensorType.Dimension.Type dimensionType, int vectorCount) { - return vectors(3, dimensionType, vectorCount); - } - - private List<Tensor> vectors(int vectorSize, TensorType.Dimension.Type dimensionType, int vectorCount) { + int vectorSize = 3; List<Tensor> tensors = new ArrayList<>(); TensorType type = vectorType(new TensorType.Builder(), "x", dimensionType, vectorSize); for (int i = 0; i < vectorCount; i++) { @@ -151,10 +140,7 @@ public class TensorTestCase { * This matrix contains the same vectors as returned by createVectors, in a single list element for convenience. */ private List<Tensor> matrix(TensorType.Dimension.Type dimensionType, int vectorCount) { - return matrix(3, dimensionType, vectorCount); - } - - private List<Tensor> matrix(int vectorSize, TensorType.Dimension.Type dimensionType, int vectorCount) { + int vectorSize = 3; TensorType.Builder typeBuilder = new TensorType.Builder(); typeBuilder.dimension("i", dimensionType == Type.indexedBound ? Type.indexedUnbound : dimensionType); vectorType(typeBuilder, "x", dimensionType, vectorSize); |