| 314 | } |
| 315 | |
| 316 | void Compute(OpKernelContext* ctx) override { |
| 317 | const unsigned int src_idx = 0; |
| 318 | const Tensor& input = ctx->input(src_idx); |
| 319 | const float input_min_range = ctx->input(1).flat<float>()(0); |
| 320 | const float input_max_range = ctx->input(2).flat<float>()(0); |
| 321 | float min_range = std::min(0.0f, input_min_range); |
| 322 | float max_range; |
| 323 | OP_REQUIRES(ctx, (input_max_range >= input_min_range), |
| 324 | errors::InvalidArgument( |
| 325 | "input_max_range must be larger than input_min_range.")); |
| 326 | |
| 327 | // When the minimum and maximum ranges are too close together, nudge them |
| 328 | // apart by a small value so that they are slightly different. This helps |
| 329 | // us avoid creating ill-formed buffers where all quantized values map to |
| 330 | // the same float number. These kinds of buffers cause problems for |
| 331 | // downstream ops when they need to do calculations on them. |
| 332 | // We pick the value by making sure that zero is not more than 100x the |
| 333 | // overall range from the maximum, so that the value can be easily |
| 334 | // represented when we promote the quantized value to a higher |
| 335 | // intermediate bit depth, since that's a common requirement. |
| 336 | const float epsilon = std::max(1.0f, std::max(fabsf(input_min_range), |
| 337 | fabsf(input_max_range))) * |
| 338 | ensure_minimum_range_; |
| 339 | max_range = std::max(input_max_range, min_range + epsilon); |
| 340 | // Clamping the max_range to zero since max_range can also be negative. |
| 341 | max_range = std::max(0.0f, max_range); |
| 342 | auto cpu_engine = engine(ENGINE_CPU, 0); |
| 343 | const Tensor& src_tensor = MklGetInput(ctx, src_idx); |
| 344 | MklDnnShape src_mkl_shape; |
| 345 | GetMklShape(ctx, src_idx, &src_mkl_shape); |
| 346 | auto src_tf_shape = src_mkl_shape.IsMklTensor() ? src_mkl_shape.GetTfShape() |
| 347 | : src_tensor.shape(); |
| 348 | auto src_dims = src_mkl_shape.IsMklTensor() |
| 349 | ? src_mkl_shape.GetSizesAsMklDnnDims() |
| 350 | : TFShapeToMklDnnDims(src_tensor.shape()); |
| 351 | auto output_dims = src_dims; |
| 352 | // Set the dst layout to be the best mkl layout based on dims and type. |
| 353 | MEMORY_FORMAT dst_layout_type; |
| 354 | switch (src_tf_shape.dims()) { |
| 355 | case 0: |
| 356 | ComputeScalar(ctx, min_range, max_range); |
| 357 | return; |
| 358 | case 1: |
| 359 | dst_layout_type = MEMORY_FORMAT::x; |
| 360 | break; |
| 361 | case 2: |
| 362 | dst_layout_type = MEMORY_FORMAT::nc; |
| 363 | break; |
| 364 | case 3: |
| 365 | dst_layout_type = MEMORY_FORMAT::tnc; |
| 366 | break; |
| 367 | case 4: |
| 368 | dst_layout_type = MEMORY_FORMAT::nhwc; |
| 369 | break; |
| 370 | case 5: |
| 371 | dst_layout_type = MEMORY_FORMAT::ndhwc; |
| 372 | break; |
| 373 | default: |
nothing calls this directly
no test coverage detected