From fe4d943cb2afb7c9005619ceef3af83d2b083c1c Mon Sep 17 00:00:00 2001 From: carb0n <58676303+carb0n@users.noreply.github.com> Date: Tue, 4 Aug 2020 00:22:44 -0400 Subject: [PATCH] add constant_value_as_shape tests will fail until next commit --- src/TensorFlowNET.Core/Tensors/tensor_util.cs | 198 ++++++++++++++++++ 1 file changed, 198 insertions(+) diff --git a/src/TensorFlowNET.Core/Tensors/tensor_util.cs b/src/TensorFlowNET.Core/Tensors/tensor_util.cs index 16aaea44..c50d807f 100644 --- a/src/TensorFlowNET.Core/Tensors/tensor_util.cs +++ b/src/TensorFlowNET.Core/Tensors/tensor_util.cs @@ -216,6 +216,204 @@ namespace Tensorflow return tensor_proto; } + public static TensorShape constant_value_as_shape(Tensor tensor) + { + bool hasattr(Graph property, string attr) + { + var t = property.GetType().GetProperties(); + foreach (System.Reflection.PropertyInfo pi in t) + { + if (pi.Name == attr) + return true; + } + return false; + } + + if (tensor.GetType() == typeof(EagerTensor)) + { + int[] dims = {}; + foreach (int dim in tensor.numpy()) + if (dim != 1) + { + dims[dims.Length] = dim; + } else + { + // -1 == Unknown + dims[dims.Length] = -1; + } + return new TensorShape(dims); + } + + if (tensor.TensorShape.ndim == 0) + { + var value_ = constant_value(tensor); + if (value_ == null) + throw new ValueError( + @"Received a scalar with unknown value as shape; require a statically +known scalar with value '-1' to describe an unknown shape."); + if (value_ != -1) + throw new ValueError( + String.Format(@"Received a scalar value {0} as shape; require a statically known +scalar with value '-1' to describe an unknown shape.", value_)); + return tensor.TensorShape.unknown_shape(-1); + } + + var shape = tensor.TensorShape.with_rank(1); + if (shape == new TensorShape(new int[] {1})) + { + return new TensorShape(new int[] {}); + } else if (tensor.op.type == "Cast") + { + var pre_cast = constant_value_as_shape(tensor.op.inputs[0]); + if (pre_cast.dims == null) + return pre_cast; + var cast_dtype = dtypes.as_dtype((Type)tensor.op.get_attr("DstT")); + if (!Array.Exists(new [] {dtypes.int32, dtypes.int64}, cast_dtype_ => cast_dtype_ == cast_dtype)) + return tensor.TensorShape.unknown_shape(shape.dims[0]); + + int[] x_ = {}; + foreach (var x in pre_cast.as_list()) + if (x != -1) + x_[x_.Length] = x; + else + x_[x_.Length] = -1; + var dest_dtype_shape_array = np.array(x_).astype(cast_dtype.as_numpy_dtype()); + + int[] y_ = {}; + foreach(int y in dest_dtype_shape_array) + if (y >= 0) + y_[y_.Length] = y; + else + y_[y_.Length] = -1; + return new TensorShape(y_); + } else if (tensor.op.type == "Shape") + { + return tensor.op.inputs[0].shape; + } else if (tensor.op.type == "Pack") + { + var ret_ = new TensorShape(new int[] {}); + if ((int)tensor.op.get_attr("axis") != 0) + throw new ValueError(String.Format( + @"Since rank 1 inputs are expected, Pack's axis: {0} must be 0, otherwise it +would not be rank 1.", tensor.op.get_attr("axis"))); + foreach (Tensor pack_input in tensor.op.inputs) + { + var pack_input_val = constant_value(pack_input); + Dimension new_dim; + if (pack_input_val < 0) + { + new_dim = new Dimension(-1); + } else if (pack_input_val == null) + { + new_dim = new Dimension(-1); + } else + { + new_dim = new Dimension(pack_input_val); + } + ret_ = ret_.concatenate(new int[] {new_dim}); + } + return ret_; + } else if (tensor.op.type == "Concat") + { + var ret_ = new TensorShape(new int[] {}); + + var inputlist_ = new ArraySegment(tensor.op.inputs, 1, + tensor.op.inputs.Length - 1); + foreach (var concat_input in inputlist_) + { + ret_ = ret_.concatenate(constant_value_as_shape(concat_input)); + } + return ret_; + } else if (tensor.op.type == "StridedSlice") + { + try + { + var begin = constant_value(tensor.op.inputs[1]); + var end = constant_value(tensor.op.inputs[2]); + var strides = constant_value(tensor.op.inputs[3]); + if (new [] {begin, end, strides}.All(x => x == null)) + { + begin = begin[0]; + end = end[0]; + strides = strides[0]; + var begin_mask = tensor.op.get_attr("begin_mask"); + if ((int)begin_mask == 1) + { + begin = null; + } + var end_mask = tensor.op.get_attr("end_mask"); + if ((int)end_mask == 1) + { + end = null; + } + + var ellipsis_mask = tensor.op.get_attr("ellipsis_mask"); + var new_axis_mask = tensor.op.get_attr("new_axis_mask"); + var shrink_axis_mask = tensor.op.get_attr("shrink_axis_mask"); + + bool valid_attributes; + if (!(bool)ellipsis_mask && !(bool)new_axis_mask && + !(bool)shrink_axis_mask && !((bool)begin_mask || (int)begin_mask == 1) && + !((bool)end_mask || (int)end_mask == 1)) + { + valid_attributes = true; + } else {valid_attributes = false;} + if (valid_attributes) + { + // sorry for the mess here, but this hacky solution was the best way + // i could come up with to implement the things done in python in c# + var prev_ = constant_value_as_shape(tensor.op.inputs[0]).dims; + var prev = prev_.Skip(begin).Take(end - begin).ToArray(); + // 100 being the comparison doesn't really matter here; it's going to break anyway + for (int iter = 0; iter != 100; iter = iter + strides) + { + prev[prev.Length] = prev_[iter]; + if ((iter + strides) > prev_.Length) + break; + } + var ret_ = new TensorShape(prev); + return ret_; + } + } + } catch (Exception ex) + { + if (ex is ValueError || ex is TypeError) {} + } + } else if (tensor.op.type == "Placeholder" && + tensor.op.graph.building_function && + hasattr(tensor.op.graph, "internal_captures")) + { + int i = 0; + foreach (Tensor capture in tensor.op.graph.internal_captures()) + { + if (capture.GetType() == typeof(Tensor)) + { + var external_capture = tensor.op.graph.external_captures()[i]; + return constant_value_as_shape(external_capture); + } + + i++; + } + } + + var ret = tensor.TensorShape.unknown_shape(shape.dims[0]); + var value = constant_value(tensor); + if (value != null) + { + int[] d_ = {}; + foreach (int d in value) + { + if (d >= 0) + d_[d_.Length] = d; + else + d_[d_.Length] = -1; // None + } + ret = ret.merge_with(new TensorShape(d_)); + + } + return ret; + } + public static NDArray convert_to_numpy_ndarray(object values) { NDArray nd;