Added Metal delegate and Core as subspecs of the source pod. * Removed manually written module map. The module map and umbrella header `TFLTensorFlowLite-umbrella.h` will be automatically generated with all the delegate headers. `TFLTensorFlowLite.h` is left for legacy purpose. * Modified macro to make tests work with bazel and Cocoapods. Changed some umbrella header imports to module import, `@import TFLTensorFlowLite`. PiperOrigin-RevId: 333012903 Change-Id: Ib743d7a3250b884fc406505f457ed55cea1ad20d
122 lines
4.1 KiB
Objective-C
122 lines
4.1 KiB
Objective-C
// Copyright 2020 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.
|
|
|
|
#ifdef COCOAPODS
|
|
@import TFLTensorFlowLite;
|
|
#else
|
|
#import "tensorflow/lite/experimental/objc/apis/TFLMetalDelegate.h"
|
|
#import "tensorflow/lite/experimental/objc/apis/TFLTensorFlowLite.h"
|
|
#endif
|
|
|
|
#import <Metal/MTLDevice.h>
|
|
#import <Metal/Metal.h>
|
|
#import <XCTest/XCTest.h>
|
|
|
|
NS_ASSUME_NONNULL_BEGIN
|
|
|
|
/**
|
|
* Float model resource name.
|
|
* The model has four inputs (a, b, c, d) and two outputs (x, y)
|
|
* x = a + (b + c)
|
|
* y = (b + c) + d
|
|
*/
|
|
static NSString* const kAddFloatModelResourceName = @"multi_add";
|
|
|
|
/** Model resource type. */
|
|
static NSString* const kAddModelResourceType = @"bin";
|
|
|
|
/** Size of input and output tensors */
|
|
static const int kTensorSize = 8 * 8 * 3;
|
|
|
|
/** Size of channel dimension of input and output tensors */
|
|
static const int kTensorChannels = 3;
|
|
|
|
/** Number of input tensors */
|
|
static const int kNumInputs = 4;
|
|
|
|
/** Number of output tensors */
|
|
static const int kNumOutputs = 2;
|
|
|
|
/** Accuracy used in comparing floating numbers. */
|
|
static const float kTestAccuracy = 1E-5F;
|
|
|
|
@interface TFLMetalDelegateTests : XCTestCase
|
|
@end
|
|
|
|
@implementation TFLMetalDelegateTests
|
|
|
|
- (void)testMetalDelegate {
|
|
NSBundle* bundle = [NSBundle bundleForClass:[self class]];
|
|
NSString* floatModelPath = [bundle pathForResource:kAddFloatModelResourceName
|
|
ofType:kAddModelResourceType];
|
|
|
|
TFLInterpreterOptions* options = [[TFLInterpreterOptions alloc] init];
|
|
TFLMetalDelegate* metalDelegate = [[TFLMetalDelegate alloc] init];
|
|
XCTAssertNotNil(metalDelegate);
|
|
|
|
id<MTLDevice> mtlDevice = MTLCreateSystemDefaultDevice();
|
|
if (mtlDevice == nil) return; // Stop testing if there's no GPU support
|
|
|
|
NSError* error;
|
|
TFLInterpreter* interpreter = [[TFLInterpreter alloc] initWithModelPath:floatModelPath
|
|
options:options
|
|
delegates:@[ metalDelegate ]
|
|
error:&error];
|
|
XCTAssertNil(error);
|
|
XCTAssertNotNil(interpreter);
|
|
XCTAssertTrue([interpreter allocateTensorsWithError:&error]);
|
|
XCTAssertNil(error);
|
|
|
|
// Copies the input data. For each input, input[i, j, k] == k
|
|
NSMutableData* inputData = [NSMutableData dataWithLength:sizeof(float) * kTensorSize];
|
|
for (int i = 0; i < kTensorSize / kTensorChannels; ++i) {
|
|
float* data = (float*)inputData.mutableBytes;
|
|
for (int j = 0; j < kTensorChannels; ++j) {
|
|
data[i * kTensorChannels + j] = j;
|
|
}
|
|
}
|
|
|
|
for (int input_idx = 0; input_idx < kNumInputs; ++input_idx) {
|
|
TFLTensor* inputTensor = [interpreter inputTensorAtIndex:input_idx error:&error];
|
|
XCTAssertNotNil(inputTensor);
|
|
XCTAssertTrue([inputTensor copyData:inputData error:&error]);
|
|
XCTAssertNil(error);
|
|
}
|
|
|
|
// Invokes the interpreter.
|
|
XCTAssertTrue([interpreter invokeWithError:&error]);
|
|
XCTAssertNil(error);
|
|
|
|
// Gets the output tensor data. For each output, output[i, j, k] == k * 3
|
|
for (int output_idx = 0; output_idx < kNumOutputs; ++output_idx) {
|
|
TFLTensor* outputTensor = [interpreter outputTensorAtIndex:output_idx error:&error];
|
|
NSData* outputData = [outputTensor dataWithError:&error];
|
|
XCTAssertNotNil(outputData);
|
|
XCTAssertNil(error);
|
|
|
|
float output[kTensorSize];
|
|
|
|
[outputData getBytes:output length:(sizeof(float) * kTensorSize)];
|
|
for (int i = 0; i < kTensorSize / kTensorChannels; ++i) {
|
|
for (int j = 0; j < kTensorChannels; ++j) {
|
|
XCTAssertEqualWithAccuracy(j * 3, output[i * kTensorChannels + j], kTestAccuracy);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@end
|
|
|
|
NS_ASSUME_NONNULL_END
|