summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@gmail.com>2022-06-08 22:48:46 +0200
committerGitHub <noreply@github.com>2022-06-08 22:48:46 +0200
commit583332f6acf5ac91c3cf3e8de23980c2818b5284 (patch)
tree3dff4a0456b3672f7b63de0fefb5c5beaa81d3b5
parentf954bda9c7773bb28f6005b15be2b512b83bc33e (diff)
parent61d512485d2251a9937b2a5aa1b2327228495077 (diff)
Merge pull request #22996 from vespa-engine/lesters/short-tensor-format-as-default
Lesters/short tensor format as default MERGEOK
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/Presentation.java4
-rw-r--r--container-search/src/test/java/com/yahoo/search/rendering/JsonRendererTestCase.java8
-rw-r--r--model-evaluation/src/main/java/ai/vespa/models/handler/ModelsEvaluationHandler.java6
-rw-r--r--model-evaluation/src/test/java/ai/vespa/models/handler/ModelsEvaluationHandlerTest.java16
-rw-r--r--vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/DocumentV1ApiHandler.java17
-rw-r--r--vespaclient-container-plugin/src/test/cfg/music.sd1
-rw-r--r--vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/resource/DocumentV1ApiTest.java24
-rw-r--r--vespajlib/src/main/java/com/yahoo/tensor/serialization/JsonFormat.java14
8 files changed, 56 insertions, 34 deletions
diff --git a/container-search/src/main/java/com/yahoo/search/query/Presentation.java b/container-search/src/main/java/com/yahoo/search/query/Presentation.java
index 31641b5c2f0..afa87eb4a06 100644
--- a/container-search/src/main/java/com/yahoo/search/query/Presentation.java
+++ b/container-search/src/main/java/com/yahoo/search/query/Presentation.java
@@ -75,7 +75,7 @@ public class Presentation implements Cloneable {
private boolean timing = false;
/** Whether to renders tensors in short form */
- private boolean tensorShortForm = false;
+ private boolean tensorShortForm = true;
/** Set of explicitly requested summary fields, instead of summary classes */
private Set<String> summaryFields = LazySet.newHashSet();
@@ -180,7 +180,7 @@ public class Presentation implements Cloneable {
* Returns whether tensors should use short form in JSON and textual representations, see
* <a href="https://docs.vespa.ai/en/reference/document-json-format.html#tensor">https://docs.vespa.ai/en/reference/document-json-format.html#tensor</a>
* and <a href="https://docs.vespa.ai/en/reference/tensor.html#tensor-literal-form">https://docs.vespa.ai/en/reference/tensor.html#tensor-literal-form</a>.
- * Default is false.
+ * Default is true.
*/
public boolean getTensorShortForm() { return tensorShortForm; }
diff --git a/container-search/src/test/java/com/yahoo/search/rendering/JsonRendererTestCase.java b/container-search/src/test/java/com/yahoo/search/rendering/JsonRendererTestCase.java
index 118dcee9598..570b1d006de 100644
--- a/container-search/src/test/java/com/yahoo/search/rendering/JsonRendererTestCase.java
+++ b/container-search/src/test/java/com/yahoo/search/rendering/JsonRendererTestCase.java
@@ -209,14 +209,14 @@ public class JsonRendererTestCase {
+ " \"object\": \"thingie\","
+ " \"string\": \"stuff\","
+ " \"predicate\": \"a in [b]\","
- + " \"tensor1\": { \"cells\": [ { \"address\": {\"x\": \"a\"}, \"value\":2.0 } ] },"
+ + " \"tensor1\": { \"type\": \"tensor(x{})\", \"cells\": { \"a\":2.0 } },"
+ " \"tensor2\": { \"cells\": [] },"
- + " \"tensor3\": { \"cells\": [ { \"address\": {\"x\": \"a\", \"y\": \"0\"}, \"value\":2.0 }, { \"address\": {\"x\": \"a\", \"y\": \"1\"}, \"value\":-1.0 } ] },"
+ + " \"tensor3\": { \"type\": \"tensor(x{},y{})\", \"cells\": [ { \"address\": {\"x\": \"a\", \"y\": \"0\"}, \"value\":2.0 }, { \"address\": {\"x\": \"a\", \"y\": \"1\"}, \"value\":-1.0 } ] },"
+ " \"summaryfeatures\": {"
+ " \"scalar1\":1.5,"
+ " \"scalar2\":2.5,"
- + " \"tensor1\":{\"type\":\"tensor(x[3])\",\"cells\":[{\"address\":{\"x\":\"0\"},\"value\":1.5},{\"address\":{\"x\":\"1\"},\"value\":2.0},{\"address\":{\"x\":\"2\"},\"value\":2.5}]},"
- + " \"tensor2\":{\"type\":\"tensor()\",\"cells\":[{\"address\":{},\"value\":0.5}]}"
+ + " \"tensor1\":{\"type\":\"tensor(x[3])\", \"values\":[1.5, 2.0, 2.5] },"
+ + " \"tensor2\":{\"type\":\"tensor()\", \"values\":[0.5] }"
+ " },"
+ " \"data\": \"Data \\\\xc3\\\\xa6 \\\\xc3\\\\xa5\""
+ " },"
diff --git a/model-evaluation/src/main/java/ai/vespa/models/handler/ModelsEvaluationHandler.java b/model-evaluation/src/main/java/ai/vespa/models/handler/ModelsEvaluationHandler.java
index b0e2be26f8a..2661b9c2eb2 100644
--- a/model-evaluation/src/main/java/ai/vespa/models/handler/ModelsEvaluationHandler.java
+++ b/model-evaluation/src/main/java/ai/vespa/models/handler/ModelsEvaluationHandler.java
@@ -90,13 +90,13 @@ public class ModelsEvaluationHandler extends ThreadedHttpRequestHandler {
Tensor result = evaluator.evaluate();
Optional<String> format = property(request, "format.tensors");
- if (format.isPresent() && format.get().equalsIgnoreCase("short")) {
- return new Response(200, JsonFormat.encodeShortForm(result));
+ if (format.isPresent() && format.get().equalsIgnoreCase("long")) {
+ return new Response(200, JsonFormat.encode(result));
}
else if (format.isPresent() && format.get().equalsIgnoreCase("string")) {
return new Response(200, result.toString().getBytes(StandardCharsets.UTF_8));
}
- return new Response(200, JsonFormat.encode(result));
+ return new Response(200, JsonFormat.encodeShortForm(result));
}
private HttpResponse listAllModels(HttpRequest request) {
diff --git a/model-evaluation/src/test/java/ai/vespa/models/handler/ModelsEvaluationHandlerTest.java b/model-evaluation/src/test/java/ai/vespa/models/handler/ModelsEvaluationHandlerTest.java
index 3000c83b7ec..7790f8a60d0 100644
--- a/model-evaluation/src/test/java/ai/vespa/models/handler/ModelsEvaluationHandlerTest.java
+++ b/model-evaluation/src/test/java/ai/vespa/models/handler/ModelsEvaluationHandlerTest.java
@@ -66,7 +66,7 @@ public class ModelsEvaluationHandlerTest {
@Test
public void testXgBoostEvaluationWithoutBindings() {
String url = "http://localhost/model-evaluation/v1/xgboost_2_2/eval"; // only has a single function
- String expected = "{\"cells\":[{\"address\":{},\"value\":-4.376589999999999}]}";
+ String expected = "{\"type\":\"tensor()\",\"values\":[-4.376589999999999]}";
handler.assertResponse(url, 200, expected);
}
@@ -79,7 +79,7 @@ public class ModelsEvaluationHandlerTest {
properties.put("f109", "0.4");
properties.put("non-existing-binding", "-1");
String url = "http://localhost/model-evaluation/v1/xgboost_2_2/eval";
- String expected = "{\"cells\":[{\"address\":{},\"value\":-7.936679999999999}]}";
+ String expected = "{\"type\":\"tensor()\",\"values\":[-7.936679999999999]}";
handler.assertResponse(url, properties, 200, expected);
}
@@ -91,6 +91,7 @@ public class ModelsEvaluationHandlerTest {
properties.put("f60", "0.3");
properties.put("f109", "0.4");
properties.put("non-existing-binding", "-1");
+ properties.put("format.tensors", "long");
String url = "http://localhost/model-evaluation/v1/xgboost_2_2/eval";
String expected = "{\"cells\":[{\"address\":{},\"value\":-7.936679999999999}]}";
handler.assertResponse(url, properties, 200, expected);
@@ -99,7 +100,7 @@ public class ModelsEvaluationHandlerTest {
@Test
public void testLightGBMEvaluationWithoutBindings() {
String url = "http://localhost/model-evaluation/v1/lightgbm_regression/eval";
- String expected = "{\"cells\":[{\"address\":{},\"value\":1.9130086820218188}]}";
+ String expected = "{\"type\":\"tensor()\",\"values\":[1.9130086820218188]}";
handler.assertResponse(url, 200, expected);
}
@@ -112,7 +113,7 @@ public class ModelsEvaluationHandlerTest {
properties.put("categorical_2", "i");
properties.put("non-existing-binding", "-1");
String url = "http://localhost/model-evaluation/v1/lightgbm_regression/eval";
- String expected = "{\"cells\":[{\"address\":{},\"value\":2.054697758469921}]}";
+ String expected = "{\"type\":\"tensor()\",\"values\":[2.054697758469921]}";
handler.assertResponse(url, properties, 200, expected);
}
@@ -125,7 +126,7 @@ public class ModelsEvaluationHandlerTest {
properties.put("categorical_2", "j");
properties.put("non-existing-binding", "-1");
String url = "http://localhost/model-evaluation/v1/lightgbm_regression/eval";
- String expected = "{\"cells\":[{\"address\":{},\"value\":2.0745534018208094}]}";
+ String expected = "{\"type\":\"tensor()\",\"values\":[2.0745534018208094]}";
handler.assertResponse(url, properties, 200, expected);
}
@@ -162,7 +163,7 @@ public class ModelsEvaluationHandlerTest {
Map<String, String> properties = new HashMap<>();
properties.put("Placeholder", inputTensor());
String url = "http://localhost/model-evaluation/v1/mnist_softmax/eval";
- String expected = "{\"cells\":[{\"address\":{\"d0\":\"0\",\"d1\":\"0\"},\"value\":-0.3546536862850189},{\"address\":{\"d0\":\"0\",\"d1\":\"1\"},\"value\":0.3759574592113495},{\"address\":{\"d0\":\"0\",\"d1\":\"2\"},\"value\":0.06054411828517914},{\"address\":{\"d0\":\"0\",\"d1\":\"3\"},\"value\":-0.251544713973999},{\"address\":{\"d0\":\"0\",\"d1\":\"4\"},\"value\":0.017951013520359993},{\"address\":{\"d0\":\"0\",\"d1\":\"5\"},\"value\":1.2899067401885986},{\"address\":{\"d0\":\"0\",\"d1\":\"6\"},\"value\":-0.10389615595340729},{\"address\":{\"d0\":\"0\",\"d1\":\"7\"},\"value\":0.6367976665496826},{\"address\":{\"d0\":\"0\",\"d1\":\"8\"},\"value\":-1.4136744737625122},{\"address\":{\"d0\":\"0\",\"d1\":\"9\"},\"value\":-0.2573896050453186}]}";
+ String expected = "{\"type\":\"tensor(d0[],d1[10])\",\"values\":[[-0.3546536862850189,0.3759574592113495,0.06054411828517914,-0.251544713973999,0.017951013520359993,1.2899067401885986,-0.10389615595340729,0.6367976665496826,-1.4136744737625122,-0.2573896050453186]]}";
handler.assertResponse(url, properties, 200, expected);
}
@@ -171,7 +172,7 @@ public class ModelsEvaluationHandlerTest {
Map<String, String> properties = new HashMap<>();
properties.put("Placeholder", inputTensor());
String url = "http://localhost/model-evaluation/v1/mnist_softmax/default.add/eval";
- String expected = "{\"cells\":[{\"address\":{\"d0\":\"0\",\"d1\":\"0\"},\"value\":-0.3546536862850189},{\"address\":{\"d0\":\"0\",\"d1\":\"1\"},\"value\":0.3759574592113495},{\"address\":{\"d0\":\"0\",\"d1\":\"2\"},\"value\":0.06054411828517914},{\"address\":{\"d0\":\"0\",\"d1\":\"3\"},\"value\":-0.251544713973999},{\"address\":{\"d0\":\"0\",\"d1\":\"4\"},\"value\":0.017951013520359993},{\"address\":{\"d0\":\"0\",\"d1\":\"5\"},\"value\":1.2899067401885986},{\"address\":{\"d0\":\"0\",\"d1\":\"6\"},\"value\":-0.10389615595340729},{\"address\":{\"d0\":\"0\",\"d1\":\"7\"},\"value\":0.6367976665496826},{\"address\":{\"d0\":\"0\",\"d1\":\"8\"},\"value\":-1.4136744737625122},{\"address\":{\"d0\":\"0\",\"d1\":\"9\"},\"value\":-0.2573896050453186}]}";
+ String expected = "{\"type\":\"tensor(d0[],d1[10])\",\"values\":[[-0.3546536862850189,0.3759574592113495,0.06054411828517914,-0.251544713973999,0.017951013520359993,1.2899067401885986,-0.10389615595340729,0.6367976665496826,-1.4136744737625122,-0.2573896050453186]]}";
handler.assertResponse(url, properties, 200, expected);
}
@@ -179,6 +180,7 @@ public class ModelsEvaluationHandlerTest {
public void testMnistSoftmaxEvaluateSpecificFunctionWithBindingsShortForm() {
Map<String, String> properties = new HashMap<>();
properties.put("Placeholder", inputTensorShortForm());
+ properties.put("format.tensors", "long");
String url = "http://localhost/model-evaluation/v1/mnist_softmax/default.add/eval";
String expected = "{\"cells\":[{\"address\":{\"d0\":\"0\",\"d1\":\"0\"},\"value\":-0.3546536862850189},{\"address\":{\"d0\":\"0\",\"d1\":\"1\"},\"value\":0.3759574592113495},{\"address\":{\"d0\":\"0\",\"d1\":\"2\"},\"value\":0.06054411828517914},{\"address\":{\"d0\":\"0\",\"d1\":\"3\"},\"value\":-0.251544713973999},{\"address\":{\"d0\":\"0\",\"d1\":\"4\"},\"value\":0.017951013520359993},{\"address\":{\"d0\":\"0\",\"d1\":\"5\"},\"value\":1.2899067401885986},{\"address\":{\"d0\":\"0\",\"d1\":\"6\"},\"value\":-0.10389615595340729},{\"address\":{\"d0\":\"0\",\"d1\":\"7\"},\"value\":0.6367976665496826},{\"address\":{\"d0\":\"0\",\"d1\":\"8\"},\"value\":-1.4136744737625122},{\"address\":{\"d0\":\"0\",\"d1\":\"9\"},\"value\":-0.2573896050453186}]}";
handler.assertResponse(url, properties, 200, expected);
diff --git a/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/DocumentV1ApiHandler.java b/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/DocumentV1ApiHandler.java
index 8c2e39d595e..626d4f57c58 100644
--- a/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/DocumentV1ApiHandler.java
+++ b/vespaclient-container-plugin/src/main/java/com/yahoo/document/restapi/resource/DocumentV1ApiHandler.java
@@ -713,12 +713,17 @@ public class DocumentV1ApiHandler extends AbstractRequestHandler {
}
}
- synchronized void writeSingleDocument(Document document) throws IOException {
- boolean tensorShortForm = false;
- if (request != null && request.parameters().containsKey("format.tensors")) {
- tensorShortForm = request.parameters().get("format.tensors").contains("short");
+ private boolean tensorShortForm() {
+ if (request != null &&
+ request.parameters().containsKey("format.tensors") &&
+ request.parameters().get("format.tensors").contains("long")) {
+ return false;
}
- new JsonWriter(json, tensorShortForm).writeFields(document);
+ return true; // default
+ }
+
+ synchronized void writeSingleDocument(Document document) throws IOException {
+ new JsonWriter(json, tensorShortForm()).writeFields(document);
}
synchronized void writeDocumentsArrayStart() throws IOException {
@@ -737,7 +742,7 @@ public class DocumentV1ApiHandler extends AbstractRequestHandler {
ByteArrayOutputStream myOut = new ByteArrayOutputStream(1);
myOut.write(','); // Prepend rather than append, to avoid double memory copying.
try (JsonGenerator myJson = jsonFactory.createGenerator(myOut)) {
- new JsonWriter(myJson).write(document);
+ new JsonWriter(myJson, tensorShortForm()).write(document);
}
docs.add(myOut);
diff --git a/vespaclient-container-plugin/src/test/cfg/music.sd b/vespaclient-container-plugin/src/test/cfg/music.sd
index 75a1918de5b..86597379dd3 100644
--- a/vespaclient-container-plugin/src/test/cfg/music.sd
+++ b/vespaclient-container-plugin/src/test/cfg/music.sd
@@ -2,5 +2,6 @@
search music {
document music {
field artist type string { }
+ field embedding type tensor(x[3]) {}
}
}
diff --git a/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/resource/DocumentV1ApiTest.java b/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/resource/DocumentV1ApiTest.java
index bdb6c6d5580..74a86b6a7b7 100644
--- a/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/resource/DocumentV1ApiTest.java
+++ b/vespaclient-container-plugin/src/test/java/com/yahoo/document/restapi/resource/DocumentV1ApiTest.java
@@ -14,6 +14,7 @@ import com.yahoo.document.FixedBucketSpaces;
import com.yahoo.document.TestAndSetCondition;
import com.yahoo.document.config.DocumentmanagerConfig;
import com.yahoo.document.datatypes.StringFieldValue;
+import com.yahoo.document.datatypes.TensorFieldValue;
import com.yahoo.document.restapi.DocumentOperationExecutorConfig;
import com.yahoo.document.restapi.resource.DocumentV1ApiHandler.StorageCluster;
import com.yahoo.document.update.FieldUpdate;
@@ -51,6 +52,7 @@ import com.yahoo.schema.derived.Deriver;
import com.yahoo.slime.Inspector;
import com.yahoo.slime.JsonFormat;
import com.yahoo.slime.SlimeUtils;
+import com.yahoo.tensor.Tensor;
import com.yahoo.test.ManualClock;
import com.yahoo.vdslib.VisitorStatistics;
import com.yahoo.vespa.config.content.AllClustersBucketSpacesConfig;
@@ -118,7 +120,9 @@ public class DocumentV1ApiTest {
final Document doc3 = new Document(manager.getDocumentType("music"), "id:space:music:g=a:three");
{
doc1.setFieldValue("artist", "Tom Waits");
+ doc1.setFieldValue("embedding", new TensorFieldValue(Tensor.from("tensor(x[3]):[1,2,3]")));
doc2.setFieldValue("artist", "Asa-Chan & Jun-Ray");
+ doc2.setFieldValue("embedding", new TensorFieldValue(Tensor.from("tensor(x[3]):[4,5,6]")));
}
final Map<String, StorageCluster> clusters = Map.of("content", new StorageCluster("content",
@@ -229,13 +233,15 @@ public class DocumentV1ApiTest {
" {" +
" \"id\": \"id:space:music::one\"," +
" \"fields\": {" +
- " \"artist\": \"Tom Waits\"" +
+ " \"artist\": \"Tom Waits\", " +
+ " \"embedding\": { \"type\": \"tensor(x[3])\", \"values\": [1.0,2.0,3.0] } " +
" }" +
" }," +
" {" +
" \"id\": \"id:space:music:n=1:two\"," +
" \"fields\": {" +
- " \"artist\": \"Asa-Chan & Jun-Ray\"" +
+ " \"artist\": \"Asa-Chan & Jun-Ray\", " +
+ " \"embedding\": { \"type\": \"tensor(x[3])\", \"values\": [4.0,5.0,6.0] } " +
" }" +
" }," +
" {" +
@@ -278,13 +284,15 @@ public class DocumentV1ApiTest {
" {" +
" \"id\": \"id:space:music::one\"," +
" \"fields\": {" +
- " \"artist\": \"Tom Waits\"" +
+ " \"artist\": \"Tom Waits\"," +
+ " \"embedding\": { \"type\": \"tensor(x[3])\", \"values\": [1.0,2.0,3.0] } " +
" }" +
" }," +
" {" +
" \"id\": \"id:space:music:n=1:two\"," +
" \"fields\": {" +
- " \"artist\": \"Asa-Chan & Jun-Ray\"" +
+ " \"artist\": \"Asa-Chan & Jun-Ray\"," +
+ " \"embedding\": { \"type\": \"tensor(x[3])\", \"values\": [4.0,5.0,6.0] } " +
" }" +
" }" +
" ]," +
@@ -491,12 +499,13 @@ public class DocumentV1ApiTest {
parameters.responseHandler().get().handleResponse(new DocumentResponse(0, doc1));
return new Result();
});
- response = driver.sendRequest("http://localhost/document/v1/space/music/docid/one?");
+ response = driver.sendRequest("http://localhost/document/v1/space/music/docid/one?format.tensors=long");
assertSameJson("{" +
" \"pathId\": \"/document/v1/space/music/docid/one\"," +
" \"id\": \"id:space:music::one\"," +
" \"fields\": {" +
- " \"artist\": \"Tom Waits\"" +
+ " \"artist\": \"Tom Waits\"," +
+ " \"embedding\": { \"cells\": [{\"address\":{\"x\":\"0\"},\"value\":1.0},{\"address\":{\"x\":\"1\"},\"value\": 2.0},{\"address\":{\"x\":\"2\"},\"value\": 3.0}]}" +
" }" +
"}", response.readAll());
assertEquals(200, response.getStatus());
@@ -532,7 +541,8 @@ public class DocumentV1ApiTest {
response = driver.sendRequest("http://localhost/document/v1/space/music/number/1/two?condition=test%20it&tracelevel=9", POST,
"{" +
" \"fields\": {" +
- " \"artist\": \"Asa-Chan & Jun-Ray\"" +
+ " \"artist\": \"Asa-Chan & Jun-Ray\"," +
+ " \"embedding\": { \"values\": [4.0,5.0,6.0] } " +
" }" +
"}");
assertSameJson("{" +
diff --git a/vespajlib/src/main/java/com/yahoo/tensor/serialization/JsonFormat.java b/vespajlib/src/main/java/com/yahoo/tensor/serialization/JsonFormat.java
index d7758f309db..b683519988c 100644
--- a/vespajlib/src/main/java/com/yahoo/tensor/serialization/JsonFormat.java
+++ b/vespajlib/src/main/java/com/yahoo/tensor/serialization/JsonFormat.java
@@ -109,11 +109,15 @@ public class JsonFormat {
private static void encodeValues(IndexedTensor tensor, Cursor cursor, long[] indexes, int dimension) {
DimensionSizes sizes = tensor.dimensionSizes();
- for (indexes[dimension] = 0; indexes[dimension] < sizes.size(dimension); ++indexes[dimension]) {
- if (dimension < (sizes.dimensions() - 1)) {
- encodeValues(tensor, cursor.addArray(), indexes, dimension + 1);
- } else {
- cursor.addDouble(tensor.get(indexes));
+ if (indexes.length == 0) {
+ cursor.addDouble(tensor.get(0));
+ } else {
+ for (indexes[dimension] = 0; indexes[dimension] < sizes.size(dimension); ++indexes[dimension]) {
+ if (dimension < (sizes.dimensions() - 1)) {
+ encodeValues(tensor, cursor.addArray(), indexes, dimension + 1);
+ } else {
+ cursor.addDouble(tensor.get(indexes));
+ }
}
}
}