├── .build ├── build.db ├── debug │ └── MultilinearMath.build │ │ ├── master.swiftdeps │ │ ├── main.swiftdeps │ │ └── output-file-map.json └── debug.yaml ├── Resources ├── BackpropGraph.jpg ├── MNIST_compression_pca.jpg ├── Slicing3dFlat_table.jpg ├── MNIST_compression_mpca.jpg ├── MNIST_compression_umpca.jpg ├── MNIST_compression_original.jpg ├── MultilinearDataUnfolding.jpg ├── NestedVsMultidimensional.jpg ├── SlicingTwoModes3dFlat_table.jpg └── ExampleTextFile.rtf ├── Package.swift ├── Playgrounds ├── FFTPlayground.playground │ ├── contents.xcplayground │ └── Contents.swift ├── TestPlayground.playground │ ├── contents.xcplayground │ └── Contents.swift ├── MyPlayground.playground │ ├── contents.xcplayground │ └── Contents.swift ├── ComplexWavelets.playground │ ├── contents.xcplayground │ └── Contents.swift ├── DB2VanishingMoments.playground │ ├── contents.xcplayground │ └── Contents.swift ├── DB4VanishingMoments.playground │ ├── contents.xcplayground │ └── Contents.swift ├── DB6VanishingMoments.playground │ ├── contents.xcplayground │ └── Contents.swift ├── WaveletPacketTransform.playground │ ├── contents.xcplayground │ └── Contents.swift ├── WaveletReassignment.playground │ ├── contents.xcplayground │ └── Contents.swift └── WaveletPlayground.playground │ ├── contents.xcplayground │ └── Contents.swift ├── MultilinearMathWS.xcworkspace └── contents.xcworkspacedata ├── MultilinearMath.xcodeproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcuserdata │ └── vincentherrmann.xcuserdatad │ └── xcschemes │ ├── xcschememanagement.plist │ ├── MultilinearMathTests.xcscheme │ ├── MultilinearMath.xcscheme │ ├── MultilinearMath 2.xcscheme │ └── MultilinearMathExample.xcscheme ├── FastWaveletTransform.playground ├── contents.xcplayground └── Contents.swift ├── MultilinearMath ├── MultilinearMath.h └── Info.plist ├── MultilinearMathTests ├── Info.plist ├── PlotTests.swift ├── FourierTransformTests.swift ├── MultidimensionalDataTests.swift ├── MultilinearMathTests.swift ├── DataSlicingTests.swift ├── NeuralNetTests.swift ├── DispatchPerformaceTests.swift ├── AccelerateFunctionTests.swift └── WaveletTests.swift ├── Sources ├── ActivationFunctions.swift ├── FIRFilter.swift ├── BoundingProtocols.swift ├── DataSetLoading.swift ├── LinearRegression.swift ├── Optimization.swift ├── WaveletPlots.swift ├── LogisticRegression.swift ├── TensorSyntacticOperators.swift ├── DaubechiesWavelets.swift ├── ComplexNumbers.swift ├── ComplexWavelets.swift ├── WaveletCreation.swift ├── TensorOperationNodes.swift ├── MPCA.swift ├── ParametricFunctions.swift ├── Utilities.swift └── NeuralNetwork.swift └── README.md /.build/build.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentherrmann/multilinear-math/HEAD/.build/build.db -------------------------------------------------------------------------------- /Resources/BackpropGraph.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentherrmann/multilinear-math/HEAD/Resources/BackpropGraph.jpg -------------------------------------------------------------------------------- /Resources/MNIST_compression_pca.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentherrmann/multilinear-math/HEAD/Resources/MNIST_compression_pca.jpg -------------------------------------------------------------------------------- /Resources/Slicing3dFlat_table.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentherrmann/multilinear-math/HEAD/Resources/Slicing3dFlat_table.jpg -------------------------------------------------------------------------------- /Resources/MNIST_compression_mpca.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentherrmann/multilinear-math/HEAD/Resources/MNIST_compression_mpca.jpg -------------------------------------------------------------------------------- /Resources/MNIST_compression_umpca.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentherrmann/multilinear-math/HEAD/Resources/MNIST_compression_umpca.jpg -------------------------------------------------------------------------------- /Resources/MNIST_compression_original.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentherrmann/multilinear-math/HEAD/Resources/MNIST_compression_original.jpg -------------------------------------------------------------------------------- /Resources/MultilinearDataUnfolding.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentherrmann/multilinear-math/HEAD/Resources/MultilinearDataUnfolding.jpg -------------------------------------------------------------------------------- /Resources/NestedVsMultidimensional.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentherrmann/multilinear-math/HEAD/Resources/NestedVsMultidimensional.jpg -------------------------------------------------------------------------------- /Resources/SlicingTwoModes3dFlat_table.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentherrmann/multilinear-math/HEAD/Resources/SlicingTwoModes3dFlat_table.jpg -------------------------------------------------------------------------------- /Resources/ExampleTextFile.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\cocoartf949 2 | {\fonttbl} 3 | {\colortbl;\red255\green255\blue255;} 4 | \margl1440\margr1440\vieww9000\viewh8400\viewkind0 5 | } -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | import PackageDescription 2 | 3 | let package = Package(name: "MultilinearMath", 4 | targets: [], 5 | dependencies: []) 6 | 7 | -------------------------------------------------------------------------------- /Playgrounds/FFTPlayground.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Playgrounds/TestPlayground.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /MultilinearMathWS.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MultilinearMath.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /FastWaveletTransform.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Playgrounds/MyPlayground.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Playgrounds/ComplexWavelets.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Playgrounds/DB2VanishingMoments.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Playgrounds/DB4VanishingMoments.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Playgrounds/DB6VanishingMoments.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Playgrounds/WaveletPacketTransform.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Playgrounds/WaveletReassignment.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Playgrounds/WaveletPlayground.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Playgrounds/FFTPlayground.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | //: Playground - noun: a place where people can play 2 | 3 | import Cocoa 4 | import MultilinearMath 5 | 6 | //setup nodes 7 | //let forwardFFT = FourierTransform(modeSizes: [4, 4]) 8 | //let inverseFFT = InverseFourierTransform(modeSizes: [4, 4]) 9 | // 10 | //let inputSignal = randomTensor(modeSizes: 2, 4, 4).uniquelyIndexed() 11 | //let transformedSignal = forwardFFT.execute([inputSignal]) 12 | 13 | let a: [Float] = [0, -2, 2.0] 14 | let quickLook = QuickArrayPlot(array: a) 15 | let bounds = quickLook.plotView.plottingBounds 16 | //let p = quickLook.plotView.plots[0].plotBounds 17 | -------------------------------------------------------------------------------- /MultilinearMath/MultilinearMath.h: -------------------------------------------------------------------------------- 1 | // 2 | // MultilinearMath.h 3 | // MultilinearMath 4 | // 5 | // Created by Vincent Herrmann on 29.03.16. 6 | // Copyright © 2016 Vincent Herrmann. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for MultilinearMath. 12 | FOUNDATION_EXPORT double MultilinearMathVersionNumber; 13 | 14 | //! Project version string for MultilinearMath. 15 | FOUNDATION_EXPORT const unsigned char MultilinearMathVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /MultilinearMathTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /MultilinearMath/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 0.0.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSHumanReadableCopyright 24 | Copyright © 2016 Vincent Herrmann. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /MultilinearMathTests/PlotTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PlotTests.swift 3 | // MultilinearMath 4 | // 5 | // Created by Vincent Herrmann on 12.08.16. 6 | // Copyright © 2016 Vincent Herrmann. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import MultilinearMath 11 | 12 | class PlotTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testQuickLook() { 25 | let a: [Float] = [2.0, -1.0, 4.0, 1.0] 26 | let quickLook = QuickArrayPlot(array: a) 27 | let q = quickLook.customPlaygroundQuickLook 28 | } 29 | 30 | func testExample() { 31 | // This is an example of a functional test case. 32 | // Use XCTAssert and related functions to verify your tests produce the correct results. 33 | } 34 | 35 | func testPerformanceExample() { 36 | // This is an example of a performance test case. 37 | self.measure { 38 | // Put the code you want to measure the time of here. 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /.build/debug/MultilinearMath.build/master.swiftdeps: -------------------------------------------------------------------------------- 1 | version: "Apple Swift version 3.0-dev (LLVM b010debd0e, Clang 3e4d01d89b, Swift 7182c58cb2)" 2 | options: "b2375201e0462b7219dbf580d9b392b4" 3 | build_time: [512471670, 417517000] 4 | inputs: 5 | "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/Sources/AccelerateFunctionsFloat.swift": !dirty [512422800, 0] 6 | "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/Sources/main.swift": !dirty [512422395, 0] 7 | "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/Sources/MPCA.swift": !dirty [512425649, 0] 8 | "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/Sources/MultidimensionalData.swift": !dirty [512469549, 0] 9 | "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/Sources/SlicingSubscripts.swift": !dirty [512422951, 0] 10 | "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/Sources/TensorMultiplication.swift": !dirty [512423052, 0] 11 | "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/Sources/TensorOperations.swift": !dirty [512423462, 0] 12 | "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/Sources/Tensors.swift": !dirty [512422994, 0] 13 | "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/Sources/UMPCA.swift": !dirty [512425680, 0] 14 | "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/Sources/Utilities.swift": !dirty [512431554, 0] 15 | -------------------------------------------------------------------------------- /MultilinearMathTests/FourierTransformTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FourierTransformTests.swift 3 | // MultilinearMath 4 | // 5 | // Created by Vincent Herrmann on 05.08.16. 6 | // Copyright © 2016 Vincent Herrmann. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import MultilinearMath 11 | 12 | class FourierTransformTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testFFTNodes() { 25 | //setup nodes 26 | let forwardFFT = FourierTransform(modeSizes: [3, 7]) 27 | let inverseFFT = InverseFourierTransform(modeSizes: [3, 7]) 28 | 29 | let inputSignal = randomTensor(modeSizes: 2, 3, 7).uniquelyIndexed() 30 | print("original signal: \(inputSignal.values)") 31 | let transformedSignal = forwardFFT.execute([inputSignal]) 32 | let reconstructedSignal = inverseFFT.execute(transformedSignal)[0] 33 | print("reconstructed signal: \(reconstructedSignal.values)") 34 | 35 | let factor = reconstructedSignal.values[0] / inputSignal.values[0] 36 | print("scaling factor for sizes \(inputSignal.modeSizes): \(factor)") 37 | 38 | let mse = meanSquaredError(target: inputSignal.values, result: reconstructedSignal.values) 39 | print("mse: \(mse)") 40 | XCTAssert(mse < 0.01, "no acceptable reconstruction from fourier transform") 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /Playgrounds/TestPlayground.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | //: Playground - noun: a place where people can play 2 | 3 | import Cocoa 4 | import MultilinearMath 5 | 6 | let inputData = zeros(20, 4, 5) 7 | let projectionModeSizes = [3, 3] 8 | 9 | let data = inputData.uniquelyIndexed() 10 | let sampleModeCount = data.modeCount-1 11 | 12 | var projectionMatrices: [Tensor] = [] 13 | let projectionModeIndices = TensorIndex.uniqueIndexArray(sampleModeCount, excludedIndices: data.indices) 14 | 15 | for n in 0..(diagonalWithModeSizes: modeSizes, repeatedValue: 1.0) 18 | thisProjectionMatrix.indices = [projectionModeIndices[n], data.indices[n+1]] 19 | projectionMatrices.append(thisProjectionMatrix) 20 | } 21 | 22 | public func multilinearPCAProjectionP(data data: Tensor, projectionMatrices: [Tensor], doNotProjectModes: [Int] = []) -> Tensor { 23 | 24 | var currentData = data 25 | for n in 0..(modeSizes: [2, 3, 4], values: Array(0..<24).map({return Float($0)})) 26 | //var b = Tensor(scalar: 1.0) 27 | 28 | var indices: [TensorIndex] = [.a, .b, .c] 29 | t.indices = indices 30 | 31 | var reorderedT = t.reorderModes([2, 0, 1]) 32 | XCTAssertEqual(t[0, 1, 2], reorderedT[2, 0, 1], "random order") 33 | 34 | 35 | t = Tensor(modeSizes: [2, 3, 4, 5, 6, 7], values: Array(0..<5040).map({return Float($0)})) 36 | indices = [.a, .b, .c, .d, .e, .f] 37 | t.indices = indices 38 | 39 | reorderedT = t.reorderModes([0, 1, 2, 4, 3, 5]) 40 | XCTAssertEqual(t[0, 1, 2, 3, 4, 5], reorderedT[0, 1, 2, 4, 3, 5], "swap .d and .e") 41 | 42 | reorderedT = t.reorderModes([4, 5, 0, 2, 3, 1]) 43 | XCTAssertEqual(t[0, 1, 2, 3, 4, 5], reorderedT[4, 5, 0, 2, 3, 1], "random order") 44 | 45 | reorderedT = t.reorderModes([5, 4, 3, 2, 1, 0]) 46 | XCTAssertEqual(t[0, 1, 2, 3, 2, 1], reorderedT[1, 2, 3, 2, 1, 0], "inverse order") 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /MultilinearMath.xcodeproj/xcuserdata/vincentherrmann.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | MultilinearMath 2.xcscheme 8 | 9 | isShown 10 | 11 | orderHint 12 | 1 13 | 14 | MultilinearMath.xcscheme 15 | 16 | isShown 17 | 18 | orderHint 19 | 0 20 | 21 | MultilinearMathExample.xcscheme 22 | 23 | orderHint 24 | 4 25 | 26 | MultilinearMathTests.xcscheme 27 | 28 | orderHint 29 | 2 30 | 31 | 32 | SuppressBuildableAutocreation 33 | 34 | BA0BE4641CCAA81D0024F368 35 | 36 | primary 37 | 38 | 39 | BA275E0E1CAAD3B100B1FEC3 40 | 41 | primary 42 | 43 | 44 | BA275E171CAAD3B200B1FEC3 45 | 46 | primary 47 | 48 | 49 | BA3EFBC51CAB12FA00F723D4 50 | 51 | primary 52 | 53 | 54 | BA5D85DA1CAA7A03002D142F 55 | 56 | primary 57 | 58 | 59 | BA67AE581CA86C3E009906F2 60 | 61 | primary 62 | 63 | 64 | BA8FD7B31CA91CDC005A9986 65 | 66 | primary 67 | 68 | 69 | BAC83E341CAAD61400290119 70 | 71 | primary 72 | 73 | 74 | BAC83E3D1CAAD61500290119 75 | 76 | primary 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /MultilinearMathTests/MultilinearMathTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MultilinearMathTests.swift 3 | // MultilinearMathTests 4 | // 5 | // Created by Vincent Herrmann on 29.03.16. 6 | // Copyright © 2016 Vincent Herrmann. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import MultilinearMath 11 | 12 | class MultilinearMathTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | 28 | var testArray: [Float] = [] 29 | let reduceTest1 = testArray.reduce(1, {$0*$1}) 30 | testArray = [1.0] 31 | let reduceTest2 = testArray.reduce(1, {$0*$1}) 32 | testArray = [2.0, 1.0, 0.0] 33 | let reduceTest3 = testArray.reduce(1, {$0*$1}) 34 | 35 | } 36 | 37 | func testPerform() { 38 | 39 | print("") 40 | let tensor = Tensor(modeSizes: [2, 3, 2], values: Array(0..<12).map({return Float($0)})) 41 | let sum1 = sum(tensor, overModes: [0]) 42 | XCTAssertEqual(sum1.values, [6.0, 8.0, 10.0, 12.0, 14.0, 16.0], "sum over mode 0") 43 | print("") 44 | let sum2 = sum(tensor, overModes: [1]) 45 | XCTAssertEqual(sum2.values, [6.0, 9.0, 24.0, 27.0], "sum over mode 1") 46 | print("") 47 | let sum3 = sum(tensor, overModes: [0, 1]) 48 | XCTAssertEqual(sum3.values, [30.0, 36.0], "sum over mode 0 and 1") 49 | print("") 50 | let sum4 = sum(tensor, overModes: [0, 1, 2]) 51 | XCTAssertEqual(sum4.values, [66.0], "sum over mode 0") 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /Sources/ActivationFunctions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ActivationFunctions.swift 3 | // MultilinearMath 4 | // 5 | // Created by Vincent Herrmann on 19.06.16. 6 | // Copyright © 2016 Vincent Herrmann. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol ActivationFunction { 12 | func output(_ input: Tensor) -> Tensor 13 | func derivative(_ input: Tensor) -> Tensor 14 | } 15 | 16 | public struct Sigmoid: ActivationFunction { 17 | public func output(_ input: Tensor) -> Tensor { 18 | let values = input.values.map { (x) -> Float in 19 | if x >= 0 { 20 | return 1 / (1 + (exp(-x))) 21 | } else { 22 | let e = exp(x) 23 | return e / (1 + e) 24 | } 25 | } 26 | let tensor = Tensor(withPropertiesOf: input, values: values) 27 | return tensor 28 | //return 1 / (1 + exp(-input)) //this method would be numerically unstable 29 | } 30 | public func derivative(_ input: Tensor) -> Tensor { 31 | let s = output(input) 32 | let result = s °* (1-s) 33 | return result 34 | } 35 | } 36 | 37 | public struct ReLU: ActivationFunction { 38 | public var secondarySlope: Float 39 | 40 | public init(secondarySlope: Float) { 41 | self.secondarySlope = secondarySlope 42 | } 43 | 44 | public func output(_ input: Tensor) -> Tensor { 45 | return Tensor(withPropertiesOf: input, values: input.values.map({max(secondarySlope*$0, $0)})) 46 | } 47 | public func derivative(_ input: Tensor) -> Tensor { 48 | return Tensor(withPropertiesOf: input, values: input.values.map({$0 > 0 ? 1.0 : secondarySlope})) 49 | } 50 | } 51 | 52 | public struct Softplus: ActivationFunction { 53 | public func output(_ input: Tensor) -> Tensor { 54 | let output = log(1 + exp(input)) 55 | return output 56 | } 57 | public func derivative(_ input: Tensor) -> Tensor { 58 | let expo = exp(input) 59 | let der = expo °/ (1 + expo) 60 | return der 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /MultilinearMath.xcodeproj/xcuserdata/vincentherrmann.xcuserdatad/xcschemes/MultilinearMathTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 14 | 15 | 17 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 39 | 40 | 41 | 42 | 48 | 49 | 51 | 52 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /Sources/FIRFilter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FIRFilter.swift 3 | // MultilinearMath 4 | // 5 | // Created by Vincent Herrmann on 15.08.16. 6 | // Copyright © 2016 Vincent Herrmann. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct FIRFilter { 12 | public var coefficients: [Float] 13 | public var order: Int { 14 | get {return coefficients.count} 15 | } 16 | 17 | public init(coefficients: [Float]) { 18 | self.coefficients = coefficients 19 | } 20 | 21 | public func zTransform(_ z: ComplexNumber) -> ComplexNumber { 22 | // print("z transform at: \(z)") 23 | var x: ComplexNumber = 0 + i*0 24 | for n in 0.. ComplexNumber { 35 | return zTransform(cos(omega) + (i*sin(omega))) 36 | } 37 | 38 | public func frequencyResponse(resolution: Int = 100) -> (response: [ComplexNumber], frequencies: [Float]) { 39 | let xArray = Array(0.. (r: Float, i: Float) { 47 | return (a.0 + b.0, a.1 + b.1) 48 | } 49 | 50 | public func substractComplex(a: (Float, Float), b: (Float, Float)) -> (r: Float, i: Float) { 51 | return (a.0 - b.0, a.1 - b.1) 52 | } 53 | 54 | public func multiplyComplex(_ a: (Float, Float), b: (Float, Float)) -> (r: Float, i: Float) { 55 | let r = a.0 * b.0 - a.1 * b.1 56 | let i = a.1 * b.0 + a.0 * b.1 57 | return (r, i) 58 | } 59 | 60 | public func divideComplex(a: (Float, Float), b: (Float, Float)) -> (r: Float, i: Float) { 61 | let den = b.0 * b.0 + b.1 * b.1 62 | let r = (a.0 * b.0 + a.1 * b.1) / den 63 | let i = (a.1 * b.0 - a.0 * b.1) / den 64 | return (r, i) 65 | } 66 | 67 | public func powComplex(_ z: (Float, Float), n: Int) -> (r: Float, i: Float) { 68 | if(n == 0) { 69 | return (1, 0) 70 | } 71 | 72 | let m = n<0 ? -n : n 73 | var r: (Float, Float) = (1, 0) 74 | for _ in 0.. 0) { 80 | return r 81 | } else { 82 | let denom = r.0*r.0 + r.1*r.1 83 | return(r.0/denom, -r.1/denom) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /MultilinearMathTests/DataSlicingTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DataSlicingTests.swift 3 | // MultilinearMath 4 | // 5 | // Created by Vincent Herrmann on 29.03.16. 6 | // Copyright © 2016 Vincent Herrmann. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import MultilinearMath 11 | 12 | class DataSlicingTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testArrayBasedFastSlicing() { 25 | var originalTensor = zeros(5, 5, 5) 26 | let sliceTensor = ones(2, 3, 2) 27 | let subscripts: [DataSliceSubscript] = [[2, 4], [0, 1, 3], [1, 4]] 28 | 29 | //copyIndices(subscripts, modeSizes: [5, 5, 5]) 30 | originalTensor.values.withUnsafeMutableBufferPointer { (pointer) -> () in 31 | // 32 | copySliceFrom(sliceTensor, to: originalTensor, targetPointer: pointer, subscripts: subscripts, copyFromSlice: true) 33 | } 34 | 35 | } 36 | 37 | func testRangeSlicing() { 38 | var testData = Tensor(modeSizes: [3, 4, 5], values: Array(0..<60).map({return Float($0)})) 39 | 40 | let testSlice1 = testData[1..<2, 3..<4, 2..<5] 41 | let compareValues1: [Float] = [37, 38, 39] 42 | XCTAssertEqual(testSlice1.values, compareValues1, "data slice 1D") 43 | 44 | let testSlice2 = testData[0...1, 2...2, 1...2] 45 | let compareValues2: [Float] = [11, 12, 31, 32] 46 | XCTAssertEqual(testSlice2.values, compareValues2, "data slice 2D") 47 | 48 | let testSlice3 = testData[1...2, [0, 1, 2], 3...4] 49 | let compareValues3: [Float] = [23, 24, 28, 29, 33, 34, 43, 44, 48, 49, 53, 54] 50 | XCTAssertEqual(testSlice3.values, compareValues3, "data slice 3D") 51 | 52 | let testSlice4 = testData[0..<1, 0..<4, 0..<1] 53 | let compareValues4: [Float] = [0, 5, 10, 15] 54 | XCTAssertEqual(testSlice4.values, compareValues4, "data slice with variadic subscript") 55 | 56 | let slice = Tensor(modeSizes: [2, 3, 2], values: Array(0..<12).map({return Float($0)})) 57 | testData[0...1, 1...3, [1, 4]] = slice 58 | XCTAssertEqual(testData[0, 1, 1], 0, "slice replacement 1") 59 | XCTAssertEqual(testData[0, 1, 4], 1, "slice replacement 2") 60 | XCTAssertEqual(testData[0, 2, 1], 2, "slice replacement 3") 61 | XCTAssertEqual(testData[1, 3, 4], 11, "slice replacement 4") 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /MultilinearMathTests/NeuralNetTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NeuralNetTests.swift 3 | // MultilinearMath 4 | // 5 | // Created by Vincent Herrmann on 13.06.16. 6 | // Copyright © 2016 Vincent Herrmann. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import MultilinearMath 11 | 12 | class NeuralNetTests: XCTestCase { 13 | 14 | func testMNISTClassification() { 15 | print("load MNIST data...") 16 | let rawData = loadMNISTImageFile("/Users/vincentherrmann/Documents/Software/DataSets/MNIST/train-images-2.idx3-ubyte") 17 | let rawLabels = loadMNISTLabelFile("/Users/vincentherrmann/Documents/Software/DataSets/MNIST/train-labels.idx1-ubyte") 18 | print("normalize...") 19 | let data = normalize(Tensor(modeSizes: [rawData.modeSizes[0], 28*28], values: rawData.values), overModes: [0]).normalizedTensor 20 | let labels = createOneHotVectors(rawLabels.map({Int($0)}), differentValues: Array(0...9)) 21 | let trainingData = data[0..<50000, all] 22 | let trainingLabels = labels[0..<50000, all] 23 | let validationData = data[50000..<60000, all] 24 | let validationLabels = rawLabels[50000..<60000].map({Float($0)}) 25 | 26 | let estimator = NeuralNet(layerSizes: [28*28, 40, 10]) 27 | estimator.layers[0].activationFunction = ReLU(secondarySlope: 0.01) 28 | estimator.layers[1].activationFunction = ReLU(secondarySlope: 0.01) 29 | let neuralNetCost = SquaredErrorCost(forEstimator: estimator) 30 | let regularizer = ParameterDecay(decayRate: 0.0001) 31 | neuralNetCost.regularizers[0] = regularizer 32 | neuralNetCost.regularizers[2] = regularizer 33 | 34 | let epochs = 30 35 | stochasticGradientDescent(neuralNetCost, inputs: trainingData[.a, .b], targets: trainingLabels[.a, .c], updateRate: 0.1, minibatchSize: 50, validationCallback: ({ (epoch, estimator) -> (Bool) in 36 | print("epoch \(epoch)") 37 | 38 | let estimate = estimator.output(validationData) 39 | let maximumIndices = findMaximumElementOf(estimate, inMode: 1) 40 | let correctValues = zip(validationLabels, maximumIndices.values).filter({$0.0 == $0.1}) 41 | print("classified \(correctValues.count) of \(validationLabels.count) correctly") 42 | if(epoch >= epochs) { 43 | return true 44 | } else { 45 | return false 46 | } 47 | })) 48 | 49 | let testBatch = validationData[0..<10, all] 50 | let finalEstimate = neuralNetCost.estimator.output(testBatch) 51 | print("finalEstimate: \(finalEstimate.values)") 52 | let maximumIndices = findMaximumElementOf(finalEstimate, inMode: 1) 53 | print("max indices: \(maximumIndices.values)") 54 | let target = Array(validationLabels[0..<10]) 55 | print("targets: \(target)") 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /.build/debug/MultilinearMath.build/main.swiftdeps: -------------------------------------------------------------------------------- 1 | ### Swift dependencies file v0 ### 2 | provides-top-level: 3 | provides-nominal: 4 | provides-member: 5 | provides-dynamic-lookup: 6 | depends-top-level: 7 | - "print" 8 | - "StringLiteralType" 9 | depends-member: 10 | depends-nominal: 11 | depends-dynamic-lookup: 12 | depends-external: 13 | - "/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2016-03-24-a.xctoolchain/usr/lib/swift/macosx/x86_64/Swift.swiftmodule" 14 | - "/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2016-03-24-a.xctoolchain/usr/lib/swift/shims/Visibility.h" 15 | - "/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2016-03-24-a.xctoolchain/usr/lib/swift/shims/UnicodeShims.h" 16 | - "/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2016-03-24-a.xctoolchain/usr/lib/swift/shims/RuntimeStubs.h" 17 | - "/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2016-03-24-a.xctoolchain/usr/lib/swift/shims/SwiftStdint.h" 18 | - "/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2016-03-24-a.xctoolchain/usr/lib/swift/shims/SwiftStddef.h" 19 | - "/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2016-03-24-a.xctoolchain/usr/lib/swift/shims/RuntimeShims.h" 20 | - "/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2016-03-24-a.xctoolchain/usr/lib/swift/shims/LibcShims.h" 21 | - "/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2016-03-24-a.xctoolchain/usr/lib/swift/shims/RefCount.h" 22 | - "/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2016-03-24-a.xctoolchain/usr/lib/swift/shims/HeapObject.h" 23 | - "/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2016-03-24-a.xctoolchain/usr/lib/swift/shims/GlobalObjects.h" 24 | - "/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2016-03-24-a.xctoolchain/usr/lib/swift/shims/FoundationShims.h" 25 | - "/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2016-03-24-a.xctoolchain/usr/lib/swift/shims/CoreFoundationShims.h" 26 | - "/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2016-03-24-a.xctoolchain/usr/lib/swift/shims/module.map" 27 | - "/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2016-03-24-a.xctoolchain/usr/lib/swift/macosx/x86_64/SwiftOnoneSupport.swiftmodule" 28 | - "/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2016-03-24-a.xctoolchain/usr/lib/swift/macosx/x86_64/Foundation.swiftmodule" 29 | - "/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2016-03-24-a.xctoolchain/usr/lib/swift/macosx/x86_64/Darwin.swiftmodule" 30 | - "/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2016-03-24-a.xctoolchain/usr/lib/swift/macosx/x86_64/Dispatch.swiftmodule" 31 | - "/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2016-03-24-a.xctoolchain/usr/lib/swift/macosx/x86_64/ObjectiveC.swiftmodule" 32 | - "/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2016-03-24-a.xctoolchain/usr/lib/swift/macosx/x86_64/CoreGraphics.swiftmodule" 33 | - "/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2016-03-24-a.xctoolchain/usr/lib/swift/macosx/x86_64/IOKit.swiftmodule" 34 | interface-hash: "86d27d5162ef50def2df123343819566" 35 | -------------------------------------------------------------------------------- /MultilinearMathTests/DispatchPerformaceTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DispatchPerformaceTests.swift 3 | // MultilinearMath 4 | // 5 | // Created by Vincent Herrmann on 22.04.16. 6 | // Copyright © 2016 Vincent Herrmann. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import MultilinearMath 11 | 12 | class DispatchPerformaceTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testNormalizeSimple() { 25 | let values = Array(0..<125000).map({return Float($0)}) 26 | let tensor = Tensor(modeSizes: [50, 50, 50], values: values) 27 | 28 | self.measure { //0.046 / 0.01 sec //0.013 / 0.005 sec //0.021 / 0.003 sec 29 | let normalized = normalize(tensor, overModes: [0, 2]) 30 | } 31 | } 32 | 33 | func testMode0Normalization() { 34 | let tensor = randomTensor(modeSizes: 5000, 100) 35 | 36 | self.measure { //4.7 / 0.74 sec //1.182 / 0.582 sec //2.0 / 0.128 sec 37 | let normalized = normalize(tensor, overModes: [0]) 38 | } 39 | 40 | } 41 | 42 | func testMode1Normalization() { 43 | let tensor = randomTensor(modeSizes: 5000, 100) 44 | 45 | self.measure { //0.29 / 0.085 sec //0.277 / 0.15 sec //0.78 / 0.193 sec 46 | let normalized = normalize(tensor, overModes: [1]) 47 | } 48 | } 49 | 50 | func testMode0NormalizationReverse() { 51 | let tensor = randomTensor(modeSizes: 100, 5000) 52 | 53 | self.measure { //4.5 / 0.89 sec //1.4 / 0.713 sec //2.462 / 0.265 sec 54 | let normalized = normalize(tensor, overModes: [0]) 55 | } 56 | } 57 | 58 | func testMode1NormalizationReverse() { 59 | let tensor = randomTensor(modeSizes: 100, 5000) 60 | 61 | self.measure { //0.022 / 0.011 sec //0.048 / 0.043 sec //0.34 / 0.048 sec 62 | let normalized = normalize(tensor, overModes: [1]) 63 | } 64 | } 65 | 66 | func testMode0Slicing() { 67 | let tensor = ones(10000, 30) 68 | 69 | self.measure { //0.07 / 0.039 sec //0.019 / 0.023 sec //0.032 / 0.002 sec 70 | let slice = tensor[all, 7...7] 71 | 72 | } 73 | } 74 | 75 | func testMode0SlicingRev() { 76 | let tensor = ones(30, 10000) 77 | 78 | self.measure {//0.000 sec //0.000 sec //0.000 sec //0.000 / 0.000 sec 79 | let slice = tensor[all, 7...7] 80 | } 81 | } 82 | 83 | // func testNormalizeDispatched() { 84 | // let values = Array(0..<125000).map({return Float($0)}) 85 | // let tensor = Tensor(modeSizes: [50, 50, 50], values: values) 86 | // 87 | // self.measureBlock { 88 | // let normalized = normalizeConcurrent(tensor, overModes: [0, 2]) 89 | // } 90 | // } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /Sources/BoundingProtocols.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BoundingProtocols.swift 3 | // MultilinearMath 4 | // 5 | // Created by Vincent Herrmann on 12.08.16. 6 | // Copyright © 2016 Vincent Herrmann. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol Joinable { 12 | func join(with: Self...) -> Self 13 | 14 | static func join(_ j: [Self]) -> Self 15 | } 16 | 17 | extension CGRect: Joinable { 18 | public func join(with: CGRect...) -> CGRect { 19 | let j = [self] + with 20 | return CGRect.join(j) 21 | } 22 | 23 | public static func join(_ j: [CGRect]) -> CGRect { 24 | let minX = j.map({$0.minX}).min()! 25 | let minY = j.map({$0.minY}).min()! 26 | let maxX = j.map({$0.maxX}).max()! 27 | let maxY = j.map({$0.maxY}).max()! 28 | return CGRect(x: minX, y: minY, width: maxX-minX, height: maxY-minY) 29 | } 30 | } 31 | 32 | public protocol Bounded2D { 33 | var boundingRect: NSRect {get} 34 | } 35 | 36 | public protocol Plotting2D { 37 | var plots: [PlottableIn2D] {get set} 38 | var screenBounds: CGRect {get} 39 | var plottingBounds: CGRect {get set} 40 | 41 | func updatePlotting() 42 | } 43 | public extension Plotting2D { 44 | typealias Transform = (scaleX: CGFloat, scaleY: CGFloat, translateX: CGFloat, translateY: CGFloat) 45 | 46 | var transformFromPlotToScreen: Transform { 47 | get { 48 | return transformParameters(plottingBounds, to: screenBounds) 49 | } 50 | } 51 | 52 | func transformParameters(_ from: NSRect, to: NSRect) -> Transform { 53 | let scaleX = to.width / from.width 54 | let scaleY = to.height / from.height 55 | let translateX = -from.minX * scaleX + to.minX 56 | let translateY = -from.minY * scaleY + to.minY 57 | return (scaleX, scaleY, translateX, translateY) 58 | } 59 | 60 | func convertFromPlotToScreen(_ point: CGPoint) -> CGPoint { 61 | let t = transformParameters(plottingBounds, to: screenBounds) 62 | return CGPoint(x: point.x * t.scaleX + t.translateX, y: point.y * t.scaleY + t.translateY) 63 | } 64 | 65 | func convertFromScreenToPlot(_ point: CGPoint) -> CGPoint { 66 | let t = transformParameters(screenBounds, to: plottingBounds) 67 | return CGPoint(x: point.x * t.scaleX + t.translateX, y: point.y * t.scaleY + t.translateY) 68 | } 69 | 70 | mutating func addPlottable(_ newPlottable: PlottableIn2D) { 71 | newPlottable.fitTo(self) 72 | plots.append(newPlottable) 73 | } 74 | 75 | mutating func setPlottingBounds(_ newBounds: NSRect) { 76 | plottingBounds = newBounds 77 | if(newBounds.width == 0) { 78 | plottingBounds.origin.x += -0.5 79 | plottingBounds.size.width = 1 80 | } 81 | if(newBounds.height == 0) { 82 | plottingBounds.origin.y += -0.5 83 | plottingBounds.size.height = 1 84 | } 85 | } 86 | 87 | func updatePlotting() { 88 | for plot in plots { 89 | plot.fitTo(self) 90 | } 91 | } 92 | } 93 | 94 | public protocol PlottableIn2D { 95 | func draw() 96 | func fitTo(_ plotting: Plotting2D) 97 | } 98 | -------------------------------------------------------------------------------- /Playgrounds/ComplexWavelets.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | //: Playground - noun: a place where people can play 2 | 3 | import Cocoa 4 | import MultilinearMath 5 | 6 | print("playground started") 7 | 8 | let cA4L2 = calculateComplexWaveletCoefficients(vanishingMoments: 4, delayCoefficients: 3, rootsOutsideUnitCircle: [1, 2]) 9 | let cReal = Array(cA4L2.map({$0.real})) 10 | cReal 11 | let cRealW = Array(zip(cReal, Array(0.. Tensor { 12 | guard let data = try? Data(contentsOf: URL(fileURLWithPath: path)) else { 13 | print("could not load file \(path)") 14 | return Tensor(scalar: 0) 15 | } 16 | 17 | var magicNumber: UInt32 = 333 18 | (data as NSData).getBytes(&magicNumber, range: NSRange(location: 0, length: 4)) 19 | magicNumber = CFSwapInt32BigToHost(magicNumber) 20 | 21 | var imageCount: UInt32 = 10000 22 | (data as NSData).getBytes(&imageCount, range: NSRange(location: 4, length: 4)) 23 | imageCount = CFSwapInt32BigToHost(imageCount) 24 | 25 | var rows: UInt32 = 28 26 | (data as NSData).getBytes(&rows, range: NSRange(location: 8, length: 4)) 27 | rows = CFSwapInt32BigToHost(rows) 28 | 29 | var columns: UInt32 = 28 30 | (data as NSData).getBytes(&columns, range: NSRange(location: 12, length: 4)) 31 | columns = CFSwapInt32BigToHost(columns) 32 | 33 | print("load \(imageCount) MNIST images of size \(rows) x \(columns)") 34 | 35 | let elementCount = Int(imageCount * rows * columns) 36 | var values = [Float](repeating: 0, count: elementCount) 37 | 38 | let startIndex: Int = 16 39 | 40 | for i in 0..(modeSizes: [Int(imageCount), Int(rows), Int(columns)], values: values) 48 | } 49 | 50 | public func loadMNISTLabelFile(_ path: String) -> [UInt8] { 51 | guard let data = try? Data(contentsOf: URL(fileURLWithPath: path)) else { 52 | print("could not load file \(path)") 53 | return [] 54 | } 55 | 56 | var magicNumber: UInt32 = 333 57 | (data as NSData).getBytes(&magicNumber, range: NSRange(location: 0, length: 4)) 58 | magicNumber = CFSwapInt32BigToHost(magicNumber) 59 | 60 | var itemCount: UInt32 = 10000 61 | (data as NSData).getBytes(&itemCount, range: NSRange(location: 4, length: 4)) 62 | itemCount = CFSwapInt32BigToHost(itemCount) 63 | 64 | print("load \(itemCount) labels") 65 | 66 | let startIndex: Int = 8 67 | let elementCount = Int(itemCount) 68 | var values = [UInt8](repeating: 0, count: elementCount) 69 | 70 | for i in 0.. Tensor { 81 | var oneHotVectors = zeros(labels.count, differentValues.count) 82 | for i in 0.., y: Tensor) -> Tensor { 12 | 13 | let example = TensorIndex.a 14 | let feature = TensorIndex.b 15 | 16 | let exampleCount = x.modeSizes[0] 17 | let featureCount = x.modeSizes[1] 18 | 19 | var samples = Tensor(modeSizes: [exampleCount, featureCount + 1], repeatedValue: 1) 20 | samples[all, 1...featureCount] = x 21 | 22 | // formula: w = (X^T * X)^-1 * X * y 23 | let sampleCovariance = samples[example, feature] * samples[example, .k] 24 | let inverseCovariance = inverse(sampleCovariance, rowMode: 0, columnMode: 1) 25 | let parameters = inverseCovariance[feature, .k] * samples[example, .k] * y[example] 26 | 27 | return parameters 28 | } 29 | 30 | open class LinearRegressionEstimator: ParametricTensorFunction { 31 | open var parameters: [Tensor] 32 | var currentInput: Tensor = zeros() 33 | 34 | fileprivate let example = TensorIndex.a 35 | fileprivate let feature = TensorIndex.b 36 | 37 | public init(featureCount: Int) { 38 | parameters = [zeros(featureCount), zeros()] 39 | parameters[0].indices = [feature] 40 | } 41 | 42 | open func output(_ input: Tensor) -> Tensor { 43 | if(input.modeCount == 1) { 44 | currentInput = Tensor(modeSizes: [1, input.modeSizes[0]], values: input.values) 45 | currentInput.indices = [example, feature] 46 | } else { 47 | currentInput = input[example, feature] 48 | } 49 | 50 | let hypothesis = (currentInput * parameters[0]) + parameters[1] 51 | return hypothesis 52 | } 53 | 54 | open func gradients(_ gradientWrtOutput: Tensor) -> (wrtInput: Tensor, wrtParameters: [Tensor]) { 55 | let parameter0Gradient = gradientWrtOutput * currentInput 56 | let parameter1Gradient = sum(gradientWrtOutput, overModes: [0]) 57 | let inputGradient = sum(gradientWrtOutput * parameters[0], overModes: [0]) 58 | 59 | return (inputGradient, [parameter0Gradient, parameter1Gradient]) 60 | } 61 | 62 | open func updateParameters(_ subtrahends: [Tensor]) { 63 | parameters[0] = parameters[0] - subtrahends[0] 64 | parameters[1] = parameters[1] - subtrahends[1] 65 | } 66 | } 67 | 68 | /// Squared error cost for linear regression 69 | open class LinearRegressionCost: CostFunction { 70 | open var estimator: ParametricTensorFunction 71 | open var regularizers: [ParameterRegularizer?] = [nil, nil] 72 | 73 | public init(featureCount: Int) { 74 | estimator = LinearRegressionEstimator(featureCount: featureCount) 75 | } 76 | 77 | open func costForEstimate(_ estimate: Tensor, target: Tensor) -> Float { 78 | let exampleCount = Float(target.elementCount) 79 | 80 | let distance = estimate - target 81 | let cost = (0.5 / exampleCount) * (distance * distance) 82 | 83 | return cost.values[0] 84 | } 85 | 86 | open func gradientForEstimate(_ estimate: Tensor, target: Tensor) -> Tensor { 87 | if(estimate.indices != target.indices) { 88 | print("abstract indices of estimate and target should be the same!") 89 | } 90 | let exampleCount = Float(target.elementCount) 91 | let gradient = (1/exampleCount) * (estimate - target) 92 | 93 | return gradient 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /MultilinearMath.xcodeproj/xcuserdata/vincentherrmann.xcuserdatad/xcschemes/MultilinearMath.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /MultilinearMath.xcodeproj/xcuserdata/vincentherrmann.xcuserdatad/xcschemes/MultilinearMath 2.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /MultilinearMath.xcodeproj/xcuserdata/vincentherrmann.xcuserdatad/xcschemes/MultilinearMathExample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /MultilinearMathTests/AccelerateFunctionTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AccelerateFunctionTests.swift 3 | // MultilinearMath 4 | // 5 | // Created by Vincent Herrmann on 13.08.16. 6 | // Copyright © 2016 Vincent Herrmann. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import MultilinearMath 11 | 12 | class AccelerateFunctionTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testLinearEquations() { 30 | let matrix: [Float] = [2, 4, -1, 1] 31 | let results: [Float] = [3, 0] 32 | var solution: [Float] = [] 33 | matrix.withUnsafeBufferPointer { (a) -> () in 34 | results.withUnsafeBufferPointer({ (b) -> () in 35 | solution = solveLinearEquationSystem(a, factorMatrixSize: MatrixSize(rows: 2, columns: 2), results: b, resultsSize: MatrixSize(rows: 2, columns: 1)) 36 | }) 37 | } 38 | XCTAssert(solution == [0.5, 0.5], "error in linear equation system solution") 39 | //print("solution: \(solution)") 40 | 41 | let m1: [Float] = [1, 1, 1, 42 | 0, 2, 5, 43 | 2, 5, -1] 44 | let r1: [Float] = [6, -4, 27] 45 | let s1 = solveLinearEquationSystem(m1, factorMatrixSize: MatrixSize(rows: 3, columns: 3), results: r1, resultsSize: MatrixSize(rows: 3, columns: 1)) 46 | print("solution: \(s1)") 47 | 48 | 49 | for size in 2..<8 { 50 | print("") 51 | print("size: \(size)") 52 | let m: [Float] = randomTensor(min: -1, max: 1, modeSizes: size, size).values 53 | let r: [Float] = randomTensor(min: -1, max: 1, modeSizes: size).values 54 | solveLinearEquationSystem(m, factorMatrixSize: MatrixSize(rows: size, columns: size), results: r, resultsSize: MatrixSize(rows: size, columns: 1)) 55 | } 56 | } 57 | 58 | func testWaveletComputation() { 59 | let db4: [Float] = [0.6830127, 1.1830127, 0.3169873, -0.1830127] 60 | let coefficients = db4 61 | let count = coefficients.count 62 | var factorMatrix = Tensor(modeSizes: [count, count], repeatedValue: 0) 63 | for r in 0..= count) {continue} 68 | factorMatrix[r, index] = coefficients[c] 69 | } 70 | factorMatrix[r, r] += -1 71 | } 72 | factorMatrix[count-1...count-1, all] = ones(4) 73 | print("factor matrix: \(factorMatrix.values)") 74 | 75 | //count = 5 76 | //factorMatrix = randomTensor(min: -1, max: 1, modeSizes: count, count) 77 | let results: [Float] = [Float](repeating: 0, count: count-1) + [1] 78 | var solution: [Float] = [] 79 | factorMatrix.values.withUnsafeBufferPointer { (a) -> () in 80 | results.withUnsafeBufferPointer({ (b) -> () in 81 | solution = solveLinearEquationSystem(a, factorMatrixSize: MatrixSize(rows: count, columns: count), results: b, resultsSize: MatrixSize(rows: count, columns: 1)) 82 | }) 83 | } 84 | print("solution: \(solution)") 85 | } 86 | 87 | func testPerformanceExample() { 88 | // This is an example of a performance test case. 89 | self.measure { 90 | // Put the code you want to measure the time of here. 91 | } 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /Sources/Optimization.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Optimization.swift 3 | // MultilinearMath 4 | // 5 | // Created by Vincent Herrmann on 04.05.16. 6 | // Copyright © 2016 Vincent Herrmann. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | //public func batchGradientDescent(objective: GradientOptimizable, input: Tensor, output: Tensor, updateRate: Float, convergenceThreshold: Float = 0.001, maxLoops: Int = 1000) { 12 | // 13 | // var cost = FLT_MAX 14 | // 15 | // for _ in 0.., targets: Tensor, updateRate: Float, convergenceThreshold: Float = 0.00001, maxLoops: Int = Int.max, minibatchSize: Int = 16, validationCallback: (_ currentEpoch: Int, _ currentEstimator: ParametricTensorFunction) -> (Bool) = {(epoch, _) in print("epoch \(epoch)"); return false}) { 29 | 30 | var cost = FLT_MAX 31 | var epoch = 0 32 | var currentBatch = inputs 33 | var currentBatchTargets = targets 34 | var currentIndex = 0 35 | var convergenceCounter = 0 36 | 37 | print("stochastic gradient descent") 38 | 39 | for _ in 0.. 42 | var minibatchTargets: Tensor 43 | if(currentIndex + minibatchSize < currentBatch.modeSizes[0]) { 44 | let minibatchRange = CountableRange(start: currentIndex, distance: minibatchSize) 45 | 46 | minibatch = currentBatch[minibatchRange, all] 47 | minibatchTargets = currentBatchTargets[minibatchRange, all] 48 | 49 | currentIndex += minibatchSize 50 | } else { 51 | //call validiation callback, if it returns true, break the optimization loop 52 | if(validationCallback(epoch, objective.estimator)) { 53 | break 54 | } 55 | 56 | let minibatchRange = currentIndex..= 3) { 85 | print("converged!") 86 | break 87 | } 88 | } 89 | cost = newCost 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # multilinear-math 2 | Swift library for multidimensional data, tensor operations and machine learning on OS X. For additional comments and documentation, please take a look at the [Wiki](https://github.com/vincentherrmann/multilinear-math/wiki). 3 | 4 | Already implemented: 5 | - Swift wrappers of many important functions from the Accelerate framework and LAPACK (vector summation, addition, substraction, matrix and elementwise multiplication, division, matrix inverse, pseudo inverse, eigendecomposition, singular value decomposition...) 6 | - `MultidimensionData` protocol for elegant handling of multidimensional data of any kind 7 | - Clear, compact and powerful syntax for mathematical operations on tensors 8 | - Principal component analysis 9 | - Multilinear subspace learning algorithms for dimensionality reduction 10 | - Linear and logistic regression 11 | - Stochastic gradient descent 12 | - Feedforward neural networks 13 | - Sigmoid, ReLU, Softplus activation functions 14 | - Easy regularizations 15 | 16 | ## Tensor reading, writing, slicing 17 | Create data tensor: 18 | ```swift 19 | var a = Tensor(modeSizes: [3, 3, 3], repeatedValue: 0) 20 | ``` 21 | 22 | Read and write single values: 23 | ```swift 24 | let b: Float = a[1, 2, 0] 25 | a[2, 0, 1] = 3.14 26 | ``` 27 | 28 | Read and write a tensor slice
29 | ```swift 30 | let c: Tensor = a[1..<3, all, [0]] 31 | a[1...1, [0, 2], all] = Tensor(modeSizes: [2, 3], values: [1, 2, 3, 4, 5, 6]) 32 | ``` 33 | *modes of size 1 will be trimmed* 34 | 35 | ## Einstein notation 36 | Modes with same symbolic index will be summed over. Simple matrix multiplication: 37 | ```swift 38 | var m = Tensor(modeSizes: [4, 6], repeatedValue: 1) 39 | var n = Tensor(modeSizes: [6, 5], repeatedValue: 2) 40 | let matrixProduct = m[.i, .j] * n[.j, .k] 41 | ``` 42 | Geodesic deviation: 43 | ```swift 44 | var tangentVector = Tensor(modeSizes: [4], values: [0.3, 1.7, 0.2, 0.5]) 45 | tangentVector.isCartesian = false 46 | 47 | var deviationVector = Tensor(modeSizes: [4], values: [0.1, 0.9, 0.4, 1.2]) 48 | deviationVector.isCartesian = false 49 | 50 | var riemannianTensor = Tensor(diagonalWithModeSizes: [4, 4, 4, 4]) 51 | riemannianTensor.isCartesian = false 52 | riemannianTensor.variances = [.contravariant, .covariant, .covariant, .covariant] 53 | 54 | let relativeAcceleration = riemannianTensor[.μ, .ν, .ρ, .σ] * tangentVector[.ν] * tangentVector[.ρ] * deviationTensor[.σ] 55 | ``` 56 | 57 | ## Neural networks 58 | Setting up and training a simple feedforward neural net: 59 | ```swift 60 | var estimator = NeuralNet(layerSizes: [28*28, 40, 10]) 61 | estimator.layers[0].activationFunction = ReLU(secondarySlope: 0.01) 62 | estimator.layers[1].activationFunction = ReLU(secondarySlope: 0.01) 63 | 64 | var neuralNetCost = SquaredErrorCost(forEstimator: estimator) 65 | 66 | stochasticGradientDescent(neuralNetCost, inputs: trainingData[.a, .b], targets: trainingLabels[.a, .c], updateRate: 0.1, minibatchSize: 50, validationCallback: ({ (epoch, estimator) -> (Bool) in 67 | if(epoch >= 30) {return true} 68 | else {return false} 69 | })) 70 | ``` 71 | [documentation](https://github.com/vincentherrmann/multilinear-math/wiki/Neural-Networks) 72 | 73 | ## Multilinear subspace learning 74 | Extended PCA algorithms to work with tensors with arbitrary mode count 75 | - [multilinear principal component analysis (MPCA)](https://github.com/vincentherrmann/multilinear-math/wiki/Subspace-Learning) 76 | - [uncorrelated multilinear principal component analysis (UMPCA)](https://github.com/vincentherrmann/multilinear-math/wiki/Subspace-Learning)
77 | *(Lu, Plataniotis, Venetsanopoulos)* 78 | 79 | ## Installation 80 | To use this framework in an OSX XCode project: 81 | - clone this repository 82 | - open your project in XCode 83 | - drag and drop MultilinearMath.xcodeproj into the project navigator of your project 84 | - select the .xcodeproj file of your project in the navigator 85 | - go to the "General" tab and add MultilinearMath.framework to the Linked Frameworks and Libraries 86 | - go to the "Build Settings" tab and set "Embedded Content Contains Swift Code" to YES 87 | - import MultilinearMath in your Swift files 88 | 89 | If possible, whole module optimization should be used for very significant performance gains. 90 | 91 | -------------------------------------------------------------------------------- /Playgrounds/WaveletPacketTransform.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | //: Playground - noun: a place where people can play 2 | 3 | import Cocoa 4 | import MultilinearMath 5 | 6 | print("playground started") 7 | 8 | let cReal: [Float] = [-0.00252085552, 0.0188991688, 0.0510309711, -0.0490589067, 0.0589671507, 0.79271543, 1.0953089, 0.32142213, -0.227000564, -0.0872127786, 0.0242141522, 0.0032346386] 9 | //let cReal: [Float] = [0.6830127, 1.1830127, 0.3169873, -0.1830127] 10 | 11 | let wavelet = Wavelet(h0: cReal, f0: cReal.reversed()) 12 | 13 | let xArray = Array(0..<50).map({Float.pi * Float($0) / 50}) 14 | 15 | var filter = FIRFilter(coefficients: wavelet.analysisFilter(for: 2)) 16 | QuickArrayPlot(array: filter.coefficients) 17 | let ft2 = filter.frequencyResponse().response.map({$0.absoluteValue}) 18 | QuickLinesPlot(x: xArray, y: ft2) 19 | 20 | filter = FIRFilter(coefficients: wavelet.analysisFilter(for: 3)) 21 | QuickArrayPlot(array: filter.coefficients) 22 | let ft3 = filter.frequencyResponse().response.map({$0.absoluteValue}) 23 | QuickLinesPlot(x: xArray, y: ft3) 24 | 25 | filter = FIRFilter(coefficients: wavelet.analysisFilter(for: 4)) 26 | QuickArrayPlot(array: filter.coefficients) 27 | let ft4 = filter.frequencyResponse().response.map({$0.absoluteValue}) 28 | QuickLinesPlot(x: xArray, y: ft4) 29 | 30 | filter = FIRFilter(coefficients: wavelet.analysisFilter(for: 5)) 31 | QuickArrayPlot(array: filter.coefficients) 32 | let ft5 = filter.frequencyResponse().response.map({$0.absoluteValue}) 33 | QuickLinesPlot(x: xArray, y: ft5) 34 | 35 | filter = FIRFilter(coefficients: wavelet.analysisFilter(for: 6)) 36 | QuickArrayPlot(array: filter.coefficients) 37 | let ft6 = filter.frequencyResponse().response.map({$0.absoluteValue}) 38 | QuickLinesPlot(x: xArray, y: ft6) 39 | 40 | filter = FIRFilter(coefficients: wavelet.analysisFilter(for: 7)) 41 | QuickArrayPlot(array: filter.coefficients) 42 | let ft7 = filter.frequencyResponse().response.map({$0.absoluteValue}) 43 | QuickLinesPlot(x: xArray, y: ft7) 44 | 45 | filter = FIRFilter(coefficients: wavelet.analysisFilter(for: 8)) 46 | QuickArrayPlot(array: filter.coefficients) 47 | let ft8 = filter.frequencyResponse().response.map({$0.absoluteValue}) 48 | QuickLinesPlot(x: xArray, y: ft8) 49 | filter = FIRFilter(coefficients: wavelet.analysisFilter(for: 9)) 50 | QuickArrayPlot(array: filter.coefficients) 51 | let ft9 = filter.frequencyResponse().response.map({$0.absoluteValue}) 52 | QuickLinesPlot(x: xArray, y: ft9) 53 | 54 | filter = FIRFilter(coefficients: wavelet.analysisFilter(for: 10)) 55 | QuickArrayPlot(array: filter.coefficients) 56 | let ft10 = filter.frequencyResponse().response.map({$0.absoluteValue}) 57 | QuickLinesPlot(x: xArray, y: ft10) 58 | 59 | filter = FIRFilter(coefficients: wavelet.analysisFilter(for: 11)) 60 | QuickArrayPlot(array: filter.coefficients) 61 | let ft11 = filter.frequencyResponse().response.map({$0.absoluteValue}) 62 | QuickLinesPlot(x: xArray, y: ft11) 63 | 64 | filter = FIRFilter(coefficients: wavelet.analysisFilter(for: 12)) 65 | QuickArrayPlot(array: filter.coefficients) 66 | let ft12 = filter.frequencyResponse().response.map({$0.absoluteValue}) 67 | QuickLinesPlot(x: xArray, y: ft12) 68 | 69 | filter = FIRFilter(coefficients: wavelet.analysisFilter(for: 13)) 70 | QuickArrayPlot(array: filter.coefficients) 71 | let ft13 = filter.frequencyResponse().response.map({$0.absoluteValue}) 72 | QuickLinesPlot(x: xArray, y: ft13) 73 | 74 | filter = FIRFilter(coefficients: wavelet.analysisFilter(for: 14)) 75 | QuickArrayPlot(array: filter.coefficients) 76 | let ft14 = filter.frequencyResponse().response.map({$0.absoluteValue}) 77 | QuickLinesPlot(x: xArray, y: ft14) 78 | 79 | filter = FIRFilter(coefficients: wavelet.analysisFilter(for: 15)) 80 | QuickArrayPlot(array: filter.coefficients) 81 | let ft15 = filter.frequencyResponse().response.map({$0.absoluteValue}) 82 | QuickLinesPlot(x: xArray, y: ft15) 83 | 84 | filter = FIRFilter(coefficients: wavelet.analysisFilter(for: 16)) 85 | QuickArrayPlot(array: filter.coefficients) 86 | let ft16 = filter.frequencyResponse().response.map({$0.absoluteValue}) 87 | QuickLinesPlot(x: xArray, y: ft16) 88 | 89 | filter = FIRFilter(coefficients: wavelet.analysisFilter(for: 24)) 90 | QuickArrayPlot(array: filter.coefficients) 91 | let ft24 = filter.frequencyResponse().response.map({$0.absoluteValue}) 92 | QuickLinesPlot(x: xArray, y: ft24) 93 | 94 | filter = FIRFilter(coefficients: wavelet.analysisFilter(for: 56)) 95 | QuickArrayPlot(array: filter.coefficients) 96 | let ft56 = filter.frequencyResponse().response.map({$0.absoluteValue}) 97 | QuickLinesPlot(x: xArray, y: ft56) 98 | -------------------------------------------------------------------------------- /Sources/WaveletPlots.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WaveletPlots.swift 3 | // MultilinearMath 4 | // 5 | // Created by Vincent Herrmann on 07.11.16. 6 | // Copyright © 2016 Vincent Herrmann. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | public struct FastWaveletPlot: CustomPlaygroundQuickLookable { 12 | public var waveletView: WaveletView 13 | 14 | public var customPlaygroundQuickLook: PlaygroundQuickLook { 15 | get { 16 | return PlaygroundQuickLook.view(waveletView) 17 | } 18 | } 19 | 20 | public init(packets: [WaveletPacket]) { 21 | waveletView = WaveletView(frame: NSRect(x: 0, y: 0, width: 300, height: 200)) 22 | var bounds: CGRect? = nil 23 | for i in 0.. maxValue {maxValue = CGFloat(m)} 69 | } 70 | } 71 | } 72 | 73 | super.updatePlotting() 74 | } 75 | } 76 | 77 | public class WaveletPacketPlot: PlottableIn2D { 78 | public var packet: WaveletPacket 79 | var tiles: [CGRect] = [] 80 | var maxValue: CGFloat = 1 81 | public var plotBounds: CGRect { 82 | get { 83 | let yMinP = CGFloat(packet.position) / CGFloat(packet.length) 84 | let height = 1 / CGFloat(packet.length) 85 | let width = CGFloat(packet.length * packet.values.count) 86 | let bounds = CGRect(x: 0, y: yMinP, width: width, height: height) 87 | return bounds 88 | } 89 | } 90 | 91 | public init(packet: WaveletPacket) { 92 | self.packet = packet 93 | } 94 | 95 | public func draw() { 96 | for i in 0..] 13 | var currentInput: Tensor = zeros() 14 | var currentPreactivations: Tensor = zeros() 15 | 16 | fileprivate let example = TensorIndex.a 17 | fileprivate let feature = TensorIndex.b 18 | 19 | 20 | public init(featureCount: Int) { 21 | parameters = [zeros(featureCount), zeros()] 22 | parameters[0].indices = [feature] 23 | } 24 | 25 | open func output(_ input: Tensor) -> Tensor { 26 | if(input.modeCount == 1) { 27 | currentInput = Tensor(modeSizes: [1, input.modeSizes[0]], values: input.values) 28 | currentInput.indices = [example, feature] 29 | } else { 30 | currentInput = input[example, feature] 31 | } 32 | 33 | currentPreactivations = (currentInput * parameters[0]) + parameters[1] 34 | let currentHypothesis = Sigmoid().output(currentPreactivations) 35 | return currentHypothesis 36 | } 37 | 38 | open func gradients(_ gradientWrtOutput: Tensor) -> (wrtInput: Tensor, wrtParameters: [Tensor]) { 39 | let sigmoidGradient = Sigmoid().derivative(currentPreactivations) 40 | let preactivationGradient = sigmoidGradient °* gradientWrtOutput 41 | let parameter0Gradient = preactivationGradient * currentInput 42 | let parameter1Gradient = sum(preactivationGradient, overModes: [0]) 43 | let inputGradient = sum(preactivationGradient * parameters[0], overModes: [0]) 44 | 45 | return (inputGradient, [parameter0Gradient, parameter1Gradient]) 46 | } 47 | 48 | open func updateParameters(_ subtrahends: [Tensor]) { 49 | parameters[0] = parameters[0] - subtrahends[0] 50 | parameters[1] = parameters[1] - subtrahends[1] 51 | } 52 | } 53 | 54 | /// Negative log likelihood cost for logistic regression 55 | open class LogisticRegressionCost: CostFunction { 56 | open var estimator: ParametricTensorFunction 57 | open var regularizers: [ParameterRegularizer?] = [nil, nil] 58 | 59 | public init(featureCount: Int) { 60 | estimator = LogisticRegressionEstimator(featureCount: featureCount) 61 | } 62 | 63 | open func costForEstimate(_ estimate: Tensor, target: Tensor) -> Float { 64 | let exampleCount = Float(target.elementCount) 65 | 66 | let t1 = -target °* log(estimate) 67 | let t2 = (1-target) °* log(1-estimate) 68 | let cost = vectorSummation((t1-t2).values) / exampleCount 69 | 70 | return cost 71 | } 72 | 73 | open func gradientForEstimate(_ estimate: Tensor, target: Tensor) -> Tensor { 74 | if(estimate.indices != target.indices) { 75 | print("abstract indices of estimate and target should be the same!") 76 | } 77 | 78 | let g1 = target °* (1/estimate) 79 | let g2 = (1-target) °* (1 / (1-estimate)) 80 | let gradient = -(g1 - g2) 81 | 82 | return gradient 83 | } 84 | } 85 | 86 | //public func oneVsAllClassification(x x: Tensor, y: Tensor, classCount: Int, regularize: Float = 0) -> Tensor { 87 | // 88 | // let exampleCount = x.modeSizes[0] 89 | // let featureCount = x.modeSizes[1] 90 | // 91 | // var yClasses = [zeros(classCount, exampleCount)] 92 | // for c in 0..(modeSizes: [exampleCount], values: y.values.map({Float($0 == Float(c))})) 94 | // } 95 | // 96 | // var outputData = [zeros(classCount, featureCount+1)] 97 | // 98 | // combine(x, forOuterModes: [], with: yClasses[0], forOuterModes: [0], outputData: &outputData, 99 | // calculate: ({ (indexA, indexB, outerIndex, sourceA, sourceB) -> [Tensor] in 100 | // let result = logisticRegression(x: sourceA, y: sourceB[slice: indexB]) 101 | // return [result] 102 | // }), 103 | // writeOutput: ({ (indexA, indexB, outerIndex, inputData, outputData) in 104 | // outputData[0][slice: outerIndex + [all]] = inputData[0] 105 | // })) 106 | // 107 | // return (outputData[0]) 108 | //} 109 | -------------------------------------------------------------------------------- /Sources/TensorSyntacticOperators.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TensorSyntacticOperators.swift 3 | // MultilinearMath 4 | // 5 | // Created by Vincent Herrmann on 23.04.16. 6 | // Copyright © 2016 Vincent Herrmann. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // MARK: - Scalar-Tensor functions 12 | /// Negative of a tensor 13 | public prefix func -(tensor: Tensor) -> Tensor { 14 | let negative = Tensor(withPropertiesOf: tensor, values: vectorNegation(tensor.values)) 15 | return negative 16 | } 17 | 18 | /// add a scalar to every element of a tensor 19 | public func +(lhs: Tensor, rhs: Float) -> Tensor { 20 | let sum = Tensor.init(withPropertiesOf: lhs, values: vectorAddition(vector: lhs.values, add: rhs)) 21 | return sum 22 | } 23 | /// add a scalar to every element of a tensor 24 | public func +(lhs: Float, rhs: Tensor) -> Tensor { 25 | return rhs+lhs 26 | } 27 | 28 | /// substract a scalar from every element of a tensor 29 | public func -(lhs: Tensor, rhs: Float) -> Tensor { 30 | let diff = Tensor(withPropertiesOf: lhs, values: vectorAddition(vector: lhs.values, add: -rhs)) 31 | return diff 32 | } 33 | 34 | /// substract every element of a tensor from a scalar 35 | public func -(lhs: Float, rhs: Tensor) -> Tensor { 36 | return lhs + (-rhs) 37 | } 38 | 39 | /// multiply every element of a tensor with a scalar 40 | public func *(lhs: Tensor, rhs: Float) -> Tensor { 41 | let product = Tensor.init(withPropertiesOf: lhs, values: vectorMultiplication(lhs.values, factor: rhs)) 42 | return product 43 | } 44 | /// multiply every element of a tensor with a scalar 45 | public func *(lhs: Float, rhs: Tensor) -> Tensor { 46 | return rhs*lhs 47 | } 48 | 49 | // MARK: - Tensor-Tensor functions 50 | public func +(lhs: Tensor, rhs: Tensor) -> Tensor { 51 | 52 | let commonIndices = lhs.commonIndicesWith(rhs) 53 | let commonModesLhs = commonIndices.map({$0.modeA}) 54 | let outerModesLhs = lhs.modeArray.removeValues(commonModesLhs) 55 | let commonModesRhs = commonIndices.map({$0.modeB}) 56 | let outerModesRhs = rhs.modeArray.removeValues(commonModesRhs) 57 | 58 | let sum = add(a: lhs, commonModesA: commonModesLhs, outerModesA: outerModesLhs, b: rhs, commonModesB: commonModesRhs, outerModesB: outerModesRhs) 59 | 60 | return sum 61 | } 62 | 63 | public func -(lhs: Tensor, rhs: Tensor) -> Tensor { 64 | 65 | let commonIndices = lhs.commonIndicesWith(rhs) 66 | let commonModesLhs = commonIndices.map({$0.modeA}) 67 | let outerModesLhs = lhs.modeArray.removeValues(commonModesLhs) 68 | let commonModesRhs = commonIndices.map({$0.modeB}) 69 | let outerModesRhs = rhs.modeArray.removeValues(commonModesRhs) 70 | 71 | let difference = substract(a: lhs, commonModesA: commonModesLhs, outerModesA: outerModesLhs, b: rhs, commonModesB: commonModesRhs, outerModesB: outerModesRhs) 72 | 73 | return difference 74 | } 75 | 76 | infix operator °* 77 | public func °*(lhs: Tensor, rhs: Tensor) -> Tensor { 78 | 79 | let commonIndices = lhs.commonIndicesWith(rhs) 80 | let commonModesLhs = commonIndices.map({$0.modeA}) 81 | let outerModesLhs = lhs.modeArray.removeValues(commonModesLhs) 82 | let commonModesRhs = commonIndices.map({$0.modeB}) 83 | let outerModesRhs = rhs.modeArray.removeValues(commonModesRhs) 84 | 85 | let product = multiplyElementwise(a: lhs, commonModesA: commonModesLhs, outerModesA: outerModesLhs, b: rhs, commonModesB: commonModesRhs, outerModesB: outerModesRhs) 86 | 87 | return product 88 | } 89 | 90 | infix operator °/ 91 | public func °/(lhs: Tensor, rhs: Tensor) -> Tensor { 92 | 93 | let commonIndices = lhs.commonIndicesWith(rhs) 94 | let commonModesLhs = commonIndices.map({$0.modeA}) 95 | let outerModesLhs = lhs.modeArray.removeValues(commonModesLhs) 96 | let commonModesRhs = commonIndices.map({$0.modeB}) 97 | let outerModesRhs = rhs.modeArray.removeValues(commonModesRhs) 98 | 99 | let quotient = divide(a: lhs, commonModesA: commonModesLhs, outerModesA: outerModesLhs, b: rhs, commonModesB: commonModesRhs, outerModesB: outerModesRhs) 100 | 101 | return quotient 102 | } 103 | 104 | /// divide a scalar by every element of a tensor 105 | public func /(lhs: Float, rhs: Tensor) -> Tensor { 106 | let quotient = Tensor.init(withPropertiesOf: rhs, values: vectorDivision(numerator: lhs, vector: rhs.values)) 107 | return quotient 108 | } 109 | -------------------------------------------------------------------------------- /Sources/DaubechiesWavelets.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DaubechiesWavelets.swift 3 | // MultilinearMath 4 | // 5 | // Created by Vincent Herrmann on 12.10.16. 6 | // Copyright © 2016 Vincent Herrmann. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class DaubechiesWavelet: Wavelet { 12 | public init(vanishingMoments: Int) { 13 | let c = calculateDaubechiesCoefficients(vanishingMoments: vanishingMoments) 14 | super.init(h0: c, f0: c.reversed()) 15 | } 16 | } 17 | 18 | public func calculateDaubechiesCoefficients(vanishingMoments: Int) -> [Float] { 19 | 20 | //create Q polynomial 21 | var summedExpansion: [PolynomialTerm] = [(1 + 0*i, 0)] 22 | let qBase: [PolynomialTerm] = [(0.5 + 0*i, 0), (-0.25 + 0*i, 1), (-0.25 + 0*i, -1)] 23 | 24 | for n in 1.. 1 { 52 | roots.append(1 / root) 53 | } else { 54 | roots.append(root) 55 | } 56 | } 57 | 58 | if roots.count >= vanishingMoments-1 { 59 | break 60 | } 61 | } 62 | 63 | //create H0 polynomial 64 | var h0: [PolynomialTerm] = [(2 + 0*i, 0)] 65 | 66 | for _ in 0.. [PolynomialTerm] { 95 | 96 | var expansion: [PolynomialTerm] = [] 97 | 98 | for term1 in factor1 { 99 | for term2 in factor2 { 100 | let coeff = term1.coefficient * term2.coefficient 101 | let pow = term1.power + term2.power 102 | 103 | if let i = expansion.map({$0.power}).index(where: {$0 == pow}) { 104 | expansion[i] = (expansion[i].coefficient + coeff, pow) 105 | } else { 106 | expansion.append((term1.coefficient * term2.coefficient, term1.power + term2.power)) 107 | } 108 | } 109 | } 110 | 111 | expansion.sort(by: {$0.power < $1.power}) 112 | 113 | return expansion 114 | } 115 | 116 | public func binomialCoefficient(_ a: Int, choose b: Int) -> Int { 117 | let num = Array(1...a).reduce(1, {$0*$1}) 118 | let f1 = Array(1...b).reduce(1, {$0*$1}) 119 | let f2 = Array(1...(a-b)).reduce(1, {$0*$1}) 120 | return num/(f1*f2) 121 | } 122 | 123 | public struct ComplexNewtonApproximator { 124 | public var polynomial: ComplexPolynomial 125 | public var threshold: Float = 0.00000001 126 | public var maxIterations: Int = 50 127 | 128 | public init(polynomial: ComplexPolynomial) { 129 | self.polynomial = polynomial 130 | } 131 | 132 | public func findRoot(from: ComplexNumber) -> ComplexNumber? { 133 | var currentPosition = from 134 | var currentValue = polynomial.valueFor(z: currentPosition) 135 | 136 | for _ in 0.. Bool { 55 | if (lhs.real == rhs.real && lhs.imaginary == rhs.imaginary) { 56 | return true 57 | } else { 58 | return false 59 | } 60 | } 61 | 62 | static public func abs(_ x: ComplexNumber) -> ComplexNumber { 63 | return ComplexNumber(real: x.absoluteValue, imaginary: 0) 64 | } 65 | } 66 | 67 | public func +(lhs: ComplexNumber, rhs: ComplexNumber) -> ComplexNumber { 68 | return ComplexNumber(real: lhs.real + rhs.real, imaginary: lhs.imaginary + rhs.imaginary) 69 | } 70 | public func +(lhs: ComplexNumber, rhs: Float) -> ComplexNumber { 71 | return ComplexNumber(real: lhs.real + rhs, imaginary: lhs.imaginary) 72 | } 73 | public func +(lhs: Float, rhs: ComplexNumber) -> ComplexNumber { 74 | return ComplexNumber(real: lhs + rhs.real, imaginary: rhs.imaginary) 75 | } 76 | 77 | public prefix func -(a: ComplexNumber) -> ComplexNumber { 78 | return ComplexNumber(real: -a.real, imaginary: -a.imaginary) 79 | } 80 | public func -(lhs: ComplexNumber, rhs: ComplexNumber) -> ComplexNumber { 81 | return ComplexNumber(real: lhs.real - rhs.real, imaginary: lhs.imaginary - rhs.imaginary) 82 | } 83 | public func -(lhs: ComplexNumber, rhs: Float) -> ComplexNumber { 84 | return ComplexNumber(real: lhs.real - rhs, imaginary: lhs.imaginary) 85 | } 86 | public func -(lhs: Float, rhs: ComplexNumber) -> ComplexNumber { 87 | return ComplexNumber(real: lhs - rhs.real, imaginary: -rhs.imaginary) 88 | } 89 | 90 | public func *(lhs: ComplexNumber, rhs: ComplexNumber) -> ComplexNumber { 91 | let r = lhs.real * rhs.real - lhs.imaginary * rhs.imaginary 92 | let i = lhs.imaginary * rhs.real + lhs.real * rhs.imaginary 93 | return ComplexNumber(real: r, imaginary: i) 94 | } 95 | public func *(lhs: ComplexNumber, rhs: Float) -> ComplexNumber { 96 | return ComplexNumber(real: lhs.real * rhs, imaginary: lhs.imaginary * rhs) 97 | } 98 | public func *(lhs: Float, rhs: ComplexNumber) -> ComplexNumber { 99 | return ComplexNumber(real: lhs * rhs.real, imaginary: lhs * rhs.imaginary) 100 | } 101 | 102 | public func /(lhs: ComplexNumber, rhs: ComplexNumber) -> ComplexNumber { 103 | let denominator = rhs.real * rhs.real + rhs.imaginary * rhs.imaginary 104 | let r = (lhs.real * rhs.real + lhs.imaginary * rhs.imaginary) / denominator 105 | let i = (lhs.imaginary * rhs.real - lhs.real * rhs.imaginary) / denominator 106 | return ComplexNumber(real: r, imaginary: i) 107 | } 108 | public func /(lhs: ComplexNumber, rhs: Float) -> ComplexNumber { 109 | return ComplexNumber(real: lhs.real / rhs, imaginary: lhs.imaginary / rhs) 110 | } 111 | public func /(lhs: Float, rhs: ComplexNumber) -> ComplexNumber { 112 | return ComplexNumber(real: lhs, imaginary: 0) / rhs 113 | } 114 | 115 | public func pow(_ z: ComplexNumber, n: Int) -> ComplexNumber { 116 | var r = 1 + i*0 117 | if n == 0 { 118 | return r 119 | } else if n > 0 { 120 | for _ in 0.. ComplexNumber { 148 | var currentZ: ComplexNumber = ComplexNumber(real: 1, imaginary: 0) 149 | var currentResult: ComplexNumber = ComplexNumber(real: 0, imaginary: 0) 150 | 151 | for c in coefficients { 152 | let s = c * currentZ 153 | currentResult = currentResult + s 154 | currentZ = currentZ * z 155 | } 156 | 157 | return currentResult 158 | } 159 | } 160 | 161 | 162 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /Playgrounds/WaveletPlayground.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | //: Playground - noun: a place where people can play 2 | 3 | import Cocoa 4 | import MultilinearMath 5 | 6 | let db2: [Float] = [1, 1] 7 | let db4: [Float] = [0.6830127, 1.1830127, 0.3169873, -0.1830127] 8 | let db4w: [Float] = [0.6830127, -1.1830127, 0.3169873, 0.1830127] 9 | let db6: [Float] = [0.3326705529509569, 0.8068915093133388, 0.4598775021193313, -0.13501102001039084, -0.08544127388224149, 0.035226291882100656].map({$0 * pow(2, 0.5)}) 10 | let db8: [Float] = [0.23037781330885523, 0.7148465705525415, 0.6308807679295904, -0.02798376941698385, -0.18703481171888114, 0.030841381835986965, 0.032883011666982945, -0.010597401784997278].map({$0 * pow(2, 0.5)}) 11 | 12 | //QuickArrayPlot(array: db2) 13 | let db2Wavelet = scalingFunction(from: db2, levels: 6) 14 | QuickArrayPlot(array: db2Wavelet) 15 | 16 | QuickArrayPlot(array: db4) 17 | let db4Wavelet = scalingFunction(from: db4, levels: 6) 18 | //let db4WaveletP = QuickArrayPlot(array: db4Wavelet) 19 | let db4WaveletY = Array(0.. [ComplexNumber] { 12 | 13 | //create S polynomial 14 | var sPolynomial: [PolynomialTerm] = [(1 + 0*i, 0)] 15 | let sBase: [PolynomialTerm] = [(1 + 0*i, 1), (2 + 0*i, 0), (1 + 0*i, -1)] 16 | 17 | for _ in 0..(modeSizes: [fmSize, fmSize], repeatedValue: 0) 35 | var results = [Float](repeating: 0, count: fmSize) 36 | for r in 0..= sPolynomial.count) {continue} 41 | factorMatrix[r, c] = sPolynomial[index].coefficient.real 42 | } 43 | if n == 0 { 44 | results[r] = 2 45 | } 46 | } 47 | print("factor matrix: \(factorMatrix.values)") 48 | print("results: \(results)") 49 | let rCoefficients = solveLinearEquationSystem(factorMatrix.values, factorMatrixSize: MatrixSize(rows: fmSize, columns: fmSize), results: results, resultsSize: MatrixSize(rows: fmSize, columns: 1)) 50 | print("r: \(rCoefficients)") 51 | 52 | let qPolynomial = ComplexPolynomial(coefficients: rCoefficients.map({$0 + i*0})) 53 | 54 | //find roots 55 | let newton = ComplexNewtonApproximator(polynomial: qPolynomial) 56 | var roots: [ComplexNumber] = [] 57 | 58 | for s in 0..<(10*vanishingMoments) { 59 | let start = cos(2.6 * Float(s)) + sin(2.6 * Float(s))*i 60 | if let root = newton.findRoot(from: start) { 61 | if roots.contains(where: {($0 - root).absoluteValue < 0.001}) { 62 | continue 63 | } else if root.absoluteValue > 1 { 64 | roots.append(1 / root) 65 | } else { 66 | roots.append(root) 67 | } 68 | } 69 | 70 | if roots.count >= vanishingMoments + delayCoefficients - 2 { 71 | break 72 | } 73 | } 74 | 75 | roots.sort(by: {$0.0.absoluteValue < $0.1.absoluteValue}) 76 | for r in rootsOutsideUnitCircle { 77 | if r >= roots.count {continue} 78 | roots[r] = 1 / roots[r] 79 | } 80 | 81 | 82 | print("roots: \(roots)") 83 | 84 | //create H0 polynomial 85 | var h0: [PolynomialTerm] = [(2 + 0*i, 0)] 86 | 87 | for _ in 0.. [Float] { 114 | 115 | if(count == 0) { 116 | return [1] 117 | } 118 | 119 | var currentD: Float = 1 120 | var d: [Float] = [currentD] 121 | 122 | let L = Float(count - 1) 123 | 124 | for n in 0.. ComplexNumber { 138 | // let base = cos(frequency) + i*sin(frequency) 139 | // var e = 1 / base 140 | var r = 0 + i*0 141 | 142 | for n in 0.. (abs: [Float], arg: [Float], xArray: [Float]) { 152 | // let xDist = Float(x.max()! - x.min()!) 153 | // let count = Float(filter.count - 1) 154 | // let m = 0.5*log2(count / xDist) * Float.pi 155 | let m = 2 * x.max()! * Float.pi 156 | // print("fourier m: \(m)") 157 | let omegas = (0.. [Float] { 12 | var wavelet = calculateIntegerWaveletValues(coefficients) 13 | for _ in 0.. [Float] { 20 | var approx: [Float] = [1] 21 | for _ in 0.. [Float] { 28 | print("calculate wavelet function with \(scalingFunction.count) values") 29 | let distance = (scalingFunction.count-1) / (coefficients.count - 1) 30 | print("distance: \(distance)") 31 | var waveletFunction = [Float](repeating: 0, count: scalingFunction.count) 32 | 33 | for t in 0..= scalingFunction.count) {continue} 38 | currentValue += scalingFunction[index] * coefficients[k] 39 | } 40 | waveletFunction[t] = currentValue 41 | } 42 | 43 | return waveletFunction 44 | } 45 | 46 | /// calculate the values of the wavelet function on the integer position from the filter coefficients. This is done by solving a system of linear equations constructed from the dilation equation 47 | public func calculateIntegerWaveletValues(_ coefficients: [Float]) -> [Float] { 48 | let count = coefficients.count 49 | 50 | //calculate factor matrix (left side of the equation system) 51 | //example: db4 coeffients a0, a1, a2, a3 52 | // a0-1 0 0 0 = 0 53 | // a2 a1-1 a0 0 = 0 54 | // 0 a3 a2-1 a1 = 0 55 | // 0 0 0 a3-1 = 0 56 | // 1 1 1 1 = 1 //this last equation is added to each row to get an unambiguous solution 57 | var factorMatrix = Tensor(modeSizes: [count, count], repeatedValue: 0) 58 | for r in 0..= count) {continue} 63 | factorMatrix[r, index] = coefficients[c] 64 | } 65 | factorMatrix[r, r] += -1 66 | } 67 | factorMatrix = factorMatrix + 1 68 | let results = [Float](repeating: 1, count: count-1) + [1] 69 | 70 | print("count: \(count)") 71 | print("factor matrix: \(factorMatrix.values)") 72 | print("results: \(results)") 73 | let solution = solveLinearEquationSystem(factorMatrix.values, factorMatrixSize: MatrixSize(rows: count, columns: count), results: results, resultsSize: MatrixSize(rows: count, columns: 1)) 74 | 75 | let testResults = matrixMultiplication(matrixA: factorMatrix.values, sizeA: MatrixSize(rows: count, columns: count), matrixB: solution, sizeB: MatrixSize(rows: count, columns: 1)) 76 | print("test result: \(testResults)") 77 | 78 | return solution 79 | } 80 | 81 | public func coefficientsFromScalingFunction(values: [Float], count: Int) -> [Float] { 82 | let halfT = ((values.count-1) / (count-1)) / 2 83 | 84 | var factorMatrix = Tensor(modeSizes: [count, count], repeatedValue: 0) 85 | var results = [Float](repeating: 0, count: count) 86 | for r in 0..= values.count) {continue} 92 | factorMatrix[r, c] = values[index] 93 | } 94 | } 95 | 96 | let solution = solveLinearEquationSystem(factorMatrix.values, factorMatrixSize: MatrixSize(rows: count, columns: count), results: results, resultsSize: MatrixSize(rows: count, columns: 1)) 97 | 98 | return solution 99 | } 100 | 101 | public func newWaveletApproximation(_ currentApproximation: [Float], coefficients: [Float]) -> [Float] { 102 | let newLevel = 2 * (currentApproximation.count-1) / (coefficients.count-1) 103 | var newApproximation = [Float](repeating: 0, count: (coefficients.count-1) * newLevel + 1) 104 | let newValueCount = (coefficients.count-1) * (newLevel/2) 105 | print("new level: \(newLevel), value count: \(newApproximation.count), new values: \(newValueCount)") 106 | 107 | for t in 0..= newApproximation.count) {continue} 116 | value += coefficients[c] * newApproximation[index] 117 | } 118 | newApproximation[2*n+1] = value 119 | } 120 | return newApproximation 121 | } 122 | 123 | ///apprimate filter using the cascading algorithm 124 | public func newFilterApproximation(_ currentApproximation: [Float], coefficients: [Float]) -> [Float] { 125 | let newValueCount = 2*currentApproximation.count + coefficients.count - 2 126 | var newApproximation = [Float](repeating: 0, count: newValueCount) 127 | 128 | for j in 0..= 0 && index < currentApproximation.count) { 134 | currentValue += coefficients[k] * currentApproximation[index] 135 | } 136 | 137 | } 138 | } 139 | newApproximation[j] = currentValue 140 | } 141 | 142 | return newApproximation 143 | } 144 | -------------------------------------------------------------------------------- /MultilinearMathTests/WaveletTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WaveletTests.swift 3 | // MultilinearMath 4 | // 5 | // Created by Vincent Herrmann on 14.08.16. 6 | // Copyright © 2016 Vincent Herrmann. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import MultilinearMath 11 | 12 | class WaveletTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testDB4() { 30 | let db4: [Float] = [0.48296291314469025, 0.836516303737469, 0.22414386804185735, -0.12940952255092145].map({$0 * pow(2, 0.5)}) 31 | //let db4: [Float] = [0.6830127, 1.1830127, 0.3169873, -0.1830127] 32 | let db4Wavelet = scalingFunction(from: db4, levels: 0) 33 | print("db4: \(db4Wavelet)") 34 | } 35 | 36 | func testDB6() { 37 | //let db6: [Float] = [0.035226291882100656, -0.08544127388224149, -0.13501102001039084, 0.4598775021193313, 0.8068915093133388, 0.3326705529509569] 38 | let db6: [Float] = [0.3326705529509569, 0.8068915093133388, 0.4598775021193313, -0.13501102001039084, -0.08544127388224149, 0.035226291882100656].map({$0 * pow(2, 0.5)}) 39 | 40 | 41 | //let db6: [Float] = [0.47046721, 1.14111692, 0.650365, -0.19093442, -0.12083221, 0.0498175] 42 | let db6Wavelet = scalingFunction(from: db6, levels: 0) 43 | print("db6: \(db6Wavelet)") 44 | } 45 | 46 | func testDB8() { 47 | 48 | let db8: [Float] = [0.23037781330885523, 0.7148465705525415, 0.6308807679295904, -0.02798376941698385, -0.18703481171888114, 0.030841381835986965, 0.032883011666982945, -0.010597401784997278].map({$0 * pow(2, 0.5)}) 49 | 50 | 51 | //let db8: [Float] = [1, 0, -2, 3, 4, 1, 1, -3] 52 | let db8Wavelet = scalingFunction(from: db8, levels: 0) 53 | print("db8: \(db8Wavelet)") 54 | } 55 | 56 | // func testFrequencyResponse() { 57 | // let db4: [Float] = [0.48296291314469025, 0.836516303737469, 0.22414386804185735, -0.12940952255092145].map({$0 * pow(2, 0.5)}) 58 | // let spectrum = FIRFilter(coefficients: db4) 59 | // let v = Array(0..<99).map({(Float($0)/100)}) 60 | // let fr = v.map({spectrum.frequencyResponse(6.28*$0 - 3.14)}) 61 | // let impulseResponse = fr.map({$0.r * $0.i}) 62 | // print("ir: \(impulseResponse)") 63 | // } 64 | 65 | func testDaubechiesCoefficients() { 66 | let db6 = calculateDaubechiesCoefficients(vanishingMoments: 3) 67 | let db6Tensor = Tensor(modeSizes: [6], values: db6) 68 | let target = Tensor(modeSizes: [6], values: [0.049817, -0.12083, -0.19093, 0.65037, 1.1411, 0.47047]) 69 | 70 | let s = sum(db6Tensor[.a] - target[.a], overModes: [0]) 71 | 72 | XCTAssert(abs(s.values[0]) < 0.01, "wrong daubechies 6 coefficients: \(db6)") 73 | 74 | print("daubechies 6 coefficients: \(db6)") 75 | } 76 | 77 | func testFlatDelayAllpass() { 78 | let d3 = flatDelayCoefficients(count: 3, delay: 0.5) 79 | print("d3: \(d3)") 80 | 81 | let d4 = flatDelayCoefficients(count: 4, delay: 0.5) 82 | print("d4: \(d4)") 83 | } 84 | 85 | func testComplexWavelet() { 86 | let cA4L2 = calculateComplexWaveletCoefficients(vanishingMoments: 4, delayCoefficients: 3, rootsOutsideUnitCircle: [1, 2]) 87 | let real = cA4L2.map({$0.real}) 88 | let imag = cA4L2.map({$0.imaginary}) 89 | 90 | print("cA4L2midPhase real: \(real)") 91 | print("cA4L2midPhase imaginary: \(imag)") 92 | 93 | let scaling = scalingFunction(from: real, levels: 6) 94 | let reconstructedReal = coefficientsFromScalingFunction(values: scaling, count: 12) 95 | print("reconstructed real: \(reconstructedReal)") 96 | 97 | 98 | } 99 | 100 | func testForwardFWT() { 101 | let db4 = DaubechiesWavelet(vanishingMoments: 2) 102 | let signal = Array(0..<128).map({Float($0)/10}).map({sin(3*$0)}) 103 | var currentSignal = signal 104 | var analysis: [[Float]] = [] 105 | 106 | var a: (r0: [Float], r1: [Float]) = ([], []) 107 | for _ in 0..<4 { 108 | a = waveletTransformForwardStep(signal: currentSignal, h0: db4.h0, h1: db4.h1) 109 | currentSignal = a.r0 110 | 111 | analysis.append(a.r1) 112 | } 113 | 114 | analysis.append(a.r0) 115 | } 116 | 117 | func testPacketCode() { 118 | var c = WaveletPacket(values: [1, 2, 3, 4, 5, 6, 7], levels: [.scaling, .scaling, .wavelet]) 119 | print("code: \(c.code), levels: \(c.levels), level: \(c.level), length: \(c.length)") 120 | 121 | c.addLevel(.scaling) 122 | print("code: \(c.code), levels: \(c.levels), level: \(c.level), length: \(c.length)") 123 | 124 | c.addLevel(.wavelet) 125 | print("code: \(c.code), levels: \(c.levels), level: \(c.level), length: \(c.length)") 126 | 127 | } 128 | 129 | func testPacketPlot() { 130 | let p1 = WaveletPacket(values: [0, 1, 2, 1], levels: [.wavelet]) 131 | let p2 = WaveletPacket(values: [2, 0], levels: [.scaling, .wavelet]) 132 | let p3 = WaveletPacket(values: [0, 1], levels: [.scaling, .scaling]) 133 | 134 | let plot = FastWaveletPlot(packets: [p1, p2, p3]) 135 | 136 | } 137 | 138 | func testPacketTransform() { 139 | let h0: [Float] = [0.6830127, 1.1830127, 0.3169873, -0.1830127] 140 | let w = Wavelet(h0: h0, f0: h0.reversed()) 141 | 142 | let count: Int = 1024 143 | let length: Float = 30 144 | let xArray = Array(0..]) -> [Tensor] 17 | } 18 | 19 | protocol TensorFourierTransform { 20 | static var transformSetup: (vDSP_DFT_Setup?, vDSP_Length) -> OpaquePointer {get} 21 | static var transformFunction: (OpaquePointer, UnsafePointer, UnsafePointer, UnsafeMutablePointer, UnsafeMutablePointer) -> () {get} 22 | var modeSizes: [Int] {get} 23 | var transformSizes: [Int]! {get set} 24 | var dftSetups: [OpaquePointer] {get set} 25 | } 26 | extension TensorFourierTransform { 27 | mutating func setupTransform() { 28 | /// next biggest integer base 2 logarithms of the mode sizes 29 | let logSizes = modeSizes.map{Int(ceil(log2(Float($0))))} 30 | transformSizes = logSizes.map{Int(pow(2, Float($0)))} 31 | print("transform sizes: \(transformSizes)") 32 | 33 | //create dft setup for each mode 34 | dftSetups = [] 35 | var currentSetup: OpaquePointer? = nil 36 | for size in transformSizes { 37 | currentSetup = Self.transformSetup(currentSetup, UInt(size)) 38 | dftSetups.append(currentSetup!) 39 | } 40 | } 41 | 42 | func performFourierTransform(_ input: Tensor) -> Tensor { 43 | var inputTensor = input 44 | print("input tensor sizes: \(inputTensor.modeSizes)") 45 | var outputTensor = [Tensor(withPropertiesOf: inputTensor)] 46 | 47 | for mode in 0.. [Tensor] in 59 | let real = sourceData[slice: currentIndex][0...0, all].values 60 | let imag = sourceData[slice: currentIndex][1...1, all].values 61 | 62 | print("index \(currentIndex) real vector: \(real)") 63 | print("index \(currentIndex) imag vector: \(imag)") 64 | 65 | var realOutValues = [Float](repeating: 0, count: vectorSize) 66 | var imagOutValues = [Float](repeating: 0, count: vectorSize) 67 | 68 | Self.transformFunction(self.dftSetups[mode], real, imag, &realOutValues, &imagOutValues) 69 | 70 | let outputValues = realOutValues + imagOutValues 71 | let output = Tensor(modeSizes: [2, vectorSize], values: outputValues) 72 | // print("index \(currentIndex) real output: \(realOutValues)") 73 | // print("index \(currentIndex) imag output: \(imagOutValues)") 74 | 75 | return [output] 76 | }, 77 | writeOutput: { (currentIndex, outerIndex, inputData, outputData) in 78 | outputData[0][slice: currentIndex] = inputData[0] 79 | }) 80 | 81 | inputTensor = outputTensor[0] 82 | } 83 | 84 | return outputTensor[0] 85 | } 86 | } 87 | 88 | public struct FourierTransform: TensorFourierTransform, TensorOperationNode { 89 | public static var inputCount = 1 90 | public static var outputCount = 1 91 | 92 | static var transformSetup: (vDSP_DFT_Setup?, vDSP_Length) -> OpaquePointer = {(prevSetup, length) -> OpaquePointer in 93 | return vDSP_DFT_zop_CreateSetup(prevSetup, length, vDSP_DFT_Direction.FORWARD)! 94 | } 95 | static var transformFunction = {(setup, realIn, imagIn, realOut, imagOut) -> () in 96 | vDSP_DFT_Execute(setup, realIn, imagIn, realOut, imagOut) 97 | } 98 | 99 | var modeSizes: [Int] 100 | var transformSizes: [Int]! 101 | var dftSetups: [vDSP_DFT_Setup] = [] 102 | 103 | public init(modeSizes: [Int]) { 104 | self.modeSizes = modeSizes 105 | setup() 106 | } 107 | 108 | mutating public func setup() { 109 | setupTransform() 110 | } 111 | 112 | public func execute(_ input: [Tensor]) -> [Tensor] { 113 | print("transform sizes: \(transformSizes)") 114 | let paddedInput = changeModeSizes(input[0], targetSizes: [2] + transformSizes) 115 | print("padded input sizes: \(paddedInput.modeSizes)") 116 | let output = performFourierTransform(paddedInput) 117 | return [output] 118 | } 119 | } 120 | 121 | public struct InverseFourierTransform: TensorFourierTransform, TensorOperationNode { 122 | public static var inputCount = 1 123 | public static var outputCount = 1 124 | 125 | static var transformSetup: (vDSP_DFT_Setup?, vDSP_Length) -> OpaquePointer = {(prevSetup, length) -> OpaquePointer in 126 | return vDSP_DFT_zop_CreateSetup(prevSetup, length, vDSP_DFT_Direction.INVERSE)! 127 | } 128 | static var transformFunction = {(setup, realIn, imagIn, realOut, imagOut) -> () in 129 | vDSP_DFT_Execute(setup, realIn, imagIn, realOut, imagOut) 130 | } 131 | 132 | var modeSizes: [Int] 133 | var transformSizes: [Int]! 134 | var dftSetups: [vDSP_DFT_Setup] = [] 135 | 136 | public init(modeSizes: [Int]) { 137 | self.modeSizes = modeSizes 138 | setup() 139 | } 140 | 141 | mutating public func setup() { 142 | setupTransform() 143 | } 144 | 145 | public func execute(_ input: [Tensor]) -> [Tensor] { 146 | let factor = 1 / Float(transformSizes.reduce(1, {$0*$1})) 147 | let output = performFourierTransform(input[0]) * factor 148 | let scaledOutput = changeModeSizes(output, targetSizes: [2] + modeSizes) 149 | return [scaledOutput] 150 | } 151 | } 152 | 153 | 154 | -------------------------------------------------------------------------------- /Sources/MPCA.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MPCA.swift 3 | // MultilinearMath 4 | // 5 | // Created by Vincent Herrmann on 27.03.16. 6 | // Copyright © 2016 Vincent Herrmann. All rights reserved. 7 | // 8 | 9 | //paper: H. Lu, K. N. Plataniotis, and A. N. Venetsanopoulos "MPCA: Multilinear Principal Component Analysis of Tensor Objects" July 2012 10 | 11 | import Foundation 12 | 13 | /// Multilinear Principal Component Analysis. Performs feature extraction by projecting a collection of samples with arbitrary mode count and sizes to a subspace while capturing most of the original variance. 14 | /// 15 | /// - Parameter data: Sample tensor. The first mode enumerates the different samples. The remaining modes constitute the specific sample. All sample elements should be centered, i.e. have mean 0. 16 | /// - Parameter projectionModeSizes: The mode sizes of the projected sample. Every mode has to be smaller of the as the corresponding mode in the original samples. `projectionModeSizes.count` = `data.modeCount-1` 17 | /// 18 | /// - Returns: 19 | /// `projectedData`:
The data projected to a subspace with the given mode sizes.
20 | /// `projectionMatrices`:
One matrix for each sample mode 21 | public func multilinearPCA(_ inputData: Tensor, projectionModeSizes: [Int]) -> (projectedData: Tensor, projectionMatrices: [Tensor]) { 22 | 23 | //Initialization 24 | let data = inputData.uniquelyIndexed() 25 | let sampleModeCount = data.modeCount - 1 26 | let maxLoops = sampleModeCount == 1 ? 1 : 20 //the simple PCA (sampleModeCount = 1) has a closed form solution 27 | let projectionDifferenceThreshold: Float = 0.001 28 | 29 | var projectionMatrices: [Tensor] = [] // U_n 30 | let projectionModeIndices = TensorIndex.uniqueIndexArray(sampleModeCount, excludedIndices: data.indices) //unique indices for projection matrices 31 | for n in 0..(diagonalWithModeSizes: modeSizes, repeatedValue: 1.0) 35 | thisProjectionMatrix.indices = [projectionModeIndices[n], data.indices[n+1]] 36 | projectionMatrices.append(thisProjectionMatrix) 37 | 38 | } 39 | var projectedData: Tensor = multilinearPCAProjection(data: data, projectionMatrices: projectionMatrices) 40 | var projectionScatter: Float = 0 41 | var newScatter = (projectedData * projectedData).values[0] 42 | print("multilinearPCA") 43 | print("initial projectionScatter: \(newScatter)") 44 | 45 | //Local Optimization 46 | for _ in 0.., oldProjectionMatrices: [Tensor]) -> [Tensor] { 65 | 66 | var newProjectionMatrices: [Tensor] = [] 67 | 68 | //construct a new projectionMatrix for each mode 69 | for n in 0..(withPropertiesOf: oldProjectionMatrices[n], values: newValues) 85 | newProjectionMatrices.append(thisProjectionMatrix) 86 | } 87 | 88 | return newProjectionMatrices 89 | } 90 | 91 | /// Project the data tensor to a space with different dimensionality via the given projection matrices. 92 | /// 93 | /// - Parameter data: Sample tensor. The first mode enumerates the different samples. The remaining modes constitute the specific sample. 94 | /// - Parameter projectionMatrices: One projection matrix for each sample mode (either the MPCA projection matrices or the reconstruction matrices) 95 | /// - Parameter doNotProjectModes: The projection of these modes will be skipped. Although the content does not matter, a projection matrix is required nonetheless for these modes. The default value is an empty array. 96 | /// 97 | /// - Returns: The data projected with the given projection matrices. 98 | public func multilinearPCAProjection(data: Tensor, projectionMatrices: [Tensor], doNotProjectModes: [Int] = []) -> Tensor { 99 | 100 | var currentData = data 101 | for n in 0..]) -> [Tensor] { 121 | var reconstructionMatrices: [Tensor] = [] 122 | for thisMatrix in projectionMatrices { 123 | let size = MatrixSize(rows: thisMatrix.modeSizes[0], columns: thisMatrix.modeSizes[1]) 124 | let values = pseudoInverse(thisMatrix.values, size: size) 125 | var thisReconstructionMatrix = Tensor(modeSizes: [size.columns, size.rows], values: values) 126 | thisReconstructionMatrix.indices = thisMatrix.indices.reversed() 127 | reconstructionMatrices.append(thisReconstructionMatrix) 128 | } 129 | return reconstructionMatrices 130 | } 131 | -------------------------------------------------------------------------------- /Sources/ParametricFunctions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ParametricFunctions.swift 3 | // MultilinearMath 4 | // 5 | // Created by Vincent Herrmann on 19.06.16. 6 | // Copyright © 2016 Vincent Herrmann. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol ParametricTensorFunction { 12 | var parameters: [Tensor] {get set} 13 | 14 | func output(_ input: Tensor) -> Tensor 15 | func gradients(_ gradientWrtOutput: Tensor) -> (wrtInput: Tensor, wrtParameters: [Tensor]) 16 | /// Substract the given values from the parameters. For gradient descent optimization algorithms 17 | mutating func updateParameters(_ subtrahends: [Tensor]) 18 | } 19 | 20 | public protocol CostFunction: class { 21 | var estimator: ParametricTensorFunction {get set} 22 | var regularizers: [ParameterRegularizer?] {get} 23 | 24 | func costForEstimate(_ estimate: Tensor, target: Tensor) -> Float 25 | func gradientForEstimate(_ estimate: Tensor, target: Tensor) -> Tensor 26 | } 27 | 28 | public extension CostFunction { 29 | func updateParameters(_ subtrahends: [Tensor]) { 30 | let parameterRegularizations = estimator.parameters.combineWith(regularizers, combineFunction: { (parameter, regularizer) -> Tensor in 31 | if let r = regularizer { 32 | return r.regularizationGradient(parameter) 33 | } else { 34 | return Tensor(withPropertiesOf: parameter, repeatedValue: 0) 35 | } 36 | }) 37 | 38 | estimator.updateParameters(subtrahends.combineWith(parameterRegularizations, combineFunction: {add(a: $0, outerModesA: [], b: $1, outerModesB: [])})) 39 | } 40 | 41 | func regularizedCostForEstimate(_ estimate: Tensor, target: Tensor) -> Float { 42 | let cost = self.costForEstimate(estimate, target: target) 43 | 44 | let regularizationCosts = estimator.parameters.combineWith(regularizers, combineFunction: { (parameter, regularizer) -> Float in 45 | return (regularizer != nil) ? regularizer!.regularizationCost(parameter) : 0 46 | }) 47 | 48 | return cost + regularizationCosts.reduce(0, {$0 + $1}) 49 | } 50 | 51 | 52 | public func numericalGradients(_ input: Tensor, target: Tensor, epsilon: Float = 0.01) -> [Tensor] { 53 | let estimate = estimator.output(input) 54 | let cost = costForEstimate(estimate, target: target) 55 | 56 | //check gradients 57 | var numericalGradients: [Tensor] = estimator.parameters 58 | 59 | for p in 0..) -> Float 92 | func regularizationGradient(_ parameter: Tensor) -> Tensor 93 | } 94 | 95 | public struct ParameterDecay: ParameterRegularizer { 96 | public var regularizationParameter: Float 97 | 98 | public init(decayRate: Float) { 99 | regularizationParameter = decayRate 100 | } 101 | 102 | public func regularizationCost(_ parameter: Tensor) -> Float { 103 | return multiply(a: parameter, remainingModesA: [], b: parameter, remainingModesB: []).values[0] * regularizationParameter 104 | } 105 | public func regularizationGradient(_ parameter: Tensor) -> Tensor { 106 | return parameter * regularizationParameter 107 | } 108 | } 109 | 110 | open class SquaredErrorCost: CostFunction { 111 | open var estimator: ParametricTensorFunction 112 | open var regularizers: [ParameterRegularizer?] 113 | 114 | public init(forEstimator: ParametricTensorFunction) { 115 | estimator = forEstimator 116 | regularizers = [ParameterRegularizer?](repeating: nil, count: estimator.parameters.count) 117 | } 118 | 119 | open func costForEstimate(_ estimate: Tensor, target: Tensor) -> Float { 120 | let exampleCount = Float(target.modeCount > 1 ? target.modeSizes[0] : 1) 121 | 122 | let error = substract(a: target, outerModesA: [], b: estimate, outerModesB: []) 123 | let cost = multiply(a: error, remainingModesA: [], b: error, remainingModesB: []) 124 | let scaledCost = cost.values[0] / exampleCount 125 | 126 | return cost.values[0] / exampleCount 127 | } 128 | 129 | open func gradientForEstimate(_ estimate: Tensor, target: Tensor) -> Tensor { 130 | let gradient = 2 * substract(a: estimate, outerModesA: [], b: target, outerModesB: []) 131 | return gradient 132 | } 133 | } 134 | 135 | open class NegLogClassificationCost: CostFunction { 136 | open var estimator: ParametricTensorFunction 137 | open var regularizers: [ParameterRegularizer?] { 138 | get { 139 | return [ParameterRegularizer?](repeating: nil, count: estimator.parameters.count) 140 | } 141 | } 142 | 143 | public init(forEstimator: ParametricTensorFunction) { 144 | estimator = forEstimator 145 | } 146 | 147 | open func costForEstimate(_ estimate: Tensor, target: Tensor) -> Float { 148 | let exampleCount = Float(target.modeCount > 1 ? target.modeSizes[0] : 1) 149 | 150 | let t1 = -target °* log(estimate) 151 | let t2 = (1-target) °* log(1-estimate) 152 | let cost = vectorSummation((t1-t2).values) / exampleCount 153 | 154 | return cost 155 | } 156 | 157 | open func gradientForEstimate(_ estimate: Tensor, target: Tensor) -> Tensor { 158 | let g1 = target °* (1/estimate) 159 | let g2 = (1-target) °* (1 / (1-estimate)) 160 | let gradient = -(g1 - g2) 161 | 162 | return gradient 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /.build/debug/MultilinearMath.build/output-file-map.json: -------------------------------------------------------------------------------- 1 | { 2 | "": { 3 | "swift-dependencies": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/master.swiftdeps" 4 | }, 5 | "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/Sources/AccelerateFunctionsFloat.swift": { 6 | "dependencies": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/AccelerateFunctionsFloat.d", 7 | "object": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/AccelerateFunctionsFloat.swift.o", 8 | "swiftmodule": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/AccelerateFunctionsFloat~partial.swiftmodule", 9 | "swift-dependencies": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/AccelerateFunctionsFloat.swiftdeps" 10 | }, 11 | "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/Sources/main.swift": { 12 | "dependencies": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/main.d", 13 | "object": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/main.swift.o", 14 | "swiftmodule": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/main~partial.swiftmodule", 15 | "swift-dependencies": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/main.swiftdeps" 16 | }, 17 | "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/Sources/MPCA.swift": { 18 | "dependencies": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/MPCA.d", 19 | "object": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/MPCA.swift.o", 20 | "swiftmodule": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/MPCA~partial.swiftmodule", 21 | "swift-dependencies": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/MPCA.swiftdeps" 22 | }, 23 | "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/Sources/MultidimensionalData.swift": { 24 | "dependencies": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/MultidimensionalData.d", 25 | "object": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/MultidimensionalData.swift.o", 26 | "swiftmodule": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/MultidimensionalData~partial.swiftmodule", 27 | "swift-dependencies": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/MultidimensionalData.swiftdeps" 28 | }, 29 | "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/Sources/SlicingSubscripts.swift": { 30 | "dependencies": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/SlicingSubscripts.d", 31 | "object": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/SlicingSubscripts.swift.o", 32 | "swiftmodule": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/SlicingSubscripts~partial.swiftmodule", 33 | "swift-dependencies": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/SlicingSubscripts.swiftdeps" 34 | }, 35 | "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/Sources/TensorMultiplication.swift": { 36 | "dependencies": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/TensorMultiplication.d", 37 | "object": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/TensorMultiplication.swift.o", 38 | "swiftmodule": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/TensorMultiplication~partial.swiftmodule", 39 | "swift-dependencies": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/TensorMultiplication.swiftdeps" 40 | }, 41 | "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/Sources/TensorOperations.swift": { 42 | "dependencies": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/TensorOperations.d", 43 | "object": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/TensorOperations.swift.o", 44 | "swiftmodule": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/TensorOperations~partial.swiftmodule", 45 | "swift-dependencies": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/TensorOperations.swiftdeps" 46 | }, 47 | "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/Sources/Tensors.swift": { 48 | "dependencies": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/Tensors.d", 49 | "object": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/Tensors.swift.o", 50 | "swiftmodule": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/Tensors~partial.swiftmodule", 51 | "swift-dependencies": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/Tensors.swiftdeps" 52 | }, 53 | "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/Sources/UMPCA.swift": { 54 | "dependencies": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/UMPCA.d", 55 | "object": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/UMPCA.swift.o", 56 | "swiftmodule": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/UMPCA~partial.swiftmodule", 57 | "swift-dependencies": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/UMPCA.swiftdeps" 58 | }, 59 | "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/Sources/Utilities.swift": { 60 | "dependencies": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/Utilities.d", 61 | "object": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/Utilities.swift.o", 62 | "swiftmodule": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/Utilities~partial.swiftmodule", 63 | "swift-dependencies": "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/Utilities.swiftdeps" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Sources/Utilities.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Utilities.swift 3 | // MultilinearMath 4 | // 5 | // Created by Vincent Herrmann on 27.03.16. 6 | // Copyright © 2016 Vincent Herrmann. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Accelerate 11 | 12 | public protocol Number: Comparable { 13 | init(_ value: Int) 14 | init(_ value: Float) 15 | init(_ value: Double) 16 | 17 | static func + (lhs: Self, rhs: Self) -> Self 18 | static func - (lhs: Self, rhs: Self) -> Self 19 | static func * (lhs: Self, rhs: Self) -> Self 20 | static func / (lhs: Self, rhs: Self) -> Self 21 | } 22 | 23 | extension Double : Number {} 24 | extension Float : Number {} 25 | extension Int : Number {} 26 | extension Int64 : Number {} 27 | 28 | public protocol IntegerType { 29 | var value: Int {get} 30 | } 31 | extension Int: IntegerType { 32 | public var value: Int { 33 | get{ 34 | return self 35 | } 36 | } 37 | } 38 | 39 | // credit for the UnsafeBuffer protocol to Chris Liscio https://github.com/liscio 40 | /// A CollectionType that can perfom functions on its Unsafe(Mutable)BufferPointer 41 | public protocol UnsafeBuffer: RandomAccessCollection { 42 | typealias IndexDistance = Int 43 | 44 | /// Perform the a function with a UnsafeBufferPointer to this collection 45 | func performWithUnsafeBufferPointer(_ body: (UnsafeBufferPointer) throws -> R) -> R? 46 | } 47 | 48 | public protocol UnsafeMutableBuffer: UnsafeBuffer, RandomAccessCollection { 49 | typealias IndexDistance = Int 50 | 51 | /// Perform the a function with a UnsafeMutableBufferPointer to this collection 52 | mutating func performWithUnsafeMutableBufferPointer(_ body: (inout UnsafeMutableBufferPointer) throws -> R) -> R? 53 | } 54 | 55 | 56 | extension Array : UnsafeMutableBuffer { 57 | public func performWithUnsafeBufferPointer(_ body: (UnsafeBufferPointer) throws -> R) -> R? { 58 | let value = try? withUnsafeBufferPointer(body) 59 | return value 60 | } 61 | 62 | mutating public func performWithUnsafeMutableBufferPointer(_ body: (inout UnsafeMutableBufferPointer) throws -> R) -> R? { 63 | 64 | let value = try? withUnsafeMutableBufferPointer(body) 65 | return value 66 | } 67 | } 68 | extension ArraySlice : UnsafeMutableBuffer { 69 | public func performWithUnsafeBufferPointer(_ body: (UnsafeBufferPointer) throws -> R) -> R? { 70 | let value = try? withUnsafeBufferPointer(body) 71 | return value 72 | } 73 | 74 | mutating public func performWithUnsafeMutableBufferPointer(_ body: (inout UnsafeMutableBufferPointer) throws -> R) -> R? { 75 | let value = try? withUnsafeMutableBufferPointer(body) 76 | return value 77 | } 78 | } 79 | extension UnsafeBufferPointer: UnsafeBuffer { 80 | public func performWithUnsafeBufferPointer(_ body: (UnsafeBufferPointer) throws -> R) -> R? { 81 | let value = try? body(self) 82 | return value 83 | } 84 | } 85 | extension UnsafeMutableBufferPointer: UnsafeMutableBuffer { 86 | public func performWithUnsafeBufferPointer(_ body: (UnsafeBufferPointer) throws -> R) -> R? { 87 | let thisPointer = UnsafeBufferPointer(start: baseAddress, count: count) 88 | let value = try? body(thisPointer) 89 | return value 90 | } 91 | public func performWithUnsafeMutableBufferPointer(_ body: (inout UnsafeMutableBufferPointer) throws -> R) -> R? { 92 | var thisPointer = self 93 | let value = try? body(&thisPointer) 94 | return value 95 | } 96 | } 97 | 98 | extension Collection { 99 | /// Return a copy of `self` with its elements shuffled 100 | func shuffle() -> [Iterator.Element] { 101 | var list = Array(self) 102 | list.shuffleInPlace() 103 | return list 104 | } 105 | } 106 | 107 | extension MutableCollection where IndexDistance == Int, Index == Int { 108 | /// Shuffle the elements of `self` in-place. 109 | mutating func shuffleInPlace() { 110 | // empty and single-element collections don't shuffle 111 | if count < 2 { return } 112 | 113 | for i in 0.. (_ arrayA: [A], arrayB: [B], combineFunction: (_ a: A, _ b: B) -> R) -> [R] { 124 | let length = min(arrayA.count, arrayB.count) 125 | var result = [R]() 126 | for i in 0..(_ array: [E], combineFunction: (_ t: Element, _ e: E) -> R) -> [R] { 134 | return arrayCombine(self, arrayB: array, combineFunction: combineFunction) 135 | } 136 | 137 | /// Safe access to array elements 138 | subscript (safe index: Int) -> Element? { 139 | return indices.contains(index) ? self[index] : nil 140 | } 141 | } 142 | 143 | public extension UnsafeBuffer where Iterator.Element: Equatable, Index == Int { 144 | ///remove the given values from this array 145 | func removeValues(_ values: [Iterator.Element]) -> [Iterator.Element] { 146 | var returnArray = Array(self) 147 | for value in values { 148 | if let index = returnArray.index(of: value) { 149 | returnArray.remove(at: index) 150 | 151 | } 152 | } 153 | return returnArray 154 | } 155 | } 156 | 157 | //public func squaredDistance(a: [Float], b: [Float]) -> Float { 158 | // return a.combineWith(b, combineFunction: {($0-$1)*($0-$1)}).reduce(0, combine: {$0+$1}) 159 | //} 160 | 161 | public func meanSquaredError(target: [Float], result: [Float]) -> Float { 162 | assert(target.count == result.count) 163 | let factor = 1 / Float(target.count) 164 | let errors = zip(target, result).map({pow($0.0 - $0.1, 2)}) 165 | return errors.reduce(0, {$0 + $1}) * factor 166 | } 167 | 168 | public func memoryAddress(_ o: UnsafeRawPointer) -> UnsafeRawPointer { 169 | return UnsafeRawPointer(bitPattern: unsafeBitCast(o, to: Int.self))! 170 | } 171 | 172 | public func printArrayAddress(_ array: inout [T]) { 173 | print("array memory address: \(memoryAddress(&array))") 174 | } 175 | 176 | 177 | public extension String { 178 | 179 | /// `Int8` value of the first character 180 | var charValue: Int8 { 181 | get { 182 | return (self.cString(using: String.Encoding.utf8)?[0])! 183 | } 184 | } 185 | 186 | subscript (i: Int) -> Character { 187 | return self[self.characters.index(self.startIndex, offsetBy: i)] 188 | } 189 | 190 | // subscript (r: CountableRange) -> String { 191 | // let start = characters.index(startIndex, offsetBy: r.lowerBound) 192 | // let end = <#T##String.CharacterView corresponding to `start`##String.CharacterView#>.index(start, offsetBy: r.upperBound - r.lowerBound) 193 | // return self[start.. = zeros() 15 | /// Cached activations of this layer of the last forward propagation, for being used in backpropagation of the gradients. Can be a minibatch. 16 | var currentActivations: Tensor = zeros() 17 | 18 | open var activationFunction: ActivationFunction 19 | 20 | var previousLayer: NeuralNetLayer? 21 | var nextLayer: NeuralNetLayer? 22 | 23 | /// mode for samples in batch 24 | let batch = TensorIndex.a 25 | /// mode for neurons in the previous layer 26 | let prev = TensorIndex.b 27 | /// mode for neurons in this layer 28 | let this = TensorIndex.c 29 | 30 | open var parameters: [Tensor] = [] 31 | 32 | init(previousLayer: NeuralNetLayer? = nil, nextLayer: NeuralNetLayer? = nil, activationFunction: ActivationFunction = Sigmoid()) { 33 | self.previousLayer = previousLayer 34 | self.nextLayer = nextLayer 35 | self.activationFunction = activationFunction 36 | if let prev = previousLayer { 37 | prev.nextLayer = self 38 | } 39 | if let next = nextLayer { 40 | next.previousLayer = self 41 | } 42 | } 43 | 44 | open func output(_ input: Tensor) -> Tensor { 45 | currentPreactivations = input[batch, this] 46 | currentActivations = activationFunction.output(currentPreactivations) 47 | return currentActivations // [batch, this] 48 | } 49 | 50 | open func gradients(_ gradientWrtOutput: Tensor) -> (wrtInput: Tensor, wrtParameters: [Tensor]) { 51 | let preactivationGradient = activationFunction.derivative(currentPreactivations) °* gradientWrtOutput[batch, this] // [batch, this] 52 | return (preactivationGradient, []) 53 | } 54 | 55 | open func updateParameters(_ subtrahends: [Tensor]) {} 56 | } 57 | 58 | /// One layer of a feedforward neural net 59 | open class FeedforwardLayer: NeuralNetLayer { 60 | 61 | var weights: Tensor! 62 | var bias: Tensor! 63 | 64 | override open var parameters: [Tensor] { 65 | get { 66 | return [weights, bias] 67 | } 68 | set(newParameters) { 69 | weights = newParameters[0][prev, this] 70 | bias = newParameters[1][this] 71 | } 72 | } 73 | 74 | init(weights: Tensor, bias: Tensor, previousLayer: NeuralNetLayer? = nil, nextLayer: NeuralNetLayer? = nil, activationFunction: ActivationFunction = Sigmoid()) { 75 | super.init(previousLayer: previousLayer, nextLayer: nextLayer, activationFunction: activationFunction) 76 | self.weights = weights[prev, this] 77 | self.bias = bias[this] 78 | } 79 | 80 | func feedforward(_ inputActivations: Tensor) -> Tensor { 81 | let activationProduct = inputActivations[batch, prev] * weights 82 | currentPreactivations = activationProduct + bias // [batch, this] 83 | currentActivations = activationFunction.output(currentPreactivations) 84 | return currentActivations // [batch, this] 85 | } 86 | 87 | override open func output(_ input: Tensor) -> Tensor { 88 | return feedforward(input) 89 | } 90 | 91 | /// Calculate the gradients via backpropagation 92 | /// - Parameter gradientWrtOutput: The gradient of the target function, with respect to the output of this layer, i.e. the input of the following layer. 93 | /// - Returns: 94 | /// `wrtInput`:
The gradient of the target function with respect to the input of this layer. Should be used as input to this function of the preceding layer during backpropagation.
95 | /// `wrtParameters`:
The gradient of the target function with respect to the parameters of this layer (i.e. [wrtWeights, wrtBias]).
96 | override open func gradients(_ gradientWrtOutput: Tensor) -> (wrtInput: Tensor, wrtParameters: [Tensor]) { 97 | 98 | let activationDerivative = activationFunction.derivative(currentPreactivations)[batch, this] 99 | let preactivationGradient = gradientWrtOutput[batch, this] °* activationDerivative // [batch, this] 100 | 101 | let weightGradient = previousLayer!.currentActivations[batch, prev] * preactivationGradient // [prev, this] 102 | let biasGradient = sum(preactivationGradient, overModes: [0]) // [this] 103 | let inputGradient = preactivationGradient * weights // [batch, prev] 104 | 105 | return (inputGradient, [weightGradient, biasGradient]) 106 | } 107 | 108 | 109 | 110 | override open func updateParameters(_ substrahends: [Tensor]) { 111 | weights = weights - substrahends[0] 112 | bias = bias - substrahends[1] 113 | } 114 | } 115 | 116 | open class NeuralNet: ParametricTensorFunction { 117 | open var layers: [NeuralNetLayer] 118 | 119 | open var parameters: [Tensor] { 120 | get { 121 | return layers.flatMap({$0.parameters}) 122 | } 123 | set(newParameters) { 124 | var currentParameter = 0 125 | 126 | for layer in layers { 127 | let parameterCount = layer.parameters.count 128 | for p in 0..) -> Tensor { 150 | return layers.reduce(input, {$1.output($0)}) 151 | } 152 | 153 | open func gradients(_ gradientWrtOutput: Tensor) -> (wrtInput: Tensor, wrtParameters: [Tensor]) { 154 | //backpropagation 155 | var gradientWrtParameters: [Tensor] = [] 156 | let gradientWrtInput = layers.reversed().reduce(gradientWrtOutput) { (currentGradientWrtOutput, currentLayer) -> Tensor in 157 | let gradients = currentLayer.gradients(currentGradientWrtOutput) 158 | gradientWrtParameters.insert(contentsOf: gradients.wrtParameters, at: 0) 159 | return gradients.wrtInput 160 | } 161 | 162 | return (gradientWrtInput, gradientWrtParameters) 163 | } 164 | 165 | open func updateParameters(_ subtrahends: [Tensor]) { 166 | var currentParameter = 0 167 | for l in layers { 168 | let parameterCount = l.parameters.count 169 | l.updateParameters(Array(subtrahends[CountableRange(start: currentParameter, distance: parameterCount)])) 170 | currentParameter += parameterCount 171 | } 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /.build/debug.yaml: -------------------------------------------------------------------------------- 1 | client: 2 | name: swift-build 3 | tools: {} 4 | targets: 5 | test: [] 6 | default: [, ] 7 | commands: 8 | /Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build: 9 | tool: mkdir 10 | outputs: ["/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build"] 11 | 12 | : 13 | tool: swift-compiler 14 | executable: /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2016-03-24-a.xctoolchain/usr/bin/swiftc 15 | module-name: MultilinearMath 16 | module-output-path: /Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.swiftmodule 17 | inputs: [] 18 | outputs: [, "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/AccelerateFunctionsFloat.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/main.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/MPCA.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/MultidimensionalData.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/SlicingSubscripts.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/TensorMultiplication.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/TensorOperations.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/Tensors.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/UMPCA.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/Utilities.swift.o"] 19 | import-paths: /Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug 20 | temps-path: /Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build 21 | objects: ["/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/AccelerateFunctionsFloat.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/main.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/MPCA.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/MultidimensionalData.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/SlicingSubscripts.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/TensorMultiplication.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/TensorOperations.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/Tensors.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/UMPCA.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/Utilities.swift.o"] 22 | other-args: ["-j8", "-Onone", "-g", "-D", SWIFT_PACKAGE, "-enable-testing", "-F", /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks, "-target", "x86_64-apple-macosx10.10", "-sdk", /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk] 23 | sources: ["/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/Sources/AccelerateFunctionsFloat.swift", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/Sources/main.swift", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/Sources/MPCA.swift", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/Sources/MultidimensionalData.swift", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/Sources/SlicingSubscripts.swift", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/Sources/TensorMultiplication.swift", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/Sources/TensorOperations.swift", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/Sources/Tensors.swift", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/Sources/UMPCA.swift", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/Sources/Utilities.swift"] 24 | is-library: false 25 | 26 | : 27 | tool: shell 28 | description: Linking .build/debug/MultilinearMath 29 | inputs: [, "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/AccelerateFunctionsFloat.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/main.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/MPCA.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/MultidimensionalData.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/SlicingSubscripts.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/TensorMultiplication.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/TensorOperations.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/Tensors.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/UMPCA.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/Utilities.swift.o"] 30 | outputs: [, "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath"] 31 | args: ["/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2016-03-24-a.xctoolchain/usr/bin/swiftc", "-target", "x86_64-apple-macosx10.10", "-sdk", /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk, "-g", "-L/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug", "-o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath", "-emit-executable", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/AccelerateFunctionsFloat.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/main.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/MPCA.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/MultidimensionalData.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/SlicingSubscripts.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/TensorMultiplication.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/TensorOperations.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/Tensors.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/UMPCA.swift.o", "/Users/vincentherrmann/Documents/Software/SwiftPackages/multilinear-math/.build/debug/MultilinearMath.build/Utilities.swift.o"] 32 | 33 | --------------------------------------------------------------------------------