summaryrefslogtreecommitdiffstats
path: root/vespajlib/src/main/java/com
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@yahoo-inc.com>2016-12-16 11:25:36 +0100
committerJon Bratseth <bratseth@yahoo-inc.com>2016-12-16 11:25:36 +0100
commit169c0cfffece0dd962c747dd981b47faad513f49 (patch)
treee0c1b1bcf9b40742a1cb75a78ad582f752d7f672 /vespajlib/src/main/java/com
parent7821d6affb977a8652d9be244ffd647b81db1789 (diff)
Generify parsing
Diffstat (limited to 'vespajlib/src/main/java/com')
-rw-r--r--vespajlib/src/main/java/com/yahoo/tensor/IndexedTensor.java154
-rw-r--r--vespajlib/src/main/java/com/yahoo/tensor/MappedTensor.java13
-rw-r--r--vespajlib/src/main/java/com/yahoo/tensor/MixedTensor.java373
-rw-r--r--vespajlib/src/main/java/com/yahoo/tensor/Tensor.java8
-rw-r--r--vespajlib/src/main/java/com/yahoo/tensor/TensorParser.java81
5 files changed, 118 insertions, 511 deletions
diff --git a/vespajlib/src/main/java/com/yahoo/tensor/IndexedTensor.java b/vespajlib/src/main/java/com/yahoo/tensor/IndexedTensor.java
index 8b1f67158b7..67ed2180201 100644
--- a/vespajlib/src/main/java/com/yahoo/tensor/IndexedTensor.java
+++ b/vespajlib/src/main/java/com/yahoo/tensor/IndexedTensor.java
@@ -82,116 +82,18 @@ public class IndexedTensor implements Tensor {
return product;
}
+ @Override
+ public TensorType type() { return type; }
+
/**
* 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 length(int dimension) {
return dimensionSizes[dimension];
}
- // TODO: Duplication with MixedTensor
- static IndexedTensor from(TensorType type, String tensorString) {
- tensorString = tensorString.trim();
- try {
- if (tensorString.startsWith("{"))
- return fromCellString(type, tensorString);
- else
- return fromNumber(Double.parseDouble(tensorString));
- }
- catch (NumberFormatException e) {
- throw new IllegalArgumentException("Excepted a number or a string starting by { or tensor(, got '" +
- tensorString + "'");
- }
- }
-
- // TODO: Duplication with MixedTensor
- private static IndexedTensor fromCellString(TensorType type, String s) {
- IndexedTensor.Builder builder = IndexedTensor.Builder.of(type);
- s = s.trim().substring(1).trim();
- while (s.length() > 1) {
- int keyOrTensorEnd = s.indexOf('}');
- int[] cellIndexes;
- if (keyOrTensorEnd == s.length()-1) { // Tensor end: No keys: This is empty or contains a single number
- cellIndexes = new int[0];
- }
- else {
- cellIndexes = indexesFrom(type, s.substring(0, keyOrTensorEnd + 1));
- s = s.substring(keyOrTensorEnd + 1).trim();
- if ( ! s.startsWith(":"))
- throw new IllegalArgumentException("Expecting a ':' after " + s + ", got '" + s + "'");
- s = s.substring(1);
- }
- int valueEnd = s.indexOf(',');
- if (valueEnd < 0) { // last value
- valueEnd = s.indexOf("}");
- if (valueEnd < 0)
- throw new IllegalArgumentException("A tensor string must end by '}'");
- }
- Double value = asDouble(cellIndexes, s.substring(0, valueEnd).trim());
-
- builder.cell(value, cellIndexes);
- s = s.substring(valueEnd+1).trim();
- }
- return builder.build();
- }
-
- // TODO: Duplication with MixedTensor
- /** Creates a tenor address from a string on the form {dimension1:label1,dimension2:label2,...} */
- private static int[] indexesFrom(TensorType type, String mapAddressString) {
- mapAddressString = mapAddressString.trim();
- if ( ! (mapAddressString.startsWith("{") && mapAddressString.endsWith("}")))
- throw new IllegalArgumentException("Expecting a tensor address enclosed in {}, got '" + mapAddressString + "'");
-
- String addressBody = mapAddressString.substring(1, mapAddressString.length() - 1).trim();
- if (addressBody.isEmpty()) return new int[0];
-
- int[] indexes = new int[type.dimensions().size()];
- for (String elementString : addressBody.split(",")) {
- String[] pair = elementString.split(":");
- if (pair.length != 2)
- throw new IllegalArgumentException("Expecting argument elements on the form dimension:label, " +
- "got '" + elementString + "'");
- String dimension = pair[0].trim();
- Optional<Integer> dimensionIndex = type.indexOfDimension(dimension);
- if ( ! dimensionIndex.isPresent())
- throw new IllegalArgumentException("Dimension '" + dimension + "' is not present in " + type);
- indexes[dimensionIndex.get()] = asInteger(pair[1].trim(), dimension);
- }
- return indexes;
- }
-
- // TODO: Duplication with MixedTensor
- private static int asInteger(String value, String dimension) {
- try {
- return Integer.parseInt(value);
- }
- catch (NumberFormatException e) {
- throw new IllegalArgumentException("Expected an integer value for dimension '" + dimension + "', " +
- "but got '" + value + "'");
- }
-
- }
-
- private static IndexedTensor fromNumber(double number) {
- return new IndexedTensor(TensorType.empty, new int[] {}, new double[] { number });
- }
-
- // TODO: Duplication with MixedTensor
- private static Double asDouble(int[] indexes, String s) {
- try {
- return Double.valueOf(s);
- }
- catch (NumberFormatException e) {
- throw new IllegalArgumentException("At " + Arrays.toString(indexes) +
- ": Expected a floating point number, got '" + s + "'");
- }
- }
-
- @Override
- public TensorType type() { return type; }
-
@Override
// TODO: Replace this with iterator
public Map<TensorAddress, Double> cells() {
@@ -274,7 +176,10 @@ public class IndexedTensor implements Tensor {
productSize *= dimensionSize;
return new double[productSize];
}
-
+
+ @Override
+ public TensorType type() { return type; }
+
@Override
public abstract IndexedTensor build();
@@ -341,7 +246,7 @@ public class IndexedTensor implements Tensor {
}
@Override
- public Tensor.Builder cell(TensorAddress address, double value) {
+ public Builder cell(TensorAddress address, double value) {
values[toValueIndex(address, dimensionSizes)] = value;
return this;
}
@@ -423,6 +328,26 @@ public class IndexedTensor implements Tensor {
}
}
+ @Override
+ public IndexedCellBuilder cell() {
+ return new IndexedCellBuilder();
+ }
+
+ @Override
+ public Builder cell(TensorAddress address, double value) {
+ 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;
+ }
+
/**
* Set a value using an index API. The number of indexes must be the same as the dimensions in the type of this.
* Values can be written in any order but all values needed to make this dense must be provided
@@ -431,6 +356,7 @@ public class IndexedTensor implements Tensor {
* @return this for chaining
*/
@SuppressWarnings("unchecked")
+ @Override
public Builder cell(double value, int... indexes) {
if (indexes.length != type.dimensions().size())
throw new IllegalArgumentException("Wrong number of indexes (" + indexes.length + ") for " + type);
@@ -456,26 +382,6 @@ public class IndexedTensor implements Tensor {
return this;
}
- @Override
- public IndexedCellBuilder cell() {
- return new IndexedCellBuilder();
- }
-
- @Override
- public Builder cell(TensorAddress address, double value) {
- 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;
- }
-
/** Fill the given list with nulls if necessary to make sure it has a (possibly null) value at the given index */
private void ensureCapacity(int index, List<Object> list) {
while (list.size() <= index)
diff --git a/vespajlib/src/main/java/com/yahoo/tensor/MappedTensor.java b/vespajlib/src/main/java/com/yahoo/tensor/MappedTensor.java
index 3c609acff45..a5a4e29e44c 100644
--- a/vespajlib/src/main/java/com/yahoo/tensor/MappedTensor.java
+++ b/vespajlib/src/main/java/com/yahoo/tensor/MappedTensor.java
@@ -113,13 +113,22 @@ public class MappedTensor implements Tensor {
public MappedCellBuilder cell() {
return new MappedCellBuilder();
}
-
+
+ @Override
+ public TensorType type() { return type; }
+
@Override
public Builder cell(TensorAddress address, double value) {
cells.put(address, value);
return this;
}
-
+
+ @Override
+ public Builder cell(double value, int... labels) {
+ cells.put(new TensorAddress(labels), value);
+ return this;
+ }
+
@Override
public MappedTensor build() {
return new MappedTensor(type, cells.build());
diff --git a/vespajlib/src/main/java/com/yahoo/tensor/MixedTensor.java b/vespajlib/src/main/java/com/yahoo/tensor/MixedTensor.java
deleted file mode 100644
index 89eb93e8fbb..00000000000
--- a/vespajlib/src/main/java/com/yahoo/tensor/MixedTensor.java
+++ /dev/null
@@ -1,373 +0,0 @@
-// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.tensor;
-
-import com.google.common.annotations.Beta;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-
-/**
- * Sketch of a mixed dimension tensor which retains efficiency for indexed dimensions
- *
- * @author bratseth
- */
-@Beta
-public class MixedTensor implements Tensor {
-
- private final TensorType type;
-
- private final IndexedDimension firstDimension;
-
- private MixedTensor(TensorType type, IndexedDimension firstDimension) {
- this.type = type;
- this.firstDimension = firstDimension;
- }
-
- /** Construct an indexed tensor having a single dimension with the given values */
- // TODO: Privatize
- public MixedTensor(TensorType type, List<Object> values) {
- if (type.dimensions().size() != 1 || ! type.dimensions().get(0).isIndexed())
- throw new IllegalArgumentException("Expected a single-dimensional indexed tensor but got " + type);
- this.type = type;
- this.firstDimension = new IndexedDimension(values);
- }
-
- /**
- * Returns the value at the given indexes
- *
- * @param indexes the indexes into the dimensions of this. Must be one number per dimension of this
- * @throws IndexOutOfBoundsException if any of the indexes are out of bound or a wrong number of indexes are given
- */
- public Double get(int ... indexes) {
- IndexedDimension currentDimension = firstDimension;
- for (int i = 0; i < indexes.length; i++) {
- if (i == indexes.length -1)
- return (Double)currentDimension.values().get(indexes[i]);
- else
- currentDimension = (IndexedDimension)currentDimension.values().get(indexes[i]);
- }
- return Double.NaN; // empty
- }
-
- /**
- * 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 length(int dimension) {
- if (firstDimension.values().isEmpty()) return 0; // empty tensor
-
- IndexedDimension currentDimension = firstDimension;
- while (dimension > 0)
- currentDimension = (IndexedDimension)currentDimension.values().get(0); // get the first as all (should) have the same size
- return currentDimension.values().size();
- }
-
- static MixedTensor from(TensorType type, String tensorString) {
- tensorString = tensorString.trim();
- try {
- if (tensorString.startsWith("{"))
- return fromCellString(type, tensorString);
- else
- return fromNumber(Double.parseDouble(tensorString));
- }
- catch (NumberFormatException e) {
- throw new IllegalArgumentException("Excepted a number or a string starting by { or tensor(, got '" +
- tensorString + "'");
- }
- }
-
- private static MixedTensor fromCellString(TensorType type, String s) {
- MixedTensor.Builder builder = new MixedTensor.Builder(type);
- s = s.trim().substring(1).trim();
- while (s.length() > 1) {
- int keyEnd = s.indexOf('}');
- int[] cellIndexes = indexesFrom(type, s.substring(0, keyEnd+1));
- s = s.substring(keyEnd + 1).trim();
- if ( ! s.startsWith(":"))
- throw new IllegalArgumentException("Expecting a ':' after " + s + ", got '" + s + "'");
- int valueEnd = s.indexOf(',');
- if (valueEnd < 0) { // last value
- valueEnd = s.indexOf("}");
- if (valueEnd < 0)
- throw new IllegalArgumentException("A tensor string must end by '}'");
- }
- Double value = asDouble(cellIndexes, s.substring(1, valueEnd).trim());
-
- builder.set(value, cellIndexes);
- s = s.substring(valueEnd+1).trim();
- }
- return builder.build();
- }
-
- /** Creates a tenor address from a string on the form {dimension1:label1,dimension2:label2,...} */
- private static int[] indexesFrom(TensorType type, String mapAddressString) {
- mapAddressString = mapAddressString.trim();
- if ( ! (mapAddressString.startsWith("{") && mapAddressString.endsWith("}")))
- throw new IllegalArgumentException("Expecting a tensor address to be enclosed in {}, got '" + mapAddressString + "'");
-
- String addressBody = mapAddressString.substring(1, mapAddressString.length() - 1).trim();
- if (addressBody.isEmpty()) return new int[0];
-
- int[] indexes = new int[type.dimensions().size()];
- for (String elementString : addressBody.split(",")) {
- String[] pair = elementString.split(":");
- if (pair.length != 2)
- throw new IllegalArgumentException("Expecting argument elements to be on the form dimension:label, " +
- "got '" + elementString + "'");
- String dimension = pair[0].trim();
- Optional<Integer> dimensionIndex = type.indexOfDimension(dimension);
- if ( ! dimensionIndex.isPresent())
- throw new IllegalArgumentException("Dimension '" + dimension + "' is not present in " + type);
- indexes[dimensionIndex.get()] = asInteger(pair[1].trim(), dimension);
- }
- return indexes;
- }
-
- private static int asInteger(String value, String dimension) {
- try {
- return Integer.parseInt(value);
- }
- catch (NumberFormatException e) {
- throw new IllegalArgumentException("Expected an integer value for dimension '" + dimension + "', " +
- "but got '" + value + "'");
- }
-
- }
-
- private static MixedTensor fromNumber(double number) {
- return new MixedTensor(TensorType.empty, new IndexedDimension(number));
- }
-
- private static Double asDouble(int[] indexes, String s) {
- try {
- return Double.valueOf(s);
- }
- catch (NumberFormatException e) {
- throw new IllegalArgumentException("At " + Arrays.toString(indexes) +
- ": Expected a floating point number, got '" + s + "'");
- }
- }
-
- @Override
- public TensorType type() { return type; }
-
- @Override
- public Map<TensorAddress, Double> cells() {
- ImmutableMap.Builder<TensorAddress, Double> builder = new ImmutableMap.Builder<>();
- populateRecursively(builder, firstDimension, new TensorAddress.Builder(type), new ArrayList<>(type.dimensions()));
- return builder.build();
- }
-
- private void populateRecursively(ImmutableMap.Builder<TensorAddress, Double> valueBuilder, IndexedDimension dimensionValues,
- TensorAddress.Builder partialAddress, List<TensorType.Dimension> remainingDimensions) {
- if (remainingDimensions.size() == 0) {
- if ( ! dimensionValues.values().isEmpty()) // either empty or a single value
- valueBuilder.put(TensorAddress.empty, (Double)dimensionValues.values().get(0));
- }
- else if (remainingDimensions.size() == 1) {
- for (int i = 0; i < dimensionValues.values().size(); i++)
- valueBuilder.put(partialAddress.copy().add(remainingDimensions.get(0).name(), String.valueOf(i)).build(),
- (Double)dimensionValues.values().get(i));
- }
- else {
- List<TensorType.Dimension> nestedRemainingDimensions = new ArrayList<>(remainingDimensions);
- TensorType.Dimension currentDimension = nestedRemainingDimensions.remove(0);
- for (int i = 0; i < dimensionValues.values().size(); i++) {
- populateRecursively(valueBuilder, (IndexedDimension)dimensionValues.values().get(i),
- partialAddress.copy().add(currentDimension.name(), String.valueOf(i)),
- nestedRemainingDimensions);
- }
- }
- }
-
- /** Returns the value at this address, or NaN if there is no value at this address */
- @Override
- public double get(TensorAddress address) {
- if (type.dimensions().isEmpty()) // either empty or a sinle value
- return firstDimension.values().isEmpty() ? Double.NaN : (double)firstDimension.values().get(0);
-
- IndexedDimension currentDimension = firstDimension;
- for (int i = 0; i < address.labels().size(); i++) {
- int index = Integer.parseInt(address.labels().get(i));
- if (index >= currentDimension.values().size()) return Double.NaN;
-
- Object value = currentDimension.values().get(index);
- if (value == null) return Double.NaN;
-
- if (i == address.labels().size() -1) // last dimension
- return (double)value;
- else
- currentDimension = (IndexedDimension)value;
- }
- return Double.NaN;
- }
-
- @Override
- public int hashCode() { return firstDimension.hashCode(); }
-
- @Override
- public String toString() { return Tensor.toStandardString(this); }
-
- @Override
- public boolean equals(Object o) {
- if ( ! (o instanceof Tensor)) return false;
- return Tensor.equals(this, (Tensor)o);
- }
-
- /** An indexed dimension containing another indexed dimension */
- private static class IndexedDimension {
-
- private final ImmutableList<Object> values;
-
- public IndexedDimension() { values = ImmutableList.of(); }
-
- public IndexedDimension(Double value) { values = ImmutableList.of(value); }
-
- public IndexedDimension(List<Object> values) {
- this.values = ImmutableList.copyOf(values);
- }
-
- public ImmutableList<Object> values() { return values; }
-
- }
-
- public static class Builder implements Tensor.Builder {
-
- private final TensorType type;
-
- /** List of List or Double */
- private List<Object> firstDimension = null;
-
- public Builder(TensorType type) {
- this.type = type;
- }
-
- @Override
- public MixedTensor build() {
- // TODO: Enforce that all values in all dimensions are equally large
- if (firstDimension == null) // empty
- return new MixedTensor(type, new IndexedDimension());
- if (type.dimensions().isEmpty()) // single number
- return new MixedTensor(type,
- new IndexedDimension((Double)firstDimension.get(0)));
-
- List<TensorType.Dimension> dimensions = new ArrayList<>(type.dimensions());
- IndexedDimension firstDimension = buildRecursively(dimensions, this.firstDimension);
- return new MixedTensor(type, firstDimension);
- }
-
- @SuppressWarnings("unchecked")
- private IndexedDimension buildRecursively(List<TensorType.Dimension> remainingDimensions,
- List<Object> currentDimensionValues) {
- if (remainingDimensions.size() == 1) { // last dimension
- for (int i = 0; i < currentDimensionValues.size(); i++)
- if (currentDimensionValues.get(i) == null)
- throw new IllegalArgumentException("Missing a value at index " + i + " in dimension " +
- remainingDimensions.get(0) + " for tensor of type " + type);
- return new IndexedDimension(currentDimensionValues);
- }
- else {
- List<TensorType.Dimension> nestedRemainingDimensions = new ArrayList<>(remainingDimensions);
- TensorType.Dimension currentDimension = nestedRemainingDimensions.remove(0);
- ImmutableList.Builder<Object> values = new ImmutableList.Builder<>();
- for (int i = 0; i < currentDimensionValues.size(); i++) {
- if (currentDimensionValues.get(i) == null)
- throw new IllegalArgumentException("Missing a value at index " + i + " in dimension " +
- currentDimension + " for tensor of type " + type);
- values.add(buildRecursively(nestedRemainingDimensions, (List<Object>)currentDimensionValues.get(i)));
- }
- return new IndexedDimension(values.build());
- }
- }
-
- /**
- * Set a value using an index API. The number of indexes must be the same as the dimensions in the type of this.
- * Values can be written in any order but all values needed to make this dense must be provided
- * before building this.
- *
- * @return this for chaining
- */
- @SuppressWarnings("unchecked")
- public Builder set(double value, int ... indexes) {
- if (indexes.length != type.dimensions().size())
- throw new IllegalArgumentException("Wrong number of indexes (" + indexes.length + ") for " + type);
-
- if (indexes.length == 0) {
- firstDimension = Collections.singletonList(value);
- return this;
- }
-
- if (firstDimension == null)
- firstDimension = new ArrayList<>();
- List<Object> currentValues = firstDimension;
- for (int dimensionIndex = 0; dimensionIndex < indexes.length; dimensionIndex++) {
- ensureCapacity(indexes[dimensionIndex], currentValues);
- if (dimensionIndex == indexes.length - 1) { // last dimension
- currentValues.set(indexes[dimensionIndex], value);
- }
- else {
- if (currentValues.get(indexes[dimensionIndex]) == null)
- currentValues.set(indexes[dimensionIndex], new ArrayList<>());
- currentValues = (List<Object>)currentValues.get(indexes[dimensionIndex]);
- }
- }
- return this;
- }
-
- @Override
- public IndexedCellBuilder cell() {
- return new IndexedCellBuilder();
- }
-
- @Override
- public Builder cell(TensorAddress address, double value) {
- 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) + "'");
- }
- }
- set(value, indexes);
- return this;
- }
-
- /** Fill the given list with nulls if necessary to make sure it has a (possibly null) value at the given index */
- private void ensureCapacity(int index, List<Object> list) {
- while (list.size() <= index)
- list.add(list.size(), null);
- }
-
- public class IndexedCellBuilder implements CellBuilder {
-
- private final TensorAddress.Builder addressBuilder = new TensorAddress.Builder(MixedTensor.Builder.this.type);
-
- @Override
- public MixedTensor.Builder.IndexedCellBuilder label(String dimension, String label) {
- addressBuilder.add(dimension, label);
- return this;
- }
-
- @Override
- public MixedTensor.Builder.IndexedCellBuilder label(String dimension, int label) {
- return label(dimension, String.valueOf(label));
- }
-
- @Override
- public MixedTensor.Builder value(double cellValue) {
- return MixedTensor.Builder.this.cell(addressBuilder.build(), cellValue);
- }
-
- }
-
- }
-}
diff --git a/vespajlib/src/main/java/com/yahoo/tensor/Tensor.java b/vespajlib/src/main/java/com/yahoo/tensor/Tensor.java
index f548e551788..34e36d9fb8b 100644
--- a/vespajlib/src/main/java/com/yahoo/tensor/Tensor.java
+++ b/vespajlib/src/main/java/com/yahoo/tensor/Tensor.java
@@ -251,11 +251,17 @@ public interface Tensor {
return IndexedTensor.Builder.of(type);
}
+ /** Returns the type this is building */
+ TensorType type();
+
/** Return a cell builder */
CellBuilder cell();
- /** Add a built cell */
+ /** Add a cell */
Builder cell(TensorAddress address, double value);
+
+ /** Add a cell */
+ Builder cell(double value, int ... labels);
Tensor build();
diff --git a/vespajlib/src/main/java/com/yahoo/tensor/TensorParser.java b/vespajlib/src/main/java/com/yahoo/tensor/TensorParser.java
index 625a799ace3..5990e633266 100644
--- a/vespajlib/src/main/java/com/yahoo/tensor/TensorParser.java
+++ b/vespajlib/src/main/java/com/yahoo/tensor/TensorParser.java
@@ -39,17 +39,6 @@ class TensorParser {
}
}
- private static Tensor tensorFromValueString(String tensorCellString, TensorType type) {
- boolean containsIndexedDimensions = type.dimensions().stream().anyMatch(d -> d.isIndexed());
- boolean containsMappedDimensions = type.dimensions().stream().anyMatch(d -> !d.isIndexed());
- if (containsIndexedDimensions && containsMappedDimensions)
- throw new IllegalArgumentException("Mixed dimension types are not supported, got: " + type);
- if (containsMappedDimensions)
- return MappedTensor.from(type, tensorCellString);
- else // indexed or none
- return IndexedTensor.from(type, tensorCellString);
- }
-
/** Derive the tensor type from the first address string in the given tensor string */
private static TensorType typeFromValueString(String s) {
s = s.substring(1).trim(); // remove tensor start
@@ -73,4 +62,74 @@ class TensorParser {
return builder.build();
}
+ private static Tensor tensorFromValueString(String tensorValueString, TensorType type) {
+ Tensor.Builder builder = Tensor.Builder.of(type);
+ tensorValueString = tensorValueString.trim();
+ try {
+ if (tensorValueString.startsWith("{"))
+ return fromCellString(builder, tensorValueString);
+ else
+ return builder.cell(Double.parseDouble(tensorValueString)).build();
+ }
+ catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Excepted a number or a string starting by { or tensor(, got '" +
+ tensorValueString + "'");
+ }
+ }
+
+ private static Tensor fromCellString(Tensor.Builder builder, String s) {
+ s = s.trim().substring(1).trim();
+ while (s.length() > 1) {
+ int keyOrTensorEnd = s.indexOf('}');
+ TensorAddress.Builder addressBuilder = new TensorAddress.Builder(builder.type());
+ if (keyOrTensorEnd < s.length() - 1) { // Key end: This has a key - otherwise TensorAdress is empty
+ addLabels(s.substring(0, keyOrTensorEnd + 1), addressBuilder);
+ s = s.substring(keyOrTensorEnd + 1).trim();
+ if ( ! s.startsWith(":"))
+ throw new IllegalArgumentException("Expecting a ':' after " + s + ", got '" + s + "'");
+ s = s.substring(1);
+ }
+ int valueEnd = s.indexOf(',');
+ if (valueEnd < 0) { // last value
+ valueEnd = s.indexOf("}");
+ if (valueEnd < 0)
+ throw new IllegalArgumentException("A tensor string must end by '}'");
+ }
+
+ TensorAddress address = addressBuilder.build();
+ Double value = asDouble(address, s.substring(0, valueEnd).trim());
+ builder.cell(address, value);
+ s = s.substring(valueEnd+1).trim();
+ }
+ return builder.build();
+ }
+
+ /** Creates a tenor address from a string on the form {dimension1:label1,dimension2:label2,...} */
+ private static void addLabels(String mapAddressString, TensorAddress.Builder builder) {
+ mapAddressString = mapAddressString.trim();
+ if ( ! (mapAddressString.startsWith("{") && mapAddressString.endsWith("}")))
+ throw new IllegalArgumentException("Expecting a tensor address enclosed in {}, got '" + mapAddressString + "'");
+
+ String addressBody = mapAddressString.substring(1, mapAddressString.length() - 1).trim();
+ if (addressBody.isEmpty()) return;
+
+ for (String elementString : addressBody.split(",")) {
+ String[] pair = elementString.split(":");
+ if (pair.length != 2)
+ throw new IllegalArgumentException("Expecting argument elements on the form dimension:label, " +
+ "got '" + elementString + "'");
+ String dimension = pair[0].trim();
+ builder.add(dimension, pair[1].trim());
+ }
+ }
+
+ private static Double asDouble(TensorAddress address, String s) {
+ try {
+ return Double.valueOf(s);
+ }
+ catch (NumberFormatException e) {
+ throw new IllegalArgumentException("At " + address + ": Expected a floating point number, got '" + s + "'");
+ }
+ }
+
}