Skip to content

Commit

Permalink
Support upto 4D array as REST input payload.
Browse files Browse the repository at this point in the history
  • Loading branch information
ScrapCodes committed Nov 11, 2021
1 parent b3b7ec8 commit 94abb4f
Show file tree
Hide file tree
Showing 4 changed files with 251 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@ google/
# general
.env
.bash_history

# IDE
.idea/
18 changes: 17 additions & 1 deletion proxy/marshaler.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,6 @@ func (c *CustomJSONPb) NewDecoder(r io.Reader) runtime.Decoder {
req.Inputs = make([]*gw.ModelInferRequest_InferInputTensor, 0, len(restReq.Inputs))

// TODO: Figure out better/cleaner way to do type coercion?
// TODO: Flatten N-Dimensional data arrays.

for index, input := range restReq.Inputs {
tensor := &gw.ModelInferRequest_InferInputTensor{
Expand All @@ -172,6 +171,23 @@ func (c *CustomJSONPb) NewDecoder(r io.Reader) runtime.Decoder {
Parameters: input.Parameters,
}
d := input.Data.([]interface{})
var err error
if len(d) != int(elementCount(tensor.Shape)) { // i.e. input is not already flattened.
switch len(tensor.Shape) {
case 2:
d, err = flatten2D(input)
case 3:
d, err = flatten3D(input)
case 4:
d, err = flatten4D(input)
default:
return fmt.Errorf("Unsupported dimension %d, maximum supported dimension is 4.",
len(tensor.Shape))
}
if err != nil {
return err
}
}
switch tensor.Datatype {
case BOOL:
data := make([]bool, len(d))
Expand Down
108 changes: 108 additions & 0 deletions proxy/n-dim-marshaler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package main

import (
"encoding/json"
"fmt"
"strings"
)

func flatten2D(tensor Tensor) ([]interface{}, error) {
totalLen := int(elementCount(tensor.Shape))
inputData := tensor.Data.([]interface{})
flattenedOutputData := make([]interface{}, totalLen)
// we need to flatten the array into 1D
var arrayDeserialized []float64
var arrayBool []bool
var err error
n1, n2 := int(tensor.Shape[0]), int(tensor.Shape[1])
for j := 0; j < n1; j++ {
str := fmt.Sprintf("%v", inputData[j])
str = strings.Replace(str, " ", ", ", -1) // convert it to a valid json
if tensor.Datatype == BOOL {
err = json.Unmarshal([]byte(str), &arrayBool)
} else {
err = json.Unmarshal([]byte(str), &arrayDeserialized)
}
if err != nil {
return nil, fmt.Errorf("found error while deserializing the 2D array, with shape: %v. Error: %s",
tensor.Shape, err.Error())
}
for z := 0; z < n2; z++ { // using row major order to flatten a 2D array.
if tensor.Datatype == BOOL {
flattenedOutputData[z+(j*n2)] = arrayBool[z]
} else {
flattenedOutputData[z+(j*n2)] = arrayDeserialized[z]
}
}
}
return flattenedOutputData, nil
}

func flatten3D(tensor Tensor) ([]interface{}, error) {
totalLen := int(elementCount(tensor.Shape))
inputData := tensor.Data.([]interface{})
flattenedOutputData := make([]interface{}, totalLen)
var arrayDeserialized [][]float64
var arrayBool [][]bool
var err error
n1, n2, n3 := int(tensor.Shape[0]), int(tensor.Shape[1]), int(tensor.Shape[2])
for j := 0; j < n1; j++ {
str := fmt.Sprintf("%v", inputData[j])
str = strings.Replace(str, " ", ", ", -1)
if tensor.Datatype == BOOL {
err = json.Unmarshal([]byte(str), &arrayBool)
} else {
err = json.Unmarshal([]byte(str), &arrayDeserialized)
}
if err != nil {
return nil, fmt.Errorf("found error while deserializing the 3D array, with shape: %v. Error: %s",
tensor.Shape, err.Error())
}
for z := 0; z < n2; z++ {
for k := 0; k < n3; k++ {
if tensor.Datatype == BOOL {
flattenedOutputData[k+j*n2*n3+z*n3] = arrayBool[z][k]
} else {
flattenedOutputData[k+j*n2*n3+z*n3] = arrayDeserialized[z][k]
}
}
}
}
return flattenedOutputData, nil
}

func flatten4D(tensor Tensor) ([]interface{}, error) {
totalLen := int(elementCount(tensor.Shape))
inputData := tensor.Data.([]interface{})
flattenedOutputData := make([]interface{}, totalLen)
var arrayDeserialized [][][]float64
var arrayBool [][][]bool
var err error
n1, n2, n3, n4 := int(tensor.Shape[0]), int(tensor.Shape[1]), int(tensor.Shape[2]), int(tensor.Shape[3])
for j := 0; j < n1; j++ {
str := fmt.Sprintf("%v", inputData[j])
str = strings.Replace(str, " ", ", ", -1) // convert to a valid json
if tensor.Datatype == BOOL {
err = json.Unmarshal([]byte(str), &arrayBool)
} else {
err = json.Unmarshal([]byte(str), &arrayDeserialized)

}
if err != nil {
return nil, fmt.Errorf("found error while deserializing the 4D array, with shape: %v. Error: %s",
tensor.Shape, err.Error())
}
for z := 0; z < n2; z++ {
for k := 0; k < n3; k++ {
for l := 0; l < n4; l++ {
if tensor.Datatype == BOOL {
flattenedOutputData[k*n4+j*n2*n3*n4+z*n3*n4+l] = arrayBool[z][k][l]
} else {
flattenedOutputData[k*n4+j*n2*n3*n4+z*n3*n4+l] = arrayDeserialized[z][k][l]
}
}
}
}
}
return flattenedOutputData, nil
}
123 changes: 123 additions & 0 deletions proxy/n-dim-marshaler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package main

import (
"github.com/google/go-cmp/cmp"
"testing"
)

var inputTensor2D = Tensor{
Name: "test_tensor2d",
Datatype: "FP32",
Shape: []int64{2, 64},
Parameters: nil,
Data: []interface{}{
[]interface{}{0.0, 0.0, 1.0, 11.0, 14.0, 15.0, 3.0, 0.0, 0.0, 1.0, 13.0, 16.0, 12.0, 16.0, 8.0, 0.0, 0.0, 8.0,
16.0, 4.0, 6.0, 16.0, 5.0, 0.0, 0.0, 5.0, 15.0, 11.0, 13.0, 14.0, 0.0, 0.0, 0.0, 0.0, 2.0, 12.0, 16.0, 13.0,
0.0, 0.0, 0.0, 0.0, 0.0, 13.0, 16.0, 16.0, 6.0, 0.0, 0.0, 0.0, 0.0, 16.0, 16.0, 16.0, 7.0, 0.0, 0.0, 0.0,
0.0, 11.0, 13.0, 12.0, 1.0, 0.0},
[]interface{}{2.0, 1.0, 4.0, 9.0, 8.0, 16.0, 2.0, 7.0, 9.0, 1.0, 12.0, 16.0, 12.0, 16.0, 8.0, 0.0, 0.0, 8.0,
16.0, 4.0, 6.0, 16.0, 5.0, 0.0, 0.0, 5.0, 15.0, 11.0, 13.0, 14.0, 0.0, 0.0, 0.0, 0.0, 2.0, 12.0, 16.0, 13.0,
0.0, 0.0, 0.0, 0.0, 0.0, 13.0, 16.0, 16.0, 6.0, 0.0, 0.0, 0.0, 0.0, 16.0, 16.0, 16.0, 7.0, 0.0, 0.0, 0.0,
0.0, 11.0, 13.0, 12.0, 1.0, 0.0},
},
}

var inputTensor3D = Tensor{
Name: "test_tensor3d",
Datatype: "FP32",
Shape: []int64{2, 2, 32},
Parameters: nil,
Data: []interface{}{
[]interface{}{
[]interface{}{0.0, 0.0, 1.0, 11.0, 14.0, 15.0, 3.0, 0.0, 0.0, 1.0, 13.0, 16.0, 12.0, 16.0, 8.0, 0.0, 0.0,
8.0, 16.0, 4.0, 6.0, 16.0, 5.0, 0.0, 0.0, 5.0, 15.0, 11.0, 13.0, 14.0, 0.0, 0.0},
[]interface{}{0.0, 0.0, 2.0, 12.0, 16.0, 13.0, 0.0, 0.0, 0.0, 0.0, 0.0, 13.0, 16.0, 16.0, 6.0, 0.0, 0.0,
0.0, 0.0, 16.0, 16.0, 16.0, 7.0, 0.0, 0.0, 0.0, 0.0, 11.0, 13.0, 12.0, 1.0, 0.0},
},
[]interface{}{
[]interface{}{2.0, 1.0, 4.0, 9.0, 8.0, 16.0, 2.0, 7.0, 9.0, 1.0, 12.0, 16.0, 12.0, 16.0, 8.0, 0.0, 0.0,
8.0, 16.0, 4.0, 6.0, 16.0, 5.0, 0.0, 0.0, 5.0, 15.0, 11.0, 13.0, 14.0, 0.0, 0.0},
[]interface{}{0.0, 0.0, 2.0, 12.0, 16.0, 13.0, 0.0, 0.0, 0.0, 0.0, 0.0, 13.0, 16.0, 16.0, 6.0, 0.0, 0.0,
0.0, 0.0, 16.0, 16.0, 16.0, 7.0, 0.0, 0.0, 0.0, 0.0, 11.0, 13.0, 12.0, 1.0, 0.0},
},
},
}

var inputTensor4D = Tensor{
Name: "test_tensor4d",
Datatype: "FP32",
Shape: []int64{2, 2, 2, 16},
Parameters: nil,
Data: []interface{}{
[]interface{}{
[]interface{}{
[]interface{}{0.0, 0.0, 1.0, 11.0, 14.0, 15.0, 3.0, 0.0, 0.0, 1.0, 13.0, 16.0, 12.0, 16.0, 8.0, 0.0},
[]interface{}{0.0, 8.0, 16.0, 4.0, 6.0, 16.0, 5.0, 0.0, 0.0, 5.0, 15.0, 11.0, 13.0, 14.0, 0.0, 0.0},
},
[]interface{}{
[]interface{}{0.0, 0.0, 2.0, 12.0, 16.0, 13.0, 0.0, 0.0, 0.0, 0.0, 0.0, 13.0, 16.0, 16.0, 6.0, 0.0},
[]interface{}{0.0, 0.0, 0.0, 16.0, 16.0, 16.0, 7.0, 0.0, 0.0, 0.0, 0.0, 11.0, 13.0, 12.0, 1.0, 0.0},
},
},
[]interface{}{
[]interface{}{
[]interface{}{2.0, 1.0, 4.0, 9.0, 8.0, 16.0, 2.0, 7.0, 9.0, 1.0, 12.0, 16.0, 12.0, 16.0, 8.0, 0.0},
[]interface{}{0.0, 8.0, 16.0, 4.0, 6.0, 16.0, 5.0, 0.0, 0.0, 5.0, 15.0, 11.0, 13.0, 14.0, 0.0, 0.0},
},
[]interface{}{
[]interface{}{0.0, 0.0, 2.0, 12.0, 16.0, 13.0, 0.0, 0.0, 0.0, 0.0, 0.0, 13.0, 16.0, 16.0, 6.0, 0.0},
[]interface{}{0.0, 0.0, 0.0, 16.0, 16.0, 16.0, 7.0, 0.0, 0.0, 0.0, 0.0, 11.0, 13.0, 12.0, 1.0, 0.0},
},
},
},
}

var expectedOutput = []interface{}{0.0, 0.0, 1.0, 11.0, 14.0, 15.0, 3.0, 0.0, 0.0, 1.0, 13.0, 16.0, 12.0, 16.0, 8.0,
0.0, 0.0, 8.0, 16.0, 4.0, 6.0, 16.0, 5.0, 0.0, 0.0, 5.0, 15.0, 11.0, 13.0, 14.0, 0.0, 0.0, 0.0, 0.0, 2.0, 12.0,
16.0, 13.0, 0.0, 0.0, 0.0, 0.0, 0.0, 13.0, 16.0, 16.0, 6.0, 0.0, 0.0, 0.0, 0.0, 16.0, 16.0, 16.0, 7.0, 0.0, 0.0,
0.0, 0.0, 11.0, 13.0, 12.0, 1.0, 0.0, 2.0, 1.0, 4.0, 9.0, 8.0, 16.0, 2.0, 7.0, 9.0, 1.0, 12.0, 16.0, 12.0, 16.0,
8.0, 0.0, 0.0, 8.0, 16.0, 4.0, 6.0, 16.0, 5.0, 0.0, 0.0, 5.0, 15.0, 11.0, 13.0, 14.0, 0.0, 0.0, 0.0, 0.0, 2.0, 12.0,
16.0, 13.0, 0.0, 0.0, 0.0, 0.0, 0.0, 13.0, 16.0, 16.0, 6.0, 0.0, 0.0, 0.0, 0.0, 16.0, 16.0, 16.0, 7.0, 0.0, 0.0,
0.0, 0.0, 11.0, 13.0, 12.0, 1.0, 0.0}

func Test_flatten2D(t *testing.T) {
data, err := flatten2D(inputTensor2D)
if err != nil {
t.Errorf("Failed to parse 2D array. %s", err.Error())
}
if len(data) != int(elementCount(inputTensor2D.Shape)) {
t.Errorf("conversion failed, output array should have length: %d",
int(elementCount(inputTensor2D.Shape)))
}
if d := cmp.Diff(data, expectedOutput); d != "" {
t.Errorf("diff: %v", d)
}
}

func Test_flatten3D(t *testing.T) {
data, err := flatten3D(inputTensor3D)
if err != nil {
t.Errorf("Failed to parse 3D array. %s", err.Error())
}
if len(data) != int(elementCount(inputTensor3D.Shape)) {
t.Errorf("conversion failed, output array should have length: %d",
int(elementCount(inputTensor3D.Shape)))
}
if d := cmp.Diff(data, expectedOutput); d != "" {
t.Errorf("diff: %v", d)
}
}

func Test_flatten4D(t *testing.T) {
data, err := flatten4D(inputTensor4D)
if err != nil {
t.Errorf("Failed to parse 4D array. %s", err.Error())
}
if len(data) != int(elementCount(inputTensor4D.Shape)) {
t.Errorf("conversion failed, output array should have length: %d",
int(elementCount(inputTensor4D.Shape)))
}
if d := cmp.Diff(data, expectedOutput); d != "" {
t.Errorf("diff: %v", d)
}
}

0 comments on commit 94abb4f

Please sign in to comment.