diff --git a/Handwriting.xcodeproj/project.pbxproj b/Handwriting.xcodeproj/project.pbxproj index 4fe4c64..37a9ad1 100644 --- a/Handwriting.xcodeproj/project.pbxproj +++ b/Handwriting.xcodeproj/project.pbxproj @@ -151,19 +151,18 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0830; - LastUpgradeCheck = 0830; + LastUpgradeCheck = 1130; ORGANIZATIONNAME = "Swift AI"; TargetAttributes = { F082483E1EB82EC200EC951B = { CreatedOnToolsVersion = 8.3.2; - DevelopmentTeam = 5ZHKK24J8V; ProvisioningStyle = Automatic; }; }; }; buildConfigurationList = F082483A1EB82EC200EC951B /* Build configuration list for PBXProject "Handwriting" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, @@ -232,21 +231,30 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -282,21 +290,30 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -325,13 +342,13 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = 5ZHKK24J8V; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = Handwriting/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "swift-ai.Handwriting"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -339,13 +356,13 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = 5ZHKK24J8V; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = Handwriting/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "swift-ai.Handwriting"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 5.0; }; name = Release; }; diff --git a/Handwriting/Activation.swift b/Handwriting/Activation.swift index e76f024..3e4c88d 100644 --- a/Handwriting/Activation.swift +++ b/Handwriting/Activation.swift @@ -12,7 +12,7 @@ import Accelerate public extension NeuralNet { - public enum ActivationFunction { + enum ActivationFunction { // MARK: Hidden Activation Functions // These functions may be used for all hidden layers of a `NeuralNet`. diff --git a/Handwriting/AppDelegate.swift b/Handwriting/AppDelegate.swift index 689e878..0d3451f 100644 --- a/Handwriting/AppDelegate.swift +++ b/Handwriting/AppDelegate.swift @@ -14,7 +14,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { let mainViewController = MainViewController() self.window = UIWindow(frame: UIScreen.main.bounds) diff --git a/Handwriting/Dataset.swift b/Handwriting/Dataset.swift index b4a94aa..4ce531c 100644 --- a/Handwriting/Dataset.swift +++ b/Handwriting/Dataset.swift @@ -12,7 +12,7 @@ import Foundation public extension NeuralNet { /// A complete dataset for training a neural network, including training sets and validation sets. - public struct Dataset { + struct Dataset { /// Errors that may be thrown by `Dataset`. public enum Error: Swift.Error { diff --git a/Handwriting/Error.swift b/Handwriting/Error.swift index 7c8e8c3..c16ad74 100644 --- a/Handwriting/Error.swift +++ b/Handwriting/Error.swift @@ -12,7 +12,7 @@ import Foundation public extension NeuralNet { /// Functions for calculating error on a validation set. - public enum ErrorFunction { + enum ErrorFunction { /// Mean squared error function. /// 1 / n * ∑( (a[i] - t[i]) * (a[i] - t[i]) ) case meanSquared @@ -73,7 +73,7 @@ public extension NeuralNet { let realSet = real[start..<(start + cols)] let labelSet = target[start..<(start + cols)] // Compare max values of outputs and labels - if realSet.index(of: realSet.max()!) != labelSet.index(of: labelSet.max()!) { + if realSet.firstIndex(of: realSet.max()!) != labelSet.firstIndex(of: labelSet.max()!) { // Incorrect answer; increment counter incorrect += 1 } diff --git a/Handwriting/MainView.swift b/Handwriting/MainView.swift index 85d3501..e6a8afd 100644 --- a/Handwriting/MainView.swift +++ b/Handwriting/MainView.swift @@ -74,13 +74,15 @@ class MainView: UIView { outputTitleLabel.textAlignment = .center // Output - outputLabel.font = UIFont.systemFont(ofSize: 100, weight: UIFontWeightLight) + outputLabel.font = UIFont.systemFont(ofSize: 100, weight: UIFont.Weight.light) + outputLabel.textColor = UIColor.black outputLabel.textAlignment = .center // Confidence confidenceLabel.font = UIFont.systemFont(ofSize: 15) confidenceLabel.textAlignment = .center - + confidenceLabel.textColor = UIColor.black + } /// Add subviews and set constraints. diff --git a/Handwriting/MainViewController.swift b/Handwriting/MainViewController.swift index 3a21574..3ef14ea 100644 --- a/Handwriting/MainViewController.swift +++ b/Handwriting/MainViewController.swift @@ -352,7 +352,7 @@ extension MainViewController { /// Extracts the output integer and confidence from the given neural network output. private func label(from output: [Float]) -> (label: Int, confidence: Float)? { guard let max = output.max() else { return nil } - return (output.index(of: max)!, max) + return (output.firstIndex(of: max)!, max) } /// Displays the given label and confidence in the output view. diff --git a/Handwriting/NeuralNet.swift b/Handwriting/NeuralNet.swift index 8933818..6c9456e 100644 --- a/Handwriting/NeuralNet.swift +++ b/Handwriting/NeuralNet.swift @@ -107,7 +107,7 @@ public extension NeuralNet { /// This change may safely be performed at any time. /// /// - Parameter weights: A 2D array of weights corresponding to each layer in the network. - public func setWeights(_ weights: [[Float]]) throws { + func setWeights(_ weights: [[Float]]) throws { // TODO: ensure valid number of weights // Reset all weights in the network @@ -115,7 +115,7 @@ public extension NeuralNet { } /// Returns an array of the network's current weights for each layer. - public func allWeights() -> [[Float]] { + func allWeights() -> [[Float]] { return cache.layerWeights } @@ -168,7 +168,7 @@ public extension NeuralNet { /// - Returns: The full batch of outputs corresponding to the provided inputs. /// - Throws: An error if the number of batches or inputs per set are incorrect. @discardableResult - public func infer(_ inputs: [[Float]]) throws -> [[Float]] { + func infer(_ inputs: [[Float]]) throws -> [[Float]] { // Make sure the correct number of batches was provided guard inputs.count == batchSize else { throw Error.inference("Incorrect number of input sets provided: \(inputs.count). Expected: \(batchSize). The number of input sets must exactly match the network's batch size.") @@ -206,7 +206,7 @@ public extension NeuralNet { /// - Throws: An error if an incorrect number of inputs is provided. /// - IMPORTANT: The number of inputs provided must exactly match the network's number of inputs (defined in its `Structure`). @discardableResult - public func infer(_ inputs: [Float]) throws -> [Float] { + func infer(_ inputs: [Float]) throws -> [Float] { // Ensure that the correct number of inputs is given guard inputs.count == layerNodeCounts[0] * batchSize else { throw Error.inference("Incorrect number of inputs provided: \(inputs.count). Expected: \(layerNodeCounts[0] * batchSize). The number of total inputs must exactly match the network's input size times its batch size.") @@ -270,7 +270,7 @@ public extension NeuralNet { /// - Parameter labels: An array of label sets corresponding to the most recent minibatch applied for inference. /// - IMPORTANT: The labels must be given in the same order as the corresponding inputs that were provided during inference. /// - Throws: An error if a data inconsistency is detected. - public func backpropagate(_ labels: [[Float]]) throws { + func backpropagate(_ labels: [[Float]]) throws { // Ensure the correct number of sets is given guard labels.count == batchSize else { throw Error.train("Incorrect number of label sets provided: \(labels.count). Expected: \(batchSize). The number of sets in a minibatch must exactly match the network's batch size.") @@ -297,7 +297,7 @@ public extension NeuralNet { /// - Parameter labels: A single set of labels corresponding to the most recent inference cycle, /// or a full minibatch of labels serialized into a single array. /// - Throws: An error if a data inconsistency is detected. - public func backpropagate(_ labels: [Float]) throws { + func backpropagate(_ labels: [Float]) throws { // Ensure that the correct number of labels was given guard labels.count == layerNodeCounts[numLayers - 1] * batchSize else { throw Error.train("Incorrect number of labels provided: \(labels.count). Expected: \(layerNodeCounts[numLayers - 1] * batchSize). The total number of labels must exactly match the network's output size times its batch size.") @@ -477,9 +477,9 @@ public extension NeuralNet { /// or perform any other logic desired. /// - Returns: The total number of training epochs performed, and the final validation error. /// - Throws: An error if invalid data is provided. Checks are performed in advance to avoid problems during the training cycle. - public func train(_ data: Dataset, maxEpochs: Int, - errorThreshold: Float, errorFunction: ErrorFunction, - epochCallback: ((_ epoch: Int, _ error: Float) -> Bool)?) throws -> (epochs: Int, error: Float) { + func train(_ data: Dataset, maxEpochs: Int, + errorThreshold: Float, errorFunction: ErrorFunction, + epochCallback: ((_ epoch: Int, _ error: Float) -> Bool)?) throws -> (epochs: Int, error: Float) { // Ensure valid error threshold guard errorThreshold > 0 else { throw Error.train("Training error threshold must be greater than zero.") diff --git a/Handwriting/Storage.swift b/Handwriting/Storage.swift index 53ec273..6a2e59d 100644 --- a/Handwriting/Storage.swift +++ b/Handwriting/Storage.swift @@ -32,7 +32,7 @@ public extension NeuralNet { /// Attempts to initialize a `NeuralNet` from a file stored at the given URL. - public convenience init(url: URL) throws { + convenience init(url: URL) throws { // Read data let data = try Data(contentsOf: url) // Extract top-level object from data @@ -96,7 +96,7 @@ public extension NeuralNet { /// Persists the `NeuralNet` to a file at the given URL. - public func save(to url: URL) throws { + func save(to url: URL) throws { // Create top-level JSON object let json: [String : Any] = [ NeuralNet.layerNodeCountsKey : layerNodeCounts, diff --git a/Handwriting/Structure.swift b/Handwriting/Structure.swift index 3ce3ca6..8e2d86c 100644 --- a/Handwriting/Structure.swift +++ b/Handwriting/Structure.swift @@ -10,7 +10,7 @@ public extension NeuralNet { /// A container for the basic structure of a `NeuralNet`. - public struct Structure { + struct Structure { /// Possible `Structure` errors. public enum Error: Swift.Error {