aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--tensorflow/contrib/lite/experimental/objc/BUILD94
-rw-r--r--tensorflow/contrib/lite/experimental/objc/README.md10
-rw-r--r--tensorflow/contrib/lite/experimental/objc/TensorFlowLiteObjc.tulsiproj/Configs/TensorFlowLiteObjc.tulsigen60
-rw-r--r--tensorflow/contrib/lite/experimental/objc/TensorFlowLiteObjc.tulsiproj/project.tulsiconf17
-rw-r--r--tensorflow/contrib/lite/experimental/objc/apis/TFLInterpreter.h188
-rw-r--r--tensorflow/contrib/lite/experimental/objc/apis/TFLInterpreterOptions.h37
-rw-r--r--tensorflow/contrib/lite/experimental/objc/apis/TFLQuantizationParameters.h36
-rw-r--r--tensorflow/contrib/lite/experimental/objc/apis/TFLTensor.h77
-rw-r--r--tensorflow/contrib/lite/experimental/objc/sources/TFLErrorUtil.h51
-rw-r--r--tensorflow/contrib/lite/experimental/objc/sources/TFLErrorUtil.m45
-rw-r--r--tensorflow/contrib/lite/experimental/objc/sources/TFLInterpreter.mm440
-rw-r--r--tensorflow/contrib/lite/experimental/objc/sources/TFLInterpreterOptions.m30
-rw-r--r--tensorflow/contrib/lite/experimental/objc/sources/TFLQuantizationParameters.m23
-rw-r--r--tensorflow/contrib/lite/experimental/objc/sources/TFLTensor+Internal.h42
-rw-r--r--tensorflow/contrib/lite/experimental/objc/sources/TFLTensor.m54
-rw-r--r--tensorflow/contrib/lite/experimental/objc/tests/TFLInterpreterOptionsTests.m49
-rw-r--r--tensorflow/contrib/lite/experimental/objc/tests/TFLInterpreterTests.m266
-rw-r--r--tensorflow/tools/pip_package/pip_smoke_test.py1
18 files changed, 1520 insertions, 0 deletions
diff --git a/tensorflow/contrib/lite/experimental/objc/BUILD b/tensorflow/contrib/lite/experimental/objc/BUILD
new file mode 100644
index 0000000000..236b96adb5
--- /dev/null
+++ b/tensorflow/contrib/lite/experimental/objc/BUILD
@@ -0,0 +1,94 @@
+# TensorFlow Lite Objective-C API.
+
+package(default_visibility = ["//visibility:private"])
+
+licenses(["notice"]) # Apache 2.0
+
+load("//tools/build_defs/apple:ios.bzl", "ios_unit_test")
+
+SOURCES = glob([
+ "sources/*.h",
+ "sources/*.m",
+ "sources/*.mm",
+])
+
+API_HEADERS = glob([
+ "apis/*.h",
+])
+
+MINIMUM_OS_VERSION = "8.0"
+
+# Compiler flags for building regular non-test libraries.
+RELEASE_COPTS = [
+ # Enables language-specific warnings for Objective-C, Objective-C++, C, and C++.
+ "-Wall",
+ # Warns if functions, variables, and types marked with the deprecated attribute are being used.
+ "-Wdeprecated-declarations",
+ # Warns for errors in documentation.
+ "-Wdocumentation",
+ # Turns all warnings into errors.
+ "-Werror",
+ # Enables extra warning flags that are not enabled by -Wall.
+ "-Wextra",
+ # Warns if a global function is defined without a previous prototype declaration.
+ "-Wmissing-prototypes",
+ # From -Wextra. Disables warning when signed value is converted to unsigned value during comparison.
+ "-Wno-sign-compare",
+ # From -Wextra. Disables warning for unused parameters, which are common in delegate methods and block callbacks.
+ "-Wno-unused-parameter",
+ # Warns if a global or local variable or type declaration shadows another variable, parameter, type, class member, or instance variable.
+ "-Wshadow",
+ # Warns if a function is declared or defined without specifying the argument types. For a block with no args, use (void) instead of ().
+ "-Wstrict-prototypes",
+ # Warns if an @selector() expression is encountered with a method name that hasn't been defined yet.
+ "-Wundeclared-selector",
+
+ # Turn off warnings for headers not part of TensorFlow Lite Objective-C API.
+ "--system-header-prefix=third_party/tensorflow/contrib/lite/experimental/c/",
+]
+
+# Compiler flags for building test libraries.
+TEST_COPTS = RELEASE_COPTS + [
+ # From -Wall. Disables warning when passing nil to a callee that requires a non-null argument.
+ "-Wno-nonnull",
+ # Disables warning when a global or local variable or type declaration shadows another.
+ "-Wno-shadow",
+]
+
+objc_library(
+ name = "TensorFlowLiteObjCLib",
+ srcs = SOURCES,
+ hdrs = API_HEADERS,
+ copts = RELEASE_COPTS,
+ deps = [
+ "//tensorflow/contrib/lite/experimental/c:c_api",
+ ],
+ alwayslink = 1,
+)
+
+ios_unit_test(
+ name = "TensorFlowLiteObjCTests",
+ size = "small",
+ minimum_os_version = MINIMUM_OS_VERSION,
+ deps = [":TensorFlowLiteObjCTestLib"],
+)
+
+objc_library(
+ name = "TensorFlowLiteObjCTestLib",
+ testonly = 1,
+ srcs = glob([
+ "tests/*.m",
+ ]),
+ hdrs = glob([
+ "apis/*.h",
+ "sources/*.h",
+ "tests/*.h",
+ ]),
+ copts = TEST_COPTS,
+ resources = [
+ "//tensorflow/contrib/lite:testdata/add.bin",
+ ],
+ deps = [
+ ":TensorFlowLiteObjCLib",
+ ],
+)
diff --git a/tensorflow/contrib/lite/experimental/objc/README.md b/tensorflow/contrib/lite/experimental/objc/README.md
new file mode 100644
index 0000000000..e8f150b1e8
--- /dev/null
+++ b/tensorflow/contrib/lite/experimental/objc/README.md
@@ -0,0 +1,10 @@
+# TensorFlow Lite Objective-C API
+
+## TensorFlowLiteObjc Tulsi Project
+
+Open the `TensorFlowLiteObjc.tulsiproj` using the Tulsi application on Mac or by
+running the following command in Terminal from the root source directory:
+
+```shell
+generate_xcodeproj.sh --genconfig tensorflow/contrib/lite/experimental/objc/TensorFlowLiteObjc.tulsiproj:TensorFlowLiteObjC --outputfolder ~/path/to/xcodeproj
+```
diff --git a/tensorflow/contrib/lite/experimental/objc/TensorFlowLiteObjc.tulsiproj/Configs/TensorFlowLiteObjc.tulsigen b/tensorflow/contrib/lite/experimental/objc/TensorFlowLiteObjc.tulsiproj/Configs/TensorFlowLiteObjc.tulsigen
new file mode 100644
index 0000000000..babb5902d3
--- /dev/null
+++ b/tensorflow/contrib/lite/experimental/objc/TensorFlowLiteObjc.tulsiproj/Configs/TensorFlowLiteObjc.tulsigen
@@ -0,0 +1,60 @@
+{
+ "sourceFilters" : [
+ "third_party/tensorflow/contrib/lite",
+ "third_party/tensorflow/contrib/lite/experimental/c",
+ "third_party/tensorflow/contrib/lite/experimental/objc",
+ "third_party/tensorflow/contrib/lite/experimental/objc/apis",
+ "third_party/tensorflow/contrib/lite/experimental/objc/sources",
+ "third_party/tensorflow/contrib/lite/experimental/objc/tests",
+ "third_party/tensorflow/contrib/lite/kernels",
+ "third_party/tensorflow/contrib/lite/kernels/internal",
+ "third_party/tensorflow/contrib/lite/nnapi",
+ "third_party/tensorflow/contrib/lite/schema",
+ ],
+ "buildTargets" : [
+ "//third_party/tensorflow/contrib/lite/experimental/objc:TensorFlowLiteObjCLib",
+ "//third_party/tensorflow/contrib/lite/experimental/objc:TensorFlowLiteObjCTests",
+ ],
+ "projectName" : "TensorFlowLiteObjC",
+ "optionSet" : {
+ "LaunchActionPreActionScript" : {
+ "p" : "$(inherited)"
+ },
+ "BazelBuildStartupOptionsRelease" : {
+ "p" : "$(inherited)"
+ },
+ "BazelBuildOptionsRelease" : {
+ "p" : "$(inherited)"
+ },
+ "BazelBuildOptionsDebug" : {
+ "p" : "$(inherited)"
+ },
+ "EnvironmentVariables" : {
+ "p" : "$(inherited)"
+ },
+ "BuildActionPreActionScript" : {
+ "p" : "$(inherited)"
+ },
+ "CommandlineArguments" : {
+ "p" : "$(inherited)"
+ },
+ "TestActionPreActionScript" : {
+ "p" : "$(inherited)"
+ },
+ "BazelBuildStartupOptionsDebug" : {
+ "p" : "$(inherited)"
+ },
+ "BuildActionPostActionScript" : {
+ "p" : "$(inherited)"
+ },
+ "TestActionPostActionScript" : {
+ "p" : "$(inherited)"
+ },
+ "LaunchActionPostActionScript" : {
+ "p" : "$(inherited)"
+ }
+ },
+ "additionalFilePaths" : [
+ "third_party/tensorflow/contrib/lite/experimental/objc/BUILD",
+ ]
+}
diff --git a/tensorflow/contrib/lite/experimental/objc/TensorFlowLiteObjc.tulsiproj/project.tulsiconf b/tensorflow/contrib/lite/experimental/objc/TensorFlowLiteObjc.tulsiproj/project.tulsiconf
new file mode 100644
index 0000000000..00299cd4cf
--- /dev/null
+++ b/tensorflow/contrib/lite/experimental/objc/TensorFlowLiteObjc.tulsiproj/project.tulsiconf
@@ -0,0 +1,17 @@
+{
+ "configDefaults" : {
+ "optionSet" : {
+ "BazelBuildOptionsDebug" : {
+ "p" : "--ios_minimum_os=8.0"
+ },
+ "BazelBuildOptionsRelease" : {
+ "p" : "--ios_minimum_os=8.0"
+ },
+ }
+ },
+ "projectName" : "TensorFlowLiteObjC",
+ "packages" : [
+ "third_party/tensorflow/contrib/lite/experimental/objc"
+ ],
+ "workspaceRoot" : "../../../../../../.."
+}
diff --git a/tensorflow/contrib/lite/experimental/objc/apis/TFLInterpreter.h b/tensorflow/contrib/lite/experimental/objc/apis/TFLInterpreter.h
new file mode 100644
index 0000000000..c07ffc06ff
--- /dev/null
+++ b/tensorflow/contrib/lite/experimental/objc/apis/TFLInterpreter.h
@@ -0,0 +1,188 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import <Foundation/Foundation.h>
+
+@class TFLInterpreterOptions;
+@class TFLTensor;
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * @enum TFLInterpreterErrorCode
+ * This enum specifies various error codes related to `TFLInterpreter`.
+ */
+typedef NS_ENUM(NSUInteger, TFLInterpreterErrorCode) {
+ /** Provided tensor index is invalid. */
+ TFLInterpreterErrorCodeInvalidTensorIndex,
+
+ /** Input data has invalid byte size. */
+ TFLInterpreterErrorCodeInvalidInputByteSize,
+
+ /** Provided shape is invalid. It must be a non-empty array of positive unsigned integers. */
+ TFLInterpreterErrorCodeInvalidShape,
+
+ /** Provided model cannot be loaded. */
+ TFLInterpreterErrorCodeFailedToLoadModel,
+
+ /** Failed to create `TFLInterpreter`. */
+ TFLInterpreterErrorCodeFailedToCreateInterpreter,
+
+ /** Failed to invoke `TFLInterpreter`. */
+ TFLInterpreterErrorCodeFailedToInvoke,
+
+ /** Failed to retrieve a tensor. */
+ TFLInterpreterErrorCodeFailedToGetTensor,
+
+ /** Failed to resize an input tensor. */
+ TFLInterpreterErrorCodeFailedToResizeInputTensor,
+
+ /** Failed to copy data into an input tensor. */
+ TFLInterpreterErrorCodeFailedToCopyDataToInputTensor,
+
+ /** Failed to get data from an output tensor. */
+ TFLInterpreterErrorCodeFailedToGetDataFromOutputTensor,
+
+ /** Failed to allocate memory for tensors. */
+ TFLInterpreterErrorCodeFailedToAllocateTensors,
+
+ /** Operaton not allowed without allocating memory for tensors first. */
+ TFLInterpreterErrorCodeAllocateTensorsRequired,
+
+ /** Operaton not allowed without invoking the interpreter first. */
+ TFLInterpreterErrorCodeInvokeInterpreterRequired,
+};
+
+/**
+ * A TensorFlow Lite model interpreter.
+ */
+@interface TFLInterpreter : NSObject
+
+/** The total number of input tensors. 0 if the interpreter creation failed. */
+@property(nonatomic, readonly) NSUInteger inputTensorCount;
+
+/** The total number of output tensors. 0 if the interpreter creation failed. */
+@property(nonatomic, readonly) NSUInteger outputTensorCount;
+
+/** Unavailable. */
+- (instancetype)init NS_UNAVAILABLE;
+
+/**
+ * Initializes a new TensorFlow Lite interpreter instance with the given model file path and the
+ * default interpreter options.
+ *
+ * @param modelPath An absolute path to a TensorFlow Lite model file stored locally on the device.
+ *
+ * @return A new instance of `TFLInterpreter` with the given model and the default interpreter
+ * options.
+ */
+- (instancetype)initWithModelPath:(NSString *)modelPath;
+
+/**
+ * Initializes a new TensorFlow Lite interpreter instance with the given model file path and
+ * options.
+ *
+ * @param modelPath An absolute path to a TensorFlow Lite model file stored locally on the device.
+ * @param options Options to use for configuring the TensorFlow Lite interpreter.
+ *
+ * @return A new instance of `TFLInterpreter` with the given model and options.
+ */
+- (instancetype)initWithModelPath:(NSString *)modelPath
+ options:(TFLInterpreterOptions *)options NS_DESIGNATED_INITIALIZER;
+
+/**
+ * Invokes the interpreter to run inference.
+ *
+ * @param error An optional error parameter populated when there is an error in invoking the
+ * interpreter.
+ *
+ * @return Whether the invocation is successful. Returns NO if an error occurred.
+ */
+- (BOOL)invokeWithError:(NSError **)error;
+
+/**
+ * Returns the input tensor at the given index.
+ *
+ * @param index The index of an input tensor.
+ * @param error An optional error parameter populated when there is an error in looking up the input
+ * tensor.
+ *
+ * @return The input tensor at the given index. `nil` if there is an error.
+ */
+- (nullable TFLTensor *)inputTensorAtIndex:(NSUInteger)index error:(NSError **)error;
+
+/**
+ * Returns the output tensor at the given index.
+ *
+ * @param index The index of an output tensor.
+ * @param error An optional error parameter populated when there is an error in looking up the
+ * output tensor.
+ *
+ * @return The output tensor at the given index. `nil` if there is an error.
+ */
+- (nullable TFLTensor *)outputTensorAtIndex:(NSUInteger)index error:(NSError **)error;
+
+/**
+ * Resizes the input tensor at the given index to the specified shape (an array of positive unsigned
+ * integers).
+ *
+ * @param index The index of an input tensor.
+ * @param shape Shape that the given input tensor should be resized to. It should be an array of
+ * positive unsigned integer(s) containing the size of each dimension.
+ * @param error An optional error parameter populated when there is an error in resizing the input
+ * tensor.
+ *
+ * @return Whether the input tensor was resized successfully. Returns NO if an error occurred.
+ */
+- (BOOL)resizeInputTensorAtIndex:(NSUInteger)index
+ toShape:(NSArray<NSNumber *> *)shape
+ error:(NSError **)error;
+
+/**
+ * Copies the given data into the input tensor at the given index. This is allowed only before the
+ * interpreter is invoked.
+ *
+ * @param data The data to set. The byte size of the data must match what's required by the given
+ * input tensor.
+ * @param index The index of an input tensor.
+ * @param error An optional error parameter populated when there is an error in setting the data.
+ *
+ * @return Whether the data was set into the input tensor successfully. Returns NO if an error
+ * occurred.
+ */
+- (BOOL)copyData:(NSData *)data toInputTensorAtIndex:(NSUInteger)index error:(NSError **)error;
+
+/**
+ * Gets the data from the output tensor at the given index. The interpreter invocation has to
+ * complete before the data can be retrieved from an output tensor.
+ *
+ * @param index The index of an output tensor.
+ * @param error An optional error parameter populated when there is an error in getting the data.
+ *
+ * @return The data of the output tensor at the given index. `nil` if there is an error.
+ */
+- (nullable NSData *)dataFromOutputTensorAtIndex:(NSUInteger)index error:(NSError **)error;
+
+/**
+ * Allocates memory for tensors.
+ *
+ * @param error An optional error parameter populated when there is an error in allocating memory.
+ *
+ * @return Whether memory allocation is successful. Returns NO if an error occurred.
+ */
+- (BOOL)allocateTensorsWithError:(NSError **)error;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/tensorflow/contrib/lite/experimental/objc/apis/TFLInterpreterOptions.h b/tensorflow/contrib/lite/experimental/objc/apis/TFLInterpreterOptions.h
new file mode 100644
index 0000000000..6461fbf017
--- /dev/null
+++ b/tensorflow/contrib/lite/experimental/objc/apis/TFLInterpreterOptions.h
@@ -0,0 +1,37 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/** Custom configuration options for a TensorFlow Lite interpreter. */
+@interface TFLInterpreterOptions : NSObject
+
+/**
+ * Maximum number of threads that the interpreter should run on. Defaults to 0 (unspecified, letting
+ * TensorFlow Lite to optimize the threading decision).
+ */
+@property(nonatomic) NSUInteger numberOfThreads;
+
+/**
+ * Initializes a new instance of `TFLInterpreterOptions`.
+ *
+ * @return A new instance of `TFLInterpreterOptions`.
+ */
+- (instancetype)init NS_DESIGNATED_INITIALIZER;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/tensorflow/contrib/lite/experimental/objc/apis/TFLQuantizationParameters.h b/tensorflow/contrib/lite/experimental/objc/apis/TFLQuantizationParameters.h
new file mode 100644
index 0000000000..3d5cf793c5
--- /dev/null
+++ b/tensorflow/contrib/lite/experimental/objc/apis/TFLQuantizationParameters.h
@@ -0,0 +1,36 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * Parameters for asymmetric quantization. Quantized values can be converted to float values using:
+ * `realValue = scale * (quantizedValue - zeroPoint)`.
+ */
+@interface TFLQuantizationParameters : NSObject
+
+/** Scale of asymmetric quantization. */
+@property(nonatomic, readonly) float scale;
+
+/** Zero point of asymmetric quantization. */
+@property(nonatomic, readonly) int32_t zeroPoint;
+
+/** Unavailable. */
+- (instancetype)init NS_UNAVAILABLE;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/tensorflow/contrib/lite/experimental/objc/apis/TFLTensor.h b/tensorflow/contrib/lite/experimental/objc/apis/TFLTensor.h
new file mode 100644
index 0000000000..d08b8fc0e9
--- /dev/null
+++ b/tensorflow/contrib/lite/experimental/objc/apis/TFLTensor.h
@@ -0,0 +1,77 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import <Foundation/Foundation.h>
+
+@class TFLQuantizationParameters;
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * @enum TFLTensorDataType
+ * This enum specifies supported TensorFlow Lite tensor data types.
+ */
+typedef NS_ENUM(NSUInteger, TFLTensorDataType) {
+ /** Tensor data type not available. This indicates an error with the model. */
+ TFLTensorDataTypeNoType,
+
+ /** 32-bit single precision floating point. */
+ TFLTensorDataTypeFloat32,
+
+ /** 32-bit signed integer. */
+ TFLTensorDataTypeInt32,
+
+ /** 8-bit unsigned integer. */
+ TFLTensorDataTypeUInt8,
+
+ /** 64-bit signed integer. */
+ TFLTensorDataTypeInt64,
+
+ /** Boolean. */
+ TFLTensorDataTypeBool,
+
+ /** 16-bit signed integer. */
+ TFLTensorDataTypeInt16,
+};
+
+/**
+ * An input or output tensor in a TensorFlow Lite model.
+ */
+@interface TFLTensor : NSObject
+
+/** Name of the tensor. */
+@property(nonatomic, readonly, copy) NSString *name;
+
+/** Data type of the tensor. */
+@property(nonatomic, readonly) TFLTensorDataType dataType;
+
+/**
+ * Shape of the tensor, an array of positive unsigned integer(s) containing the size of each
+ * dimension. For example: the shape of [[[1, 1, 1], [2, 2, 2]], [[3, 3, 3], [4, 4, 4]]] is
+ * [2, 2, 3].
+ */
+@property(nonatomic, readonly, copy) NSArray<NSNumber *> *shape;
+
+/** Number of bytes for the tensor data. */
+@property(nonatomic, readonly) NSUInteger byteSize;
+
+/** Parameters for asymmetric quantization. `nil` if the tensor does not use quantization. */
+@property(nonatomic, readonly, nullable) TFLQuantizationParameters *quantizationParameters;
+
+/** Unavailable. */
+- (instancetype)init NS_UNAVAILABLE;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/tensorflow/contrib/lite/experimental/objc/sources/TFLErrorUtil.h b/tensorflow/contrib/lite/experimental/objc/sources/TFLErrorUtil.h
new file mode 100644
index 0000000000..b6fd4763d6
--- /dev/null
+++ b/tensorflow/contrib/lite/experimental/objc/sources/TFLErrorUtil.h
@@ -0,0 +1,51 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import <Foundation/Foundation.h>
+
+#import "third_party/tensorflow/contrib/lite/experimental/objc/apis/TFLInterpreter.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/** Helper utility for error reporting. */
+@interface TFLErrorUtil : NSObject
+
+/**
+ * Creates and returns an interpreter error with the given error code and description.
+ *
+ * @param code Error code.
+ * @param description Error description.
+ *
+ * @return The created interpreter error with the given error code and description.
+ */
++ (NSError *)interpreterErrorWithCode:(TFLInterpreterErrorCode)code
+ description:(NSString *)description;
+
+/**
+ * Creates and saves an interpreter error with the given error code and description.
+ *
+ * @param code Error code.
+ * @param description Error description.
+ * @param error Pointer to where to save the created error. If `nil`, no error will be saved.
+ */
++ (void)saveInterpreterErrorWithCode:(TFLInterpreterErrorCode)code
+ description:(NSString *)description
+ error:(NSError **)error;
+
+/** Unavailable. */
+- (instancetype)init NS_UNAVAILABLE;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/tensorflow/contrib/lite/experimental/objc/sources/TFLErrorUtil.m b/tensorflow/contrib/lite/experimental/objc/sources/TFLErrorUtil.m
new file mode 100644
index 0000000000..756d69481c
--- /dev/null
+++ b/tensorflow/contrib/lite/experimental/objc/sources/TFLErrorUtil.m
@@ -0,0 +1,45 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import "TFLErrorUtil.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/** Error domain of TensorFlow Lite interpreter related errors. */
+static NSString *const TFLInterpreterErrorDomain = @"org.tensorflow.lite.interpreter";
+
+@implementation TFLErrorUtil
+
+#pragma mark - Public
+
++ (NSError *)interpreterErrorWithCode:(TFLInterpreterErrorCode)code
+ description:(NSString *)description {
+ return [NSError errorWithDomain:TFLInterpreterErrorDomain
+ code:code
+ userInfo:@{NSLocalizedDescriptionKey : description}];
+}
+
++ (void)saveInterpreterErrorWithCode:(TFLInterpreterErrorCode)code
+ description:(NSString *)description
+ error:(NSError **)error {
+ if (error) {
+ *error = [NSError errorWithDomain:TFLInterpreterErrorDomain
+ code:code
+ userInfo:@{NSLocalizedDescriptionKey : description}];
+ }
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/tensorflow/contrib/lite/experimental/objc/sources/TFLInterpreter.mm b/tensorflow/contrib/lite/experimental/objc/sources/TFLInterpreter.mm
new file mode 100644
index 0000000000..0f940a5cf3
--- /dev/null
+++ b/tensorflow/contrib/lite/experimental/objc/sources/TFLInterpreter.mm
@@ -0,0 +1,440 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import "third_party/tensorflow/contrib/lite/experimental/objc/apis/TFLInterpreter.h"
+
+#import "TFLErrorUtil.h"
+#import "TFLTensor+Internal.h"
+#import "third_party/tensorflow/contrib/lite/experimental/objc/apis/TFLInterpreterOptions.h"
+#import "third_party/tensorflow/contrib/lite/experimental/objc/apis/TFLTensor.h"
+
+#include "third_party/tensorflow/contrib/lite/experimental/c/c_api.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * @enum TFLTensorType
+ * This enum specifies input or output tensor types.
+ */
+typedef NS_ENUM(NSUInteger, TFLTensorType) {
+ /** Input tensor type. */
+ TFLTensorTypeInput,
+
+ /** Output tensor type. */
+ TFLTensorTypeOutput,
+};
+
+// Names used for indicating input or output in error messages.
+static NSString *const kTFLInputDirection = @"input";
+static NSString *const kTFLOutputDirection = @"output";
+
+/**
+ * Error reporter for TFLInterpreter.
+ *
+ * @param user_data User data. Not used.
+ * @param format Error message which may contain argument formatting specifiers.
+ * @param args Values of the arguments in the error message.
+ */
+static void TFLInterpreterErrorReporter(void *user_data, const char *format, va_list args) {
+ NSLog(@"%@", [[NSString alloc] initWithFormat:@(format) arguments:args]);
+}
+
+@interface TFLInterpreter ()
+
+/** TFL_Interpreter backed by C API. */
+@property(nonatomic, nullable) TFL_Interpreter *interpreter;
+
+/**
+ * An error in initializing the interpreter. If not `nil`, this error will be reported when the
+ * interpreter is used.
+ */
+@property(nonatomic, nullable) NSError *initializationError;
+
+@end
+
+@implementation TFLInterpreter
+
+#pragma mark - NSObject
+
+- (void)dealloc {
+ TFL_DeleteInterpreter(_interpreter);
+}
+
+#pragma mark - Public
+
+- (instancetype)initWithModelPath:(NSString *)modelPath {
+ return [self initWithModelPath:modelPath options:[[TFLInterpreterOptions alloc] init]];
+}
+
+- (instancetype)initWithModelPath:(NSString *)modelPath options:(TFLInterpreterOptions *)options {
+ self = [super init];
+
+ if (self != nil) {
+ const char *modelPathCString = modelPath.UTF8String;
+ NSString *pathErrorString =
+ [NSString stringWithFormat:@"Cannot load model from path (%@).", modelPath];
+ if (modelPathCString == nullptr) {
+ _initializationError =
+ [TFLErrorUtil interpreterErrorWithCode:TFLInterpreterErrorCodeFailedToLoadModel
+ description:pathErrorString];
+ return self;
+ }
+
+ TFL_Model *model = TFL_NewModelFromFile(modelPathCString);
+ if (model == nullptr) {
+ _initializationError =
+ [TFLErrorUtil interpreterErrorWithCode:TFLInterpreterErrorCodeFailedToLoadModel
+ description:pathErrorString];
+ return self;
+ }
+
+ TFL_InterpreterOptions *cOptions = TFL_NewInterpreterOptions();
+ if (cOptions == nullptr) {
+ _initializationError =
+ [TFLErrorUtil interpreterErrorWithCode:TFLInterpreterErrorCodeFailedToCreateInterpreter
+ description:@"Failed to create the interpreter."];
+ TFL_DeleteModel(model);
+ return self;
+ }
+
+ if (options.numberOfThreads > 0) {
+ TFL_InterpreterOptionsSetNumThreads(cOptions, (int32_t)options.numberOfThreads);
+ }
+ TFL_InterpreterOptionsSetErrorReporter(cOptions, TFLInterpreterErrorReporter, nullptr);
+
+ _interpreter = TFL_NewInterpreter(model, cOptions);
+ if (_interpreter == nullptr) {
+ _initializationError =
+ [TFLErrorUtil interpreterErrorWithCode:TFLInterpreterErrorCodeFailedToCreateInterpreter
+ description:@"Failed to create the interpreter."];
+ } else {
+ _inputTensorCount = (NSUInteger)TFL_InterpreterGetInputTensorCount(_interpreter);
+ _outputTensorCount = (NSUInteger)TFL_InterpreterGetOutputTensorCount(_interpreter);
+ if (_inputTensorCount <= 0 || _outputTensorCount <= 0) {
+ _initializationError =
+ [TFLErrorUtil interpreterErrorWithCode:TFLInterpreterErrorCodeFailedToCreateInterpreter
+ description:@"Failed to create the interpreter."];
+ }
+ }
+ TFL_DeleteInterpreterOptions(cOptions);
+ TFL_DeleteModel(model);
+ }
+
+ return self;
+}
+
+- (BOOL)invokeWithError:(NSError **)error {
+ if (self.initializationError != nil) {
+ [self saveInitializationErrorToDestination:error];
+ return NO;
+ }
+
+ if (TFL_InterpreterInvoke(self.interpreter) != kTfLiteOk) {
+ [TFLErrorUtil saveInterpreterErrorWithCode:TFLInterpreterErrorCodeFailedToInvoke
+ description:@"Failed to invoke the interpreter."
+ error:error];
+ return NO;
+ }
+
+ return YES;
+}
+
+- (nullable TFLTensor *)inputTensorAtIndex:(NSUInteger)index error:(NSError **)error {
+ if (self.initializationError != nil) {
+ [self saveInitializationErrorToDestination:error];
+ return nil;
+ }
+
+ if (![self isValidTensorIndex:index belowLimit:self.inputTensorCount error:error]) {
+ return nil;
+ }
+
+ return [self tensorOfType:TFLTensorTypeInput atIndex:index error:error];
+}
+
+- (nullable TFLTensor *)outputTensorAtIndex:(NSUInteger)index error:(NSError **)error {
+ if (self.initializationError != nil) {
+ [self saveInitializationErrorToDestination:error];
+ return nil;
+ }
+
+ if (![self isValidTensorIndex:index belowLimit:self.outputTensorCount error:error]) {
+ return nil;
+ }
+
+ return [self tensorOfType:TFLTensorTypeOutput atIndex:index error:error];
+}
+
+- (BOOL)resizeInputTensorAtIndex:(NSUInteger)index
+ toShape:(NSArray<NSNumber *> *)shape
+ error:(NSError **)error {
+ if (self.initializationError != nil) {
+ [self saveInitializationErrorToDestination:error];
+ return NO;
+ }
+
+ if (![self isValidTensorIndex:index belowLimit:self.inputTensorCount error:error]) {
+ return NO;
+ }
+
+ if (shape.count == 0) {
+ [TFLErrorUtil saveInterpreterErrorWithCode:TFLInterpreterErrorCodeInvalidShape
+ description:@"Invalid shape. Must not be empty."
+ error:error];
+ return NO;
+ }
+
+ int cDimensions[self.inputTensorCount];
+ for (int d = 0; d < shape.count; ++d) {
+ int dimension = shape[d].intValue;
+ if (dimension <= 0) {
+ NSString *errorDescription = @"Invalid shape. Dimensions must be positive integers.";
+ [TFLErrorUtil saveInterpreterErrorWithCode:TFLInterpreterErrorCodeInvalidShape
+ description:errorDescription
+ error:error];
+ return NO;
+ }
+ cDimensions[d] = dimension;
+ }
+
+ if (TFL_InterpreterResizeInputTensor(self.interpreter, (int32_t)index, cDimensions,
+ (int32_t)shape.count) != kTfLiteOk) {
+ NSString *errorDescription = [NSString
+ stringWithFormat:@"Failed to resize input tensor at index (%lu).", (unsigned long)index];
+ [TFLErrorUtil saveInterpreterErrorWithCode:TFLInterpreterErrorCodeFailedToResizeInputTensor
+ description:errorDescription
+ error:error];
+ return NO;
+ }
+
+ return YES;
+}
+
+- (BOOL)copyData:(NSData *)data toInputTensorAtIndex:(NSUInteger)index error:(NSError **)error {
+ if (self.initializationError != nil) {
+ [self saveInitializationErrorToDestination:error];
+ return NO;
+ }
+
+ if (![self isValidTensorIndex:index belowLimit:self.inputTensorCount error:error]) {
+ return NO;
+ }
+
+ TFL_Tensor *tensor = TFL_InterpreterGetInputTensor(self.interpreter, (int32_t)index);
+ if (tensor == nullptr) {
+ NSString *errorDescription = [NSString
+ stringWithFormat:@"Failed to get input tensor at index (%lu).", (unsigned long)index];
+ [TFLErrorUtil saveInterpreterErrorWithCode:TFLInterpreterErrorCodeFailedToCopyDataToInputTensor
+ description:errorDescription
+ error:error];
+ return NO;
+ }
+
+ NSUInteger byteSize = (NSUInteger)TFL_TensorByteSize(tensor);
+ if (data.length != byteSize) {
+ NSString *errorDescription = [NSString
+ stringWithFormat:@"Input tensor at index (%lu) expects data size (%lu), but got (%lu).",
+ (unsigned long)index, byteSize, (unsigned long)data.length];
+ [TFLErrorUtil saveInterpreterErrorWithCode:TFLInterpreterErrorCodeInvalidInputByteSize
+ description:errorDescription
+ error:error];
+ return NO;
+ }
+
+ if (TFL_TensorCopyFromBuffer(tensor, data.bytes, data.length) != kTfLiteOk) {
+ NSString *errorDescription =
+ [NSString stringWithFormat:@"Failed to copy data into input tensor at index (%lu).",
+ (unsigned long)index];
+ [TFLErrorUtil saveInterpreterErrorWithCode:TFLInterpreterErrorCodeFailedToCopyDataToInputTensor
+ description:errorDescription
+ error:error];
+ return NO;
+ }
+
+ return YES;
+}
+
+- (nullable NSData *)dataFromOutputTensorAtIndex:(NSUInteger)index error:(NSError **)error {
+ if (self.initializationError != nil) {
+ [self saveInitializationErrorToDestination:error];
+ return nil;
+ }
+
+ if (![self isValidTensorIndex:index belowLimit:self.outputTensorCount error:error]) {
+ return nil;
+ }
+
+ const TFL_Tensor *tensor = TFL_InterpreterGetOutputTensor(self.interpreter, (int32_t)index);
+ if (tensor == nullptr) {
+ NSString *errorDescription = [NSString
+ stringWithFormat:@"Failed to get output tensor at index (%lu).", (unsigned long)index];
+ [TFLErrorUtil
+ saveInterpreterErrorWithCode:TFLInterpreterErrorCodeFailedToGetDataFromOutputTensor
+ description:errorDescription
+ error:error];
+ return nil;
+ }
+
+ void *bytes = TFL_TensorData(tensor);
+ NSUInteger byteSize = (NSUInteger)TFL_TensorByteSize(tensor);
+ if (bytes == nullptr || byteSize == 0) {
+ NSString *errorDescription = [NSString
+ stringWithFormat:@"Failed to get output tensor data at index (%lu).", (unsigned long)index];
+ [TFLErrorUtil
+ saveInterpreterErrorWithCode:TFLInterpreterErrorCodeFailedToGetDataFromOutputTensor
+ description:errorDescription
+ error:error];
+ return nil;
+ }
+
+ return [NSData dataWithBytes:bytes length:byteSize];
+}
+
+- (BOOL)allocateTensorsWithError:(NSError **)error {
+ if (self.initializationError != nil) {
+ [self saveInitializationErrorToDestination:error];
+ return NO;
+ }
+
+ if (TFL_InterpreterAllocateTensors(self.interpreter) != kTfLiteOk) {
+ [TFLErrorUtil saveInterpreterErrorWithCode:TFLInterpreterErrorCodeFailedToAllocateTensors
+ description:@"Failed to allocate memory for tensors."
+ error:error];
+ return NO;
+ }
+ return YES;
+}
+
+#pragma mark - Private
+
+- (nullable TFLTensor *)tensorOfType:(TFLTensorType)type
+ atIndex:(NSUInteger)index
+ error:(NSError **)error {
+ const TFL_Tensor *tensor = nullptr;
+ NSString *tensorType;
+ switch (type) {
+ case TFLTensorTypeInput:
+ tensor = TFL_InterpreterGetInputTensor(self.interpreter, (int32_t)index);
+ tensorType = kTFLInputDirection;
+ break;
+ case TFLTensorTypeOutput:
+ tensor = TFL_InterpreterGetOutputTensor(self.interpreter, (int32_t)index);
+ tensorType = kTFLOutputDirection;
+ break;
+ }
+
+ if (tensor == nullptr) {
+ NSString *errorDescription =
+ [NSString stringWithFormat:@"Failed to get %@ tensor at index (%lu).", tensorType,
+ (unsigned long)index];
+ [TFLErrorUtil saveInterpreterErrorWithCode:TFLInterpreterErrorCodeFailedToGetTensor
+ description:errorDescription
+ error:error];
+ return nil;
+ }
+
+ const char *cName = TFL_TensorName(tensor);
+ if (cName == nullptr) {
+ NSString *errorDescription =
+ [NSString stringWithFormat:@"Failed to get name of %@ tensor at index (%lu).", tensorType,
+ (unsigned long)index];
+ [TFLErrorUtil saveInterpreterErrorWithCode:TFLInterpreterErrorCodeFailedToGetTensor
+ description:errorDescription
+ error:error];
+ return nil;
+ }
+ NSString *name = [NSString stringWithUTF8String:cName];
+
+ TFLTensorDataType dataType = [self tensorDataTypeFromCTensorType:TFL_TensorType(tensor)];
+
+ int32_t rank = TFL_TensorNumDims(tensor);
+ if (rank <= 0) {
+ NSString *errorDescription =
+ [NSString stringWithFormat:@"%@ tensor at index (%lu) has invalid rank (%d).", tensorType,
+ (unsigned long)index, rank];
+ [TFLErrorUtil saveInterpreterErrorWithCode:TFLInterpreterErrorCodeFailedToGetTensor
+ description:errorDescription
+ error:error];
+ return nil;
+ }
+ NSMutableArray *shape = [NSMutableArray arrayWithCapacity:rank];
+ for (int32_t d = 0; d < rank; d++) {
+ int32_t dimension = TFL_TensorDim(tensor, d);
+ if (dimension <= 0) {
+ NSString *errorDescription =
+ [NSString stringWithFormat:@"%@ tensor at index (%lu) has invalid %d-th dimension (%d).",
+ tensorType, (unsigned long)index, d, dimension];
+ [TFLErrorUtil saveInterpreterErrorWithCode:TFLInterpreterErrorCodeFailedToGetTensor
+ description:errorDescription
+ error:error];
+ return nil;
+ }
+ shape[d] = @((NSUInteger)dimension);
+ }
+
+ // TODO: Set quantization parameters when C API supports it.
+ return [[TFLTensor alloc] initWithName:name
+ dataType:dataType
+ shape:shape
+ byteSize:(NSUInteger)TFL_TensorByteSize(tensor)
+ quantizationParameters:nil];
+}
+
+- (TFLTensorDataType)tensorDataTypeFromCTensorType:(TFL_Type)cTensorType {
+ switch (cTensorType) {
+ case kTfLiteFloat32:
+ return TFLTensorDataTypeFloat32;
+ case kTfLiteInt32:
+ return TFLTensorDataTypeInt32;
+ case kTfLiteUInt8:
+ return TFLTensorDataTypeUInt8;
+ case kTfLiteInt64:
+ return TFLTensorDataTypeInt64;
+ case kTfLiteBool:
+ return TFLTensorDataTypeBool;
+ case kTfLiteInt16:
+ return TFLTensorDataTypeInt16;
+ case kTfLiteNoType:
+ case kTfLiteString:
+ case kTfLiteComplex64:
+ // kTfLiteString and kTfLiteComplex64 are not supported in TensorFlow Lite Objc API.
+ return TFLTensorDataTypeNoType;
+ }
+}
+
+- (void)saveInitializationErrorToDestination:(NSError **)destination {
+ if (destination != NULL) {
+ *destination = self.initializationError;
+ }
+}
+
+- (BOOL)isValidTensorIndex:(NSUInteger)index
+ belowLimit:(NSUInteger)totalTensorCount
+ error:(NSError **)error {
+ if (index >= totalTensorCount) {
+ NSString *errorDescription =
+ [NSString stringWithFormat:@"Invalid tensor index (%lu) exceeds max (%lu).",
+ (unsigned long)index, (unsigned long)(totalTensorCount - 1)];
+ [TFLErrorUtil saveInterpreterErrorWithCode:TFLInterpreterErrorCodeInvalidTensorIndex
+ description:errorDescription
+ error:error];
+ return NO;
+ }
+
+ return YES;
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/tensorflow/contrib/lite/experimental/objc/sources/TFLInterpreterOptions.m b/tensorflow/contrib/lite/experimental/objc/sources/TFLInterpreterOptions.m
new file mode 100644
index 0000000000..1776688288
--- /dev/null
+++ b/tensorflow/contrib/lite/experimental/objc/sources/TFLInterpreterOptions.m
@@ -0,0 +1,30 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import "third_party/tensorflow/contrib/lite/experimental/objc/apis/TFLInterpreterOptions.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@implementation TFLInterpreterOptions
+
+#pragma mark - Public
+
+- (instancetype)init {
+ self = [super init];
+ return self;
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/tensorflow/contrib/lite/experimental/objc/sources/TFLQuantizationParameters.m b/tensorflow/contrib/lite/experimental/objc/sources/TFLQuantizationParameters.m
new file mode 100644
index 0000000000..190f0479ce
--- /dev/null
+++ b/tensorflow/contrib/lite/experimental/objc/sources/TFLQuantizationParameters.m
@@ -0,0 +1,23 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import "third_party/tensorflow/contrib/lite/experimental/objc/apis/TFLQuantizationParameters.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@implementation TFLQuantizationParameters
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/tensorflow/contrib/lite/experimental/objc/sources/TFLTensor+Internal.h b/tensorflow/contrib/lite/experimental/objc/sources/TFLTensor+Internal.h
new file mode 100644
index 0000000000..f2f13e5e5f
--- /dev/null
+++ b/tensorflow/contrib/lite/experimental/objc/sources/TFLTensor+Internal.h
@@ -0,0 +1,42 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import "third_party/tensorflow/contrib/lite/experimental/objc/apis/TFLTensor.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface TFLTensor (Internal)
+
+/**
+ * Initializes a `TFLTensor` with the given name, data type, shape, and quantization parameters.
+ *
+ * @param name Name of the tensor.
+ * @param dataType Data type of the tensor.
+ * @param shape Shape of the tensor.
+ * @param byteSize Size of the tensor data in number of bytes.
+ * @param quantizationParameters Quantization parameters of the tensor. `nil` if the tensor does not
+ * use quantization.
+ *
+ * @return A new instance of `TFLTensor` with the given name, data type, shape, and quantization
+ * parameters.
+ */
+- (instancetype)initWithName:(NSString *)name
+ dataType:(TFLTensorDataType)dataType
+ shape:(NSArray<NSNumber *> *)shape
+ byteSize:(NSUInteger)byteSize
+ quantizationParameters:(nullable TFLQuantizationParameters *)quantizationParameters;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/tensorflow/contrib/lite/experimental/objc/sources/TFLTensor.m b/tensorflow/contrib/lite/experimental/objc/sources/TFLTensor.m
new file mode 100644
index 0000000000..adb1c5ad2c
--- /dev/null
+++ b/tensorflow/contrib/lite/experimental/objc/sources/TFLTensor.m
@@ -0,0 +1,54 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import "third_party/tensorflow/contrib/lite/experimental/objc/apis/TFLTensor.h"
+
+#import "TFLTensor+Internal.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface TFLTensor ()
+
+// Redefines readonly properties.
+@property(nonatomic, copy) NSString *name;
+@property(nonatomic) TFLTensorDataType dataType;
+@property(nonatomic, copy) NSArray<NSNumber *> *shape;
+@property(nonatomic) NSUInteger byteSize;
+@property(nonatomic, nullable) TFLQuantizationParameters *quantizationParameters;
+
+@end
+
+@implementation TFLTensor
+
+#pragma mark - TFLTensor (Internal)
+
+- (instancetype)initWithName:(NSString *)name
+ dataType:(TFLTensorDataType)dataType
+ shape:(NSArray<NSNumber *> *)shape
+ byteSize:(NSUInteger)byteSize
+ quantizationParameters:(nullable TFLQuantizationParameters *)quantizationParameters {
+ self = [super init];
+ if (self != nil) {
+ _name = [name copy];
+ _dataType = dataType;
+ _shape = [shape copy];
+ _byteSize = byteSize;
+ _quantizationParameters = quantizationParameters;
+ }
+ return self;
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/tensorflow/contrib/lite/experimental/objc/tests/TFLInterpreterOptionsTests.m b/tensorflow/contrib/lite/experimental/objc/tests/TFLInterpreterOptionsTests.m
new file mode 100644
index 0000000000..17c495fa18
--- /dev/null
+++ b/tensorflow/contrib/lite/experimental/objc/tests/TFLInterpreterOptionsTests.m
@@ -0,0 +1,49 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import "third_party/tensorflow/contrib/lite/experimental/objc/apis/TFLInterpreterOptions.h"
+
+#import <XCTest/XCTest.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * Unit tests for TFLInterpreterOptions.
+ */
+@interface TFLInterpreterOptionsTests : XCTestCase
+@end
+
+@implementation TFLInterpreterOptionsTests
+
+#pragma mark - Tests
+
+- (void)testInit {
+ TFLInterpreterOptions *options = [[TFLInterpreterOptions alloc] init];
+ XCTAssertNotNil(options);
+ XCTAssertEqual(options.numberOfThreads, 0);
+}
+
+- (void)testSetNumberOfThread {
+ TFLInterpreterOptions *options = [[TFLInterpreterOptions alloc] init];
+ options.numberOfThreads = 2;
+ XCTAssertEqual(options.numberOfThreads, 2);
+ options.numberOfThreads = 0;
+ XCTAssertEqual(options.numberOfThreads, 0);
+ options.numberOfThreads = 3;
+ XCTAssertEqual(options.numberOfThreads, 3);
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/tensorflow/contrib/lite/experimental/objc/tests/TFLInterpreterTests.m b/tensorflow/contrib/lite/experimental/objc/tests/TFLInterpreterTests.m
new file mode 100644
index 0000000000..9e6319a732
--- /dev/null
+++ b/tensorflow/contrib/lite/experimental/objc/tests/TFLInterpreterTests.m
@@ -0,0 +1,266 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import "third_party/tensorflow/contrib/lite/experimental/objc/apis/TFLInterpreter.h"
+
+#import <XCTest/XCTest.h>
+
+#import "third_party/tensorflow/contrib/lite/experimental/objc/apis/TFLInterpreterOptions.h"
+#import "third_party/tensorflow/contrib/lite/experimental/objc/apis/TFLTensor.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/** Model resource name. */
+static NSString *const kAddModelResourceName = @"add";
+
+/** Model resource type. */
+static NSString *const kAddModelResourceType = @"bin";
+
+/** Rank of the input and output tensor in the Add model. */
+static const NSUInteger kAddModelTensorRank = 1U;
+
+/** Size of the first (and only) dimension of the input and output tensor in the Add model. */
+static const NSUInteger kAddModelTensorFirstDimensionSize = 2U;
+
+/** Invalid input tensor index. */
+static const NSUInteger kInvalidInputTensorIndex = 1U;
+
+/** Invalid output tensor index. */
+static const NSUInteger kInvalidOutputTensorIndex = 1U;
+
+/** Accurary used in comparing floating numbers. */
+static const float kTestAccuracy = 1E-5F;
+
+/**
+ * Unit tests for TFLInterpreter.
+ */
+@interface TFLInterpreterTests : XCTestCase
+
+/** Absolute path of the Add model resource. */
+@property(nonatomic, nullable) NSString *modelPath;
+
+/** Default interpreter using the Add model. */
+@property(nonatomic, nullable) TFLInterpreter *interpreter;
+
+@end
+
+@implementation TFLInterpreterTests
+
+#pragma mark - XCTestCase
+
+- (void)setUp {
+ [super setUp];
+
+ NSBundle *bundle = [NSBundle bundleForClass:[self class]];
+ self.modelPath = [bundle pathForResource:kAddModelResourceName ofType:kAddModelResourceType];
+ self.interpreter = [[TFLInterpreter alloc] initWithModelPath:self.modelPath];
+ XCTAssertNotNil(self.interpreter);
+ XCTAssertTrue([self.interpreter allocateTensorsWithError:nil]);
+}
+
+- (void)tearDown {
+ self.modelPath = nil;
+ self.interpreter = nil;
+
+ [super tearDown];
+}
+
+#pragma mark - Tests
+
+- (void)testSuccessfulFullRun {
+ // Shape for both input and output tensor.
+ NSMutableArray *shape = [NSMutableArray arrayWithCapacity:kAddModelTensorRank];
+ shape[0] = [NSNumber numberWithUnsignedInteger:kAddModelTensorFirstDimensionSize];
+
+ // Creates the interpreter options.
+ TFLInterpreterOptions *options = [[TFLInterpreterOptions alloc] init];
+ XCTAssertNotNil(options);
+ options.numberOfThreads = 2;
+
+ // Creates the interpreter.
+ TFLInterpreter *customInterpreter = [[TFLInterpreter alloc] initWithModelPath:self.modelPath
+ options:options];
+ XCTAssertNotNil(customInterpreter);
+
+ // Allocates memory for tensors.
+ NSError *error;
+ XCTAssertTrue([customInterpreter allocateTensorsWithError:&error]);
+ XCTAssertNil(error);
+
+ // Verifies input and output tensor counts.
+ XCTAssertEqual(customInterpreter.inputTensorCount, 1);
+ XCTAssertEqual(customInterpreter.outputTensorCount, 1);
+
+ // Resizes the intput tensor.
+ XCTAssertTrue([customInterpreter resizeInputTensorAtIndex:0 toShape:shape error:&error]);
+ XCTAssertNil(error);
+
+ // Re-allocates memory for tensors.
+ XCTAssertTrue([customInterpreter allocateTensorsWithError:&error]);
+ XCTAssertNil(error);
+
+ // Verifies the input tensor.
+ TFLTensor *inputTensor = [customInterpreter inputTensorAtIndex:0 error:&error];
+ XCTAssertNotNil(inputTensor);
+ XCTAssertNil(error);
+ XCTAssertTrue([inputTensor.name isEqualToString:@"input"]);
+ XCTAssertEqual(inputTensor.dataType, TFLTensorDataTypeFloat32);
+ XCTAssertTrue([shape isEqualToArray:inputTensor.shape]);
+ XCTAssertEqual(inputTensor.byteSize, sizeof(float) * kAddModelTensorFirstDimensionSize);
+
+ // Copies the input data.
+ NSMutableData *inputData = [NSMutableData dataWithCapacity:0];
+ float one = 1.f;
+ float three = 3.f;
+ [inputData appendBytes:&one length:sizeof(float)];
+ [inputData appendBytes:&three length:sizeof(float)];
+ XCTAssertTrue([customInterpreter copyData:inputData toInputTensorAtIndex:0 error:&error]);
+ XCTAssertNil(error);
+
+ // Invokes the interpreter.
+ XCTAssertTrue([customInterpreter invokeWithError:&error]);
+ XCTAssertNil(error);
+
+ // Verifies the output tensor.
+ TFLTensor *outputTensor = [customInterpreter outputTensorAtIndex:0 error:&error];
+ XCTAssertNotNil(outputTensor);
+ XCTAssertNil(error);
+ XCTAssertTrue([outputTensor.name isEqualToString:@"output"]);
+ XCTAssertEqual(outputTensor.dataType, TFLTensorDataTypeFloat32);
+ XCTAssertTrue([shape isEqualToArray:outputTensor.shape]);
+ XCTAssertEqual(outputTensor.byteSize, sizeof(float) * kAddModelTensorFirstDimensionSize);
+
+ // Tries to query an invalid output tensor index.
+ TFLTensor *invalidOutputTensor = [customInterpreter outputTensorAtIndex:kInvalidOutputTensorIndex
+ error:&error];
+ XCTAssertNil(invalidOutputTensor);
+ XCTAssertEqual(error.code, TFLInterpreterErrorCodeInvalidTensorIndex);
+
+ // Gets the output tensor data.
+ error = nil;
+ NSData *outputData = [customInterpreter dataFromOutputTensorAtIndex:0 error:&error];
+ XCTAssertNotNil(outputData);
+ XCTAssertNil(error);
+ float output[kAddModelTensorFirstDimensionSize];
+ [outputData getBytes:output length:(sizeof(float) * kAddModelTensorFirstDimensionSize)];
+ XCTAssertEqualWithAccuracy(output[0], 3.f, kTestAccuracy);
+ XCTAssertEqualWithAccuracy(output[1], 9.f, kTestAccuracy);
+}
+
+- (void)testInitWithModelPath_invalidPath {
+ // Shape for both input and output tensor.
+ NSMutableArray *shape = [NSMutableArray arrayWithCapacity:kAddModelTensorRank];
+ shape[0] = [NSNumber numberWithUnsignedInteger:kAddModelTensorFirstDimensionSize];
+
+ // Creates the interpreter.
+ TFLInterpreter *brokenInterpreter = [[TFLInterpreter alloc] initWithModelPath:@"InvalidPath"];
+ XCTAssertNotNil(brokenInterpreter);
+ XCTAssertEqual(brokenInterpreter.inputTensorCount, 0);
+ XCTAssertEqual(brokenInterpreter.outputTensorCount, 0);
+
+ // Allocates memory for tensors.
+ NSError *error;
+ XCTAssertFalse([brokenInterpreter allocateTensorsWithError:&error]);
+ XCTAssertEqual(error.code, TFLInterpreterErrorCodeFailedToLoadModel);
+
+ // Resizes the intput tensor.
+ XCTAssertFalse([brokenInterpreter resizeInputTensorAtIndex:0 toShape:shape error:&error]);
+ XCTAssertEqual(error.code, TFLInterpreterErrorCodeFailedToLoadModel);
+
+ // Verifies the input tensor.
+ TFLTensor *inputTensor = [brokenInterpreter inputTensorAtIndex:0 error:&error];
+ XCTAssertNil(inputTensor);
+ XCTAssertEqual(error.code, TFLInterpreterErrorCodeFailedToLoadModel);
+
+ // Copies the input data.
+ NSMutableData *inputData = [NSMutableData dataWithCapacity:0];
+ float one = 1.f;
+ float three = 3.f;
+ [inputData appendBytes:&one length:sizeof(float)];
+ [inputData appendBytes:&three length:sizeof(float)];
+ XCTAssertFalse([brokenInterpreter copyData:inputData toInputTensorAtIndex:0 error:&error]);
+ XCTAssertEqual(error.code, TFLInterpreterErrorCodeFailedToLoadModel);
+
+ // Invokes the interpreter.
+ XCTAssertFalse([brokenInterpreter invokeWithError:&error]);
+ XCTAssertEqual(error.code, TFLInterpreterErrorCodeFailedToLoadModel);
+
+ // Verifies the output tensor.
+ TFLTensor *outputTensor = [brokenInterpreter outputTensorAtIndex:0 error:&error];
+ XCTAssertNil(outputTensor);
+ XCTAssertEqual(error.code, TFLInterpreterErrorCodeFailedToLoadModel);
+
+ // Gets the output tensor data.
+ NSData *outputData = [brokenInterpreter dataFromOutputTensorAtIndex:0 error:&error];
+ XCTAssertNil(outputData);
+ XCTAssertEqual(error.code, TFLInterpreterErrorCodeFailedToLoadModel);
+}
+
+- (void)testInvoke_beforeAllocation {
+ TFLInterpreter *interpreterWithoutAllocation =
+ [[TFLInterpreter alloc] initWithModelPath:self.modelPath];
+ XCTAssertNotNil(interpreterWithoutAllocation);
+
+ NSError *error;
+ XCTAssertFalse([interpreterWithoutAllocation invokeWithError:&error]);
+ XCTAssertEqual(error.code, TFLInterpreterErrorCodeFailedToInvoke);
+}
+
+- (void)testInputTensorAtIndex_invalidIndex {
+ NSError *error;
+ TFLTensor *inputTensor = [self.interpreter inputTensorAtIndex:kInvalidInputTensorIndex
+ error:&error];
+ XCTAssertNil(inputTensor);
+ XCTAssertEqual(error.code, TFLInterpreterErrorCodeInvalidTensorIndex);
+}
+
+- (void)testResizeInputTensorAtIndex_invalidIndex {
+ NSMutableArray *shape = [NSMutableArray arrayWithCapacity:kAddModelTensorRank];
+ shape[0] = [NSNumber numberWithUnsignedInteger:kAddModelTensorFirstDimensionSize];
+ NSError *error;
+ XCTAssertFalse([self.interpreter resizeInputTensorAtIndex:kInvalidInputTensorIndex
+ toShape:shape
+ error:&error]);
+ XCTAssertEqual(error.code, TFLInterpreterErrorCodeInvalidTensorIndex);
+}
+
+- (void)testResizeInputTensorAtIndex_emptyShape {
+ NSMutableArray *emptyShape = [NSMutableArray arrayWithCapacity:0];
+ NSError *error;
+ XCTAssertFalse([self.interpreter resizeInputTensorAtIndex:0 toShape:emptyShape error:&error]);
+ XCTAssertEqual(error.code, TFLInterpreterErrorCodeInvalidShape);
+}
+
+- (void)testResizeInputTensorAtIndex_zeroDimensionSize {
+ NSMutableArray *shape = [NSMutableArray arrayWithCapacity:kAddModelTensorRank];
+ shape[0] = [NSNumber numberWithUnsignedInteger:0];
+ NSError *error;
+ XCTAssertFalse([self.interpreter resizeInputTensorAtIndex:0 toShape:shape error:&error]);
+ XCTAssertEqual(error.code, TFLInterpreterErrorCodeInvalidShape);
+}
+
+- (void)testCopyDataToInputTensorAtIndex_invalidInputDataByteSize {
+ NSMutableData *inputData = [NSMutableData dataWithCapacity:0];
+ float one = 1.f;
+ float three = 3.f;
+ [inputData appendBytes:&one length:sizeof(float)];
+ [inputData appendBytes:&three length:(sizeof(float) - 1)];
+ NSError *error;
+ XCTAssertFalse([self.interpreter copyData:inputData toInputTensorAtIndex:0 error:&error]);
+ XCTAssertEqual(error.code, TFLInterpreterErrorCodeInvalidInputByteSize);
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/tensorflow/tools/pip_package/pip_smoke_test.py b/tensorflow/tools/pip_package/pip_smoke_test.py
index c6ef82ccdc..31b68c8f00 100644
--- a/tensorflow/tools/pip_package/pip_smoke_test.py
+++ b/tensorflow/tools/pip_package/pip_smoke_test.py
@@ -105,6 +105,7 @@ BLACKLIST = [
"//tensorflow/contrib/timeseries/python/timeseries:test_utils",
"//tensorflow/contrib/timeseries/python/timeseries/state_space_models:test_utils", # pylint:disable=line-too-long
"//tensorflow/contrib/image:sparse_image_warp_test_data",
+ "//tools/build_defs/apple:ios.bzl",
]