├── .gitattributes ├── .github └── workflows │ ├── build_test.yml │ ├── publish_coverage.yml │ └── publish_docs.yml ├── .gitignore ├── .jazzy.yaml ├── Cartfile ├── LASwift.podspec ├── LASwift.xcodeproj ├── GeneratedModuleMap │ └── CwlCatchExceptionSupport │ │ └── module.modulemap ├── LASwift_Info.plist ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── xcshareddata │ └── xcschemes │ └── LASwift.xcscheme ├── LICENSE ├── Package.swift ├── README.md ├── Sources ├── Matrix.swift ├── MatrixAlgebra.swift ├── MatrixArithmetic.swift ├── MatrixExponent.swift ├── MatrixLeastSquare.swift ├── MatrixStatistics.swift ├── MatrixTrigonometry.swift ├── Numeric.swift ├── Operators.swift ├── Random.swift ├── Util.swift ├── Vector.swift ├── VectorArithmetic.swift ├── VectorExponent.swift ├── VectorStatistics.swift └── VectorTrigonometry.swift └── Tests ├── MatrixTests.swift ├── PerformanceTests.swift └── VectorTests.swift /.gitattributes: -------------------------------------------------------------------------------- 1 | Example/Pods/* linguist-vendored 2 | -------------------------------------------------------------------------------- /.github/workflows/build_test.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Swift project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-swift 3 | 4 | name: Build 5 | 6 | on: 7 | push: 8 | branches: 9 | - master 10 | pull_request: 11 | branches: 12 | - master 13 | 14 | jobs: 15 | build: 16 | 17 | runs-on: macos-latest 18 | 19 | steps: 20 | - uses: actions/checkout@v3 21 | - name: Build 22 | run: swift build 23 | - name: Run tests 24 | run: swift test 25 | -------------------------------------------------------------------------------- /.github/workflows/publish_coverage.yml: -------------------------------------------------------------------------------- 1 | name: Coverage 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: macos-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v3 15 | - name: Run tests 16 | run: swift test --enable-code-coverage 17 | - name: Prepare code coverage 18 | run: xcrun llvm-cov export -format="lcov" .build/debug/LASwiftPackageTests.xctest/Contents/MacOS/LASwiftPackageTests -instr-profile .build/debug/codecov/default.profdata > info.lcov 19 | - name: Upload to codecov.io 20 | run: bash <(curl https://codecov.io/bash) 21 | env: 22 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 23 | -------------------------------------------------------------------------------- /.github/workflows/publish_docs.yml: -------------------------------------------------------------------------------- 1 | name: Documentation 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | deploy_docs: 9 | runs-on: macos-latest 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Publish Jazzy Docs 13 | uses: steven0351/publish-jazzy-docs@v1 14 | with: 15 | personal_access_token: ${{ secrets.ACCESS_TOKEN }} 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | .build/ 7 | *.pbxuser 8 | !default.pbxuser 9 | *.mode1v3 10 | !default.mode1v3 11 | *.mode2v3 12 | !default.mode2v3 13 | *.perspectivev3 14 | !default.perspectivev3 15 | xcuserdata/ 16 | *.xccheckout 17 | profile 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | .swiftpm 23 | 24 | # Bundler 25 | .bundle 26 | 27 | *.lock 28 | 29 | Carthage 30 | 31 | Package.resolved 32 | Cartfile.resolved 33 | 34 | -------------------------------------------------------------------------------- /.jazzy.yaml: -------------------------------------------------------------------------------- 1 | # podspec: LASwift.podspec 2 | 3 | author: Alexander Taraymovich 4 | github_url: https://github.com/AlexanderTar/LASwift/ 5 | 6 | module: LASwift 7 | swift_build_tool: spm 8 | build_tool_arguments: 9 | - -Xswiftc 10 | - -swift-version 11 | - -Xswiftc 12 | - "5" 13 | 14 | min_acl: public 15 | skip_undocumented: true 16 | hide_documentation_coverage: true 17 | clean: true 18 | 19 | use_safe_filenames: true 20 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexanderTar/LASwift/b0bdd4869e8aa2de37414b0cc2c4b739225b643f/Cartfile -------------------------------------------------------------------------------- /LASwift.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint LASwift.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = 'LASwift' 11 | s.version = '0.3.2' 12 | s.summary = 'Linear algebra library for Swift language' 13 | 14 | s.description = <<-DESC 15 | This library provides most of linear algebra operations on vectors and matrices 16 | required to implement machine learning algorithms. Library syntax is inspired by 17 | Matlab matrix manipulation and Haskell linear algebra library 'hmatrix' 18 | DESC 19 | 20 | s.homepage = 'https://github.com/alexandertar/LASwift' 21 | s.license = { :type => 'BSD-3-Clause', :file => 'LICENSE' } 22 | s.author = { 'Alexander Taraymovich' => 'taraymovich@me.com' } 23 | s.source = { :git => 'https://github.com/alexandertar/LASwift.git', :tag => s.version.to_s } 24 | 25 | s.ios.deployment_target = '12.0' 26 | s.osx.deployment_target = '10.13' 27 | s.tvos.deployment_target = '12.0' 28 | s.watchos.deployment_target = '6.0' 29 | 30 | s.swift_version = '5.0' 31 | 32 | s.frameworks = 'Accelerate' 33 | 34 | s.source_files = 'Sources/**/*' 35 | end 36 | -------------------------------------------------------------------------------- /LASwift.xcodeproj/GeneratedModuleMap/CwlCatchExceptionSupport/module.modulemap: -------------------------------------------------------------------------------- 1 | module CwlCatchExceptionSupport { 2 | umbrella "/Users/alexandertar/git/LASwift/.build/checkouts/CwlCatchException/Sources/CwlCatchExceptionSupport/include" 3 | export * 4 | } 5 | -------------------------------------------------------------------------------- /LASwift.xcodeproj/LASwift_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /LASwift.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | OBJ_229 /* Matrix.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_8 /* Matrix.swift */; }; 11 | OBJ_230 /* MatrixAlgebra.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* MatrixAlgebra.swift */; }; 12 | OBJ_231 /* MatrixArithmetic.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_10 /* MatrixArithmetic.swift */; }; 13 | OBJ_232 /* MatrixExponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_11 /* MatrixExponent.swift */; }; 14 | OBJ_233 /* MatrixLeastSquare.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_12 /* MatrixLeastSquare.swift */; }; 15 | OBJ_234 /* MatrixStatistics.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_13 /* MatrixStatistics.swift */; }; 16 | OBJ_235 /* MatrixTrigonometry.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_14 /* MatrixTrigonometry.swift */; }; 17 | OBJ_236 /* Numeric.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_15 /* Numeric.swift */; }; 18 | OBJ_237 /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_16 /* Operators.swift */; }; 19 | OBJ_238 /* Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_17 /* Random.swift */; }; 20 | OBJ_239 /* Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_18 /* Util.swift */; }; 21 | OBJ_240 /* Vector.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_19 /* Vector.swift */; }; 22 | OBJ_241 /* VectorArithmetic.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_20 /* VectorArithmetic.swift */; }; 23 | OBJ_242 /* VectorExponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_21 /* VectorExponent.swift */; }; 24 | OBJ_243 /* VectorStatistics.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_22 /* VectorStatistics.swift */; }; 25 | OBJ_244 /* VectorTrigonometry.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_23 /* VectorTrigonometry.swift */; }; 26 | /* End PBXBuildFile section */ 27 | 28 | /* Begin PBXFileReference section */ 29 | OBJ_10 /* MatrixArithmetic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixArithmetic.swift; sourceTree = ""; }; 30 | OBJ_11 /* MatrixExponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixExponent.swift; sourceTree = ""; }; 31 | OBJ_12 /* MatrixLeastSquare.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixLeastSquare.swift; sourceTree = ""; }; 32 | OBJ_13 /* MatrixStatistics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixStatistics.swift; sourceTree = ""; }; 33 | OBJ_14 /* MatrixTrigonometry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixTrigonometry.swift; sourceTree = ""; }; 34 | OBJ_15 /* Numeric.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Numeric.swift; sourceTree = ""; }; 35 | OBJ_159 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; 36 | OBJ_16 /* Operators.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Operators.swift; sourceTree = ""; }; 37 | OBJ_160 /* Cartfile */ = {isa = PBXFileReference; lastKnownFileType = text; path = Cartfile; sourceTree = ""; }; 38 | OBJ_161 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 39 | OBJ_162 /* Rakefile */ = {isa = PBXFileReference; lastKnownFileType = text; path = Rakefile; sourceTree = ""; }; 40 | OBJ_163 /* LASwift.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = LASwift.podspec; sourceTree = ""; }; 41 | OBJ_17 /* Random.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Random.swift; sourceTree = ""; }; 42 | OBJ_18 /* Util.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Util.swift; sourceTree = ""; }; 43 | OBJ_19 /* Vector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Vector.swift; sourceTree = ""; }; 44 | OBJ_20 /* VectorArithmetic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VectorArithmetic.swift; sourceTree = ""; }; 45 | OBJ_21 /* VectorExponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VectorExponent.swift; sourceTree = ""; }; 46 | OBJ_22 /* VectorStatistics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VectorStatistics.swift; sourceTree = ""; }; 47 | OBJ_23 /* VectorTrigonometry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VectorTrigonometry.swift; sourceTree = ""; }; 48 | OBJ_6 /* Package.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; 49 | OBJ_8 /* Matrix.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Matrix.swift; sourceTree = ""; }; 50 | OBJ_9 /* MatrixAlgebra.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixAlgebra.swift; sourceTree = ""; }; 51 | "laswift::LASwift::Product" /* LASwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = LASwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 52 | /* End PBXFileReference section */ 53 | 54 | /* Begin PBXFrameworksBuildPhase section */ 55 | OBJ_245 /* Frameworks */ = { 56 | isa = PBXFrameworksBuildPhase; 57 | buildActionMask = 0; 58 | files = ( 59 | ); 60 | runOnlyForDeploymentPostprocessing = 0; 61 | }; 62 | /* End PBXFrameworksBuildPhase section */ 63 | 64 | /* Begin PBXGroup section */ 65 | OBJ_147 /* Products */ = { 66 | isa = PBXGroup; 67 | children = ( 68 | "laswift::LASwift::Product" /* LASwift.framework */, 69 | ); 70 | name = Products; 71 | sourceTree = BUILT_PRODUCTS_DIR; 72 | }; 73 | OBJ_5 /* */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | OBJ_6 /* Package.swift */, 77 | OBJ_7 /* Sources */, 78 | OBJ_147 /* Products */, 79 | OBJ_159 /* LICENSE */, 80 | OBJ_160 /* Cartfile */, 81 | OBJ_161 /* README.md */, 82 | OBJ_162 /* Rakefile */, 83 | OBJ_163 /* LASwift.podspec */, 84 | ); 85 | name = ""; 86 | sourceTree = ""; 87 | }; 88 | OBJ_7 /* Sources */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | OBJ_8 /* Matrix.swift */, 92 | OBJ_9 /* MatrixAlgebra.swift */, 93 | OBJ_10 /* MatrixArithmetic.swift */, 94 | OBJ_11 /* MatrixExponent.swift */, 95 | OBJ_12 /* MatrixLeastSquare.swift */, 96 | OBJ_13 /* MatrixStatistics.swift */, 97 | OBJ_14 /* MatrixTrigonometry.swift */, 98 | OBJ_15 /* Numeric.swift */, 99 | OBJ_16 /* Operators.swift */, 100 | OBJ_17 /* Random.swift */, 101 | OBJ_18 /* Util.swift */, 102 | OBJ_19 /* Vector.swift */, 103 | OBJ_20 /* VectorArithmetic.swift */, 104 | OBJ_21 /* VectorExponent.swift */, 105 | OBJ_22 /* VectorStatistics.swift */, 106 | OBJ_23 /* VectorTrigonometry.swift */, 107 | ); 108 | path = Sources; 109 | sourceTree = SOURCE_ROOT; 110 | }; 111 | /* End PBXGroup section */ 112 | 113 | /* Begin PBXNativeTarget section */ 114 | "laswift::LASwift" /* LASwift */ = { 115 | isa = PBXNativeTarget; 116 | buildConfigurationList = OBJ_225 /* Build configuration list for PBXNativeTarget "LASwift" */; 117 | buildPhases = ( 118 | OBJ_228 /* Sources */, 119 | OBJ_245 /* Frameworks */, 120 | ); 121 | buildRules = ( 122 | ); 123 | dependencies = ( 124 | ); 125 | name = LASwift; 126 | productName = LASwift; 127 | productReference = "laswift::LASwift::Product" /* LASwift.framework */; 128 | productType = "com.apple.product-type.framework"; 129 | }; 130 | /* End PBXNativeTarget section */ 131 | 132 | /* Begin PBXProject section */ 133 | OBJ_1 /* Project object */ = { 134 | isa = PBXProject; 135 | attributes = { 136 | LastSwiftMigration = 9999; 137 | LastUpgradeCheck = 9999; 138 | }; 139 | buildConfigurationList = OBJ_2 /* Build configuration list for PBXProject "LASwift" */; 140 | compatibilityVersion = "Xcode 3.2"; 141 | developmentRegion = en; 142 | hasScannedForEncodings = 0; 143 | knownRegions = ( 144 | en, 145 | ); 146 | mainGroup = OBJ_5 /* */; 147 | productRefGroup = OBJ_147 /* Products */; 148 | projectDirPath = ""; 149 | projectRoot = ""; 150 | targets = ( 151 | "laswift::LASwift" /* LASwift */, 152 | ); 153 | }; 154 | /* End PBXProject section */ 155 | 156 | /* Begin PBXSourcesBuildPhase section */ 157 | OBJ_228 /* Sources */ = { 158 | isa = PBXSourcesBuildPhase; 159 | buildActionMask = 0; 160 | files = ( 161 | OBJ_229 /* Matrix.swift in Sources */, 162 | OBJ_230 /* MatrixAlgebra.swift in Sources */, 163 | OBJ_231 /* MatrixArithmetic.swift in Sources */, 164 | OBJ_232 /* MatrixExponent.swift in Sources */, 165 | OBJ_233 /* MatrixLeastSquare.swift in Sources */, 166 | OBJ_234 /* MatrixStatistics.swift in Sources */, 167 | OBJ_235 /* MatrixTrigonometry.swift in Sources */, 168 | OBJ_236 /* Numeric.swift in Sources */, 169 | OBJ_237 /* Operators.swift in Sources */, 170 | OBJ_238 /* Random.swift in Sources */, 171 | OBJ_239 /* Util.swift in Sources */, 172 | OBJ_240 /* Vector.swift in Sources */, 173 | OBJ_241 /* VectorArithmetic.swift in Sources */, 174 | OBJ_242 /* VectorExponent.swift in Sources */, 175 | OBJ_243 /* VectorStatistics.swift in Sources */, 176 | OBJ_244 /* VectorTrigonometry.swift in Sources */, 177 | ); 178 | runOnlyForDeploymentPostprocessing = 0; 179 | }; 180 | /* End PBXSourcesBuildPhase section */ 181 | 182 | /* Begin XCBuildConfiguration section */ 183 | OBJ_226 /* Debug */ = { 184 | isa = XCBuildConfiguration; 185 | buildSettings = { 186 | CURRENT_PROJECT_VERSION = 1; 187 | DRIVERKIT_DEPLOYMENT_TARGET = 19.0; 188 | ENABLE_TESTABILITY = YES; 189 | FRAMEWORK_SEARCH_PATHS = ( 190 | "$(inherited)", 191 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 192 | ); 193 | HEADER_SEARCH_PATHS = "$(inherited)"; 194 | INFOPLIST_FILE = LASwift.xcodeproj/LASwift_Info.plist; 195 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 196 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; 197 | MACOSX_DEPLOYMENT_TARGET = 10.13; 198 | OTHER_CFLAGS = "$(inherited)"; 199 | OTHER_LDFLAGS = "$(inherited)"; 200 | OTHER_SWIFT_FLAGS = "$(inherited)"; 201 | PRODUCT_BUNDLE_IDENTIFIER = LASwift; 202 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; 203 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 204 | SKIP_INSTALL = YES; 205 | SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx watchos watchsimulator"; 206 | SUPPORTS_MACCATALYST = YES; 207 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; 208 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; 209 | SWIFT_VERSION = 5.0; 210 | TARGETED_DEVICE_FAMILY = "1,3,4"; 211 | TARGET_NAME = LASwift; 212 | TVOS_DEPLOYMENT_TARGET = 12.0; 213 | WATCHOS_DEPLOYMENT_TARGET = 6.0; 214 | }; 215 | name = Debug; 216 | }; 217 | OBJ_227 /* Release */ = { 218 | isa = XCBuildConfiguration; 219 | buildSettings = { 220 | CURRENT_PROJECT_VERSION = 1; 221 | DRIVERKIT_DEPLOYMENT_TARGET = 19.0; 222 | ENABLE_TESTABILITY = YES; 223 | FRAMEWORK_SEARCH_PATHS = ( 224 | "$(inherited)", 225 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 226 | ); 227 | HEADER_SEARCH_PATHS = "$(inherited)"; 228 | INFOPLIST_FILE = LASwift.xcodeproj/LASwift_Info.plist; 229 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 230 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; 231 | MACOSX_DEPLOYMENT_TARGET = 10.13; 232 | OTHER_CFLAGS = "$(inherited)"; 233 | OTHER_LDFLAGS = "$(inherited)"; 234 | OTHER_SWIFT_FLAGS = "$(inherited)"; 235 | PRODUCT_BUNDLE_IDENTIFIER = LASwift; 236 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; 237 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 238 | SKIP_INSTALL = YES; 239 | SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx watchos watchsimulator"; 240 | SUPPORTS_MACCATALYST = YES; 241 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; 242 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; 243 | SWIFT_VERSION = 5.0; 244 | TARGETED_DEVICE_FAMILY = "1,3,4"; 245 | TARGET_NAME = LASwift; 246 | TVOS_DEPLOYMENT_TARGET = 12.0; 247 | WATCHOS_DEPLOYMENT_TARGET = 6.0; 248 | }; 249 | name = Release; 250 | }; 251 | OBJ_3 /* Debug */ = { 252 | isa = XCBuildConfiguration; 253 | buildSettings = { 254 | CLANG_ENABLE_OBJC_ARC = YES; 255 | COMBINE_HIDPI_IMAGES = YES; 256 | COPY_PHASE_STRIP = NO; 257 | DEBUG_INFORMATION_FORMAT = dwarf; 258 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 259 | ENABLE_NS_ASSERTIONS = YES; 260 | GCC_OPTIMIZATION_LEVEL = 0; 261 | GCC_PREPROCESSOR_DEFINITIONS = ( 262 | "$(inherited)", 263 | "SWIFT_PACKAGE=1", 264 | "DEBUG=1", 265 | ); 266 | MACOSX_DEPLOYMENT_TARGET = 10.10; 267 | ONLY_ACTIVE_ARCH = YES; 268 | OTHER_SWIFT_FLAGS = "$(inherited) -DXcode"; 269 | PRODUCT_NAME = "$(TARGET_NAME)"; 270 | SDKROOT = macosx; 271 | SUPPORTED_PLATFORMS = "$(AVAILABLE_PLATFORMS)"; 272 | SUPPORTS_MACCATALYST = YES; 273 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) SWIFT_PACKAGE DEBUG"; 274 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 275 | USE_HEADERMAP = NO; 276 | }; 277 | name = Debug; 278 | }; 279 | OBJ_4 /* Release */ = { 280 | isa = XCBuildConfiguration; 281 | buildSettings = { 282 | CLANG_ENABLE_OBJC_ARC = YES; 283 | COMBINE_HIDPI_IMAGES = YES; 284 | COPY_PHASE_STRIP = YES; 285 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 286 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 287 | GCC_OPTIMIZATION_LEVEL = s; 288 | GCC_PREPROCESSOR_DEFINITIONS = ( 289 | "$(inherited)", 290 | "SWIFT_PACKAGE=1", 291 | ); 292 | MACOSX_DEPLOYMENT_TARGET = 10.10; 293 | OTHER_SWIFT_FLAGS = "$(inherited) -DXcode"; 294 | PRODUCT_NAME = "$(TARGET_NAME)"; 295 | SDKROOT = macosx; 296 | SUPPORTED_PLATFORMS = "$(AVAILABLE_PLATFORMS)"; 297 | SUPPORTS_MACCATALYST = YES; 298 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) SWIFT_PACKAGE"; 299 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 300 | USE_HEADERMAP = NO; 301 | }; 302 | name = Release; 303 | }; 304 | /* End XCBuildConfiguration section */ 305 | 306 | /* Begin XCConfigurationList section */ 307 | OBJ_2 /* Build configuration list for PBXProject "LASwift" */ = { 308 | isa = XCConfigurationList; 309 | buildConfigurations = ( 310 | OBJ_3 /* Debug */, 311 | OBJ_4 /* Release */, 312 | ); 313 | defaultConfigurationIsVisible = 0; 314 | defaultConfigurationName = Release; 315 | }; 316 | OBJ_225 /* Build configuration list for PBXNativeTarget "LASwift" */ = { 317 | isa = XCConfigurationList; 318 | buildConfigurations = ( 319 | OBJ_226 /* Debug */, 320 | OBJ_227 /* Release */, 321 | ); 322 | defaultConfigurationIsVisible = 0; 323 | defaultConfigurationName = Release; 324 | }; 325 | /* End XCConfigurationList section */ 326 | }; 327 | rootObject = OBJ_1 /* Project object */; 328 | } 329 | -------------------------------------------------------------------------------- /LASwift.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /LASwift.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /LASwift.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded 6 | 7 | 8 | -------------------------------------------------------------------------------- /LASwift.xcodeproj/xcshareddata/xcschemes/LASwift.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 57 | 58 | 59 | 60 | 62 | 63 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Alexander Taraymovich 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of Alexander Taraymovich nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.1 2 | // Package.swift 3 | // 4 | // Copyright (c) 2017 Alexander Taraymovich 5 | // All rights reserved. 6 | // 7 | // This software may be modified and distributed under the terms 8 | // of the BSD license. See the LICENSE file for details. 9 | import PackageDescription 10 | 11 | let package = Package( 12 | name: "LASwift", 13 | platforms: [ 14 | .macOS(.v10_13), .iOS(.v12), .tvOS(.v12), .watchOS(.v6) 15 | ], 16 | products: [ 17 | .library(name: "LASwift", targets: ["LASwift"]) 18 | ], 19 | dependencies: [ 20 | .package(url: "https://github.com/Quick/Quick.git", from: "5.0.0"), 21 | .package(url: "https://github.com/Quick/Nimble.git", from: "10.0.0") 22 | ], 23 | targets: [ 24 | .target( 25 | name: "LASwift", 26 | dependencies: [], 27 | path: "Sources"), 28 | .testTarget( 29 | name: "LASwiftTests", 30 | dependencies: ["LASwift", "Quick", "Nimble"], 31 | path: "Tests") 32 | ], 33 | swiftLanguageVersions: [.v5] 34 | ) 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LASwift 2 | 3 | [![Build](https://github.com/AlexanderTar/LASwift/actions/workflows/build_test.yml/badge.svg?branch=master)](https://github.com/AlexanderTar/LASwift/actions/workflows/build_test.yml) 4 | [![codecov](https://codecov.io/gh/AlexanderTar/LASwift/branch/master/graph/badge.svg)](https://codecov.io/gh/AlexanderTar/LASwift) 5 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 6 | [![Documentation](https://img.shields.io/badge/LASwift-documentation-blue.svg)](https://alexandertar.github.io/LASwift/index.html) 7 | [![License](https://img.shields.io/cocoapods/l/LASwift.svg?style=flat)](https://raw.githubusercontent.com/AlexanderTar/LASwift/master/LICENSE) 8 | [![Version](https://img.shields.io/cocoapods/v/LASwift.svg?style=flat)](http://cocoapods.org/pods/LASwift) 9 | [![Platform](https://img.shields.io/cocoapods/p/LASwift.svg?style=flat)](http://cocoapods.org/pods/LASwift) 10 | 11 | LASwift provides most of linear algebra operations on vectors and matrices 12 | required to implement machine learning algorithms. Library syntax is inspired by 13 | Matlab matrix manipulation and Haskell linear algebra library 'hmatrix'. LASwift is 14 | using high-performant calculations provided by LAPACK, BLAS and vDSP through Apple 15 | Accelerate framework. 16 | 17 | ## Currently supported 18 | 19 | Following operations are fully supported for both vectors and matrices: 20 | 21 | - Arithmetic operations (addition, substraction, multiplication, division, absolute value) 22 | - Exponential functions (raise to power, exponent, logarithms) 23 | - Trigonometric functions (sine, cosine, tangent) 24 | - Statistics functions (max, min, mean value, standard deviation) 25 | 26 | Linear algebra operations on matrices: 27 | 28 | - Inversion 29 | - Transposition 30 | - Matrix power (integer values) 31 | - Eigenvectors and eigenvalues 32 | - Singular value decomposition 33 | 34 | Following matrix manipulation operations are supported: 35 | 36 | - Concatenation 37 | - Slicing 38 | 39 | ## Requirements 40 | 41 | - iOS 12.0+ / Mac OS X 10.13+ / tvOS 12.0+ / watchOS 2.0+ 42 | - Xcode 12.0+ 43 | - Swift 5.0+ 44 | 45 | ## Benchmarks 46 | 47 | Refer to [linalg-benchmarks](https://github.com/Alexander-Ignatyev/linalg-benchmarks) project 48 | regarding basic benchmarking of latest version of LASwift against most popular linear 49 | algebra libraries (Haskell hmatrix, Python NumPy, Octave, Go gonum-matrix). 50 | 51 | ## Installation 52 | 53 | #### CocoaPods 54 | 55 | Install CocoaPods if not already available: 56 | 57 | ``` bash 58 | $ [sudo] gem install cocoapods 59 | $ pod setup 60 | ``` 61 | Go to the directory of your Xcode project, and Create and Edit your *Podfile* and add _LASwift_: 62 | 63 | ``` bash 64 | $ cd /path/to/MyProject 65 | $ touch Podfile 66 | $ edit Podfile 67 | source 'https://github.com/CocoaPods/Specs.git' 68 | platform :ios, '8.0' 69 | 70 | use_frameworks! 71 | pod 'LASwift', '~> 0.3.2' 72 | ``` 73 | 74 | Install into your project: 75 | 76 | ``` bash 77 | $ pod install 78 | ``` 79 | 80 | Open your project in Xcode from the .xcworkspace file (not the usual project file): 81 | 82 | ``` bash 83 | $ open MyProject.xcworkspace 84 | ``` 85 | 86 | You can now `import LASwift` framework into your files. 87 | 88 | #### Carthage 89 | 90 | [Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that automates the process of adding frameworks to your Cocoa application. 91 | 92 | You can install Carthage with [Homebrew](http://brew.sh/) using the following command: 93 | 94 | ```bash 95 | $ brew update 96 | $ brew install carthage 97 | ``` 98 | 99 | To integrate `LASwift` into your Xcode project using Carthage, specify it in your `Cartfile` file: 100 | 101 | ```ogdl 102 | github "alexandertar/LASwift" >= 0.3.2 103 | ``` 104 | 105 | #### Swift Package Manager 106 | You can use [The Swift Package Manager](https://swift.org/package-manager) to install `LASwift` by adding the proper description to your `Package.swift` file: 107 | ```swift 108 | import PackageDescription 109 | 110 | let package = Package( 111 | name: "YOUR_PROJECT_NAME", 112 | targets: [], 113 | dependencies: [ 114 | .Package(url: "https://github.com/alexandertar/LASwift", versions: "0.3.2" ..< Version.max) 115 | ] 116 | ) 117 | ``` 118 | 119 | Note that the [Swift Package Manager](https://swift.org/package-manager) is still in early design and development, for more information checkout its [GitHub Page](https://github.com/apple/swift-package-manager). 120 | 121 | ## Contribution 122 | 123 | Currently implemented functionality should be sufficient enough to implement machine learning 124 | algorithms (as this was an initial purpose). However, if you find something missing or wish to add 125 | extra features, feel free to submit pull-requests or create issues with proposals. 126 | 127 | ## Author 128 | 129 | Alexander Taraymovich, taraymovich@me.com 130 | 131 | ## License 132 | 133 | LASwift is available under the BSD-3-Clause license. See the LICENSE file for more info. 134 | -------------------------------------------------------------------------------- /Sources/Matrix.swift: -------------------------------------------------------------------------------- 1 | // Matrix.swift 2 | // 3 | // Copyright (c) 2017 Alexander Taraymovich 4 | // All rights reserved. 5 | // 6 | // This software may be modified and distributed under the terms 7 | // of the BSD license. See the LICENSE file for details. 8 | 9 | import Accelerate 10 | 11 | // MARK: - One-line creators for matrices 12 | 13 | /// Create a matrix of zeros. 14 | /// 15 | /// - Parameters: 16 | /// - rows: number of rows 17 | /// - cols: number of columns 18 | /// - Returns: zeros matrix of specified size 19 | public func zeros(_ rows: Int, _ cols: Int) -> Matrix { 20 | precondition(rows > 0 && cols > 0, "Matrix dimensions must be positive") 21 | return Matrix(rows, cols, 0.0) 22 | } 23 | 24 | /// Create a matrix of zeros. 25 | /// 26 | /// - Parameters: 27 | /// - m: Matrix 28 | /// - Returns: matrix of zeros with same size as input 29 | public func zeros(like m: Matrix) -> Matrix { 30 | return zeros(m.rows, m.cols) 31 | } 32 | 33 | /// Create a matrix of ones. 34 | /// 35 | /// - Parameters: 36 | /// - rows: number of rows 37 | /// - cols: number of columns 38 | /// - Returns: ones matrix of specified size 39 | public func ones(_ rows: Int, _ cols: Int) -> Matrix { 40 | precondition(rows > 0 && cols > 0, "Matrix dimensions must be positive") 41 | return Matrix(rows, cols, 1.0) 42 | } 43 | 44 | /// Create a matrix of ones. 45 | /// 46 | /// - Parameters: 47 | /// - m: Matrix 48 | /// - Returns: matrix of ones with same size as input 49 | public func ones(like m: Matrix) -> Matrix { 50 | return ones(m.rows, m.cols) 51 | } 52 | 53 | /// Create a matrix of uniformly distributed on [0, 1) interval random values. 54 | /// 55 | /// - Parameters: 56 | /// - rows: number of rows 57 | /// - cols: number of columns 58 | /// - Returns: random values matrix of specified size 59 | public func rand(_ rows: Int, _ cols: Int) -> Matrix { 60 | precondition(rows > 0 && cols > 0, "Matrix dimensions must be positive") 61 | return Matrix(rows, cols, rand(rows * cols)) 62 | } 63 | 64 | /// Create a matrix of normally distibuted random values. 65 | /// 66 | /// - Parameters: 67 | /// - rows: number of rows 68 | /// - cols: number of columns 69 | /// - Returns: random values matrix of specified size 70 | public func randn(_ rows: Int, _ cols: Int) -> Matrix { 71 | precondition(rows > 0 && cols > 0, "Matrix dimensions must be positive") 72 | return Matrix(rows, cols, randn(rows * cols)) 73 | } 74 | 75 | /// Create a matrix with ones on the main diagonal and zeros elsewhere. 76 | /// 77 | /// - Parameters: 78 | /// - rows: number of rows 79 | /// - cols: number of columns 80 | /// - Returns: identity matrix of specified size 81 | public func eye(_ rows: Int, _ cols: Int) -> Matrix { 82 | precondition(rows > 0 && cols > 0, "Matrix dimensions must be positive") 83 | return Matrix((0.. Vector in 84 | var row = Vector(repeating: 0.0, count: cols) 85 | if (i < cols) { 86 | row[i] = 1.0 87 | } 88 | return row 89 | }) 90 | } 91 | 92 | /// Create a square matrix with specified values on the main diagonal and zeros elsewhere. 93 | /// 94 | /// - Parameters: 95 | /// - v: matrix of values with one column 96 | /// - Returns: square diagonal matrix with specified values 97 | public func diag(_ v: Matrix) -> Matrix { 98 | precondition(v.cols == 1, "Input must be a vector") 99 | return diag(v.flat) 100 | } 101 | 102 | /// Create a square matrix with specified values on the main diagonal and zeros elsewhere. 103 | /// 104 | /// - Parameters: 105 | /// - v: vector of values 106 | /// - Returns: square diagonal matrix with specified values 107 | public func diag(_ v: Vector) -> Matrix { 108 | let count = v.count 109 | let m: Matrix = zeros(count, count) 110 | _ = (0.. Matrix { 122 | precondition(v.cols == 1, "Input must be a vector") 123 | return diag(rows, cols, v.flat) 124 | } 125 | 126 | /// Create a matrix with specified values on the main diagonal and zeros elsewhere. 127 | /// 128 | /// - Parameters: 129 | /// - rows: number of rows 130 | /// - cols: number of columns 131 | /// - v: vector of values 132 | /// - Returns: diagonal matrix with specified values and size 133 | public func diag(_ rows: Int, _ cols: Int, _ v: Vector) -> Matrix { 134 | precondition(rows > 0 && cols > 0, "Matrix dimensions must be positive") 135 | return Matrix((0.. Vector in 136 | var row = Vector(repeating: 0.0, count: cols) 137 | if (i < cols) { 138 | row[i] = v[i] 139 | } 140 | return row 141 | }) 142 | } 143 | 144 | // MARK: - Matrix class 145 | 146 | /// Matrix dimensions. 147 | /// 148 | /// - Row: row. 149 | /// - Column: column. 150 | public enum Dim { 151 | case Row 152 | case Column 153 | } 154 | 155 | /// Matrix of Double values 156 | public class Matrix { 157 | public var flat = Vector() 158 | internal var _rows: Int = 0 159 | internal var _cols: Int = 0 160 | 161 | /// Number of rows in Matrix. 162 | public var rows: Int { 163 | return _rows 164 | } 165 | 166 | /// Number of columns in Matrix. 167 | public var cols: Int { 168 | return _cols 169 | } 170 | 171 | /// Transpose 172 | public var T: Matrix { 173 | get { 174 | return transpose(self) 175 | } 176 | } 177 | 178 | public init(_ r: Int, _ c: Int, _ value: Double = 0.0) { 179 | precondition(r > 0 && c > 0, "Matrix dimensions must be positive") 180 | flat = Vector(repeating: value, count: r * c) 181 | _rows = r 182 | _cols = c 183 | } 184 | 185 | public init(_ r: Int, _ c: Int, _ f: Vector) { 186 | precondition(r * c == f.count, "Matrix dimensions must agree") 187 | flat = f 188 | _rows = r 189 | _cols = c 190 | } 191 | 192 | /// Create new Matrix by copying existing. 193 | public init(_ M: Matrix) { 194 | flat = M.flat 195 | _rows = M.rows 196 | _cols = M.cols 197 | } 198 | 199 | /// Create 1-column Matrix (transposed Vector) 200 | public init(_ v: Vector) { 201 | flat = v 202 | _rows = v.count 203 | _cols = 1 204 | } 205 | 206 | /// Create Matrix from array of Vectors (two-dimensional array) 207 | public init(_ data: [Vector]) { 208 | // assuming empty input as invalid 209 | precondition(data.count > 0, "Input must not be empty") 210 | precondition(data[0].count > 0, "Input must not be empty") 211 | // check if all subarrays have same length 212 | precondition(Set(data.map { $0.count }).count == 1, "Input dimensions must agree") 213 | 214 | flat = data.flatMap { $0 } 215 | _rows = data.count 216 | _cols = data[0].count 217 | } 218 | } 219 | 220 | // MARK: - Gathering 221 | 222 | extension Matrix { 223 | /// Get M(row, column) element of Matrix. 224 | /// 225 | /// - Parameters: 226 | /// - row: row position of element (0-based) 227 | /// - col: col position of element (0-based) 228 | public subscript(_ row: Int, _ col: Int ) -> Double { 229 | get { 230 | precondition(indexIsValidForRow(row, col), "Invalid index") 231 | return flat[(row * cols) + col] 232 | } 233 | 234 | set { 235 | precondition(indexIsValidForRow(row, col), "Invalid index") 236 | flat[(row * cols) + col] = newValue 237 | } 238 | } 239 | 240 | /// Get M(index) element of row-major represented Matrix. 241 | /// 242 | /// - Parameters: 243 | /// - index: index of element (0-based, 0 <= index < M.rows * M.cols) 244 | public subscript(_ index: Int) -> Double { 245 | get { 246 | precondition(index < rows * cols, "Invalid index") 247 | return flat[index] 248 | } 249 | 250 | set { 251 | precondition(index < rows * cols, "Invalid index") 252 | flat[index] = newValue 253 | } 254 | } 255 | 256 | /// Get M(row) row of Matrix. 257 | /// 258 | /// - Parameters: 259 | /// - row: row index (0-based) 260 | public subscript(row row: Int) -> Vector { 261 | get { 262 | precondition(row < rows, "Invalid index") 263 | let startIndex = row * cols 264 | let endIndex = row * cols + cols 265 | return Array(flat[startIndex.. Vector { 282 | get { 283 | precondition(col < cols, "Invalid index") 284 | var result = Vector(repeating: 0.0, count: rows) 285 | _ = (0.. () in 286 | let index = i * cols + col 287 | result[i] = flat[index] 288 | } 289 | return result 290 | } 291 | 292 | set { 293 | precondition(col < cols, "Invalid index") 294 | precondition(newValue.count == rows, "Input dimensions must agree") 295 | _ = (0.. () in 296 | let index = i * cols + col 297 | flat[index] = newValue[i] 298 | } 299 | } 300 | } 301 | 302 | /// Get and set M(row, col) submatrix of Matrix. 303 | /// 304 | /// The range-based subscript methods for getting and setting submatricies. 305 | /// 306 | /// var M = Matrix([[1, 2, 3, 4], 307 | /// [5, 6, 7, 8], 308 | /// [9, 10, 11, 12]) 309 | /// 310 | /// var K = Matrix([[1, 0], 311 | /// [0, 1]) 312 | /// 313 | /// - Using bounded ranges, including partial (e.g. `..<3`): 314 | /// 315 | /// M[1..<3, 0..1] = K 316 | /// // M is now: 317 | /// // [[1, 2, 3, 4], 318 | /// // [1, 0, 7, 8], 319 | /// // [0, 1, 11, 12]] 320 | /// 321 | /// K = M[0...1, 2...] 322 | /// // K is now: 323 | /// // [[3, 4] 324 | /// // [7, 8]] 325 | /// 326 | /// - Using unbounded ranges: 327 | /// 328 | /// K = M[..., 1..2] 329 | /// // K is now: 330 | /// // [[ 2, 3], 331 | /// // [ 6, 7], 332 | /// // [10, 11]] 333 | /// 334 | /// - Parameters: 335 | /// - row: Range for rows (0-based) 336 | /// - col: Range for cols (0-based) 337 | /// 338 | /// - Returns: submatrix of size `row.count` by `col.count` 339 | /// 340 | public subscript(_ row: A, _ col: B) -> Matrix where A.Bound == Int, B.Bound == Int { 341 | get { 342 | return self[ClosedRange(row.relative(to: self[row: 0])), ClosedRange(col.relative(to: self[col: 0]))] 343 | } 344 | 345 | set { 346 | self[ClosedRange(row.relative(to: self[row: 0])), ClosedRange(col.relative(to: self[col: 0]))] = newValue 347 | } 348 | } 349 | 350 | public subscript(_ : UnboundedRange, _ col: B) -> Matrix where B.Bound == Int { 351 | get { return self[0..(col.relative(to: self[col: 0]))] } 352 | set { self[0..(col.relative(to: self[col: 0]))] = newValue } 353 | } 354 | 355 | public subscript(_ row: A, _ : UnboundedRange) -> Matrix where A.Bound == Int { 356 | get { return self[ClosedRange(row.relative(to: self[row: 0])), 0..(row.relative(to: self[row: 0])), 0.. Matrix { 361 | get { return self} 362 | set { self[0.. Matrix { 390 | get { return self[row..., col...]} 391 | set { self[row..<(row + newValue.rows), col..<(col + newValue.cols)] = newValue} 392 | } 393 | 394 | public subscript(_ row: ClosedRange, _ col: ClosedRange) -> Matrix { 395 | get { 396 | precondition(indexIsValidForRow(row.lowerBound, col.lowerBound), "Invalid range") 397 | precondition(indexIsValidForRow(row.upperBound, col.upperBound), "Invalid range") 398 | 399 | let dst = Matrix(row.count, col.count) 400 | 401 | flat.withUnsafeBufferPointer { srcBuf in 402 | let srcPtr = srcBuf.baseAddress! + row.lowerBound * cols + col.lowerBound 403 | vDSP_mmovD(srcPtr, &dst.flat, 404 | vDSP_Length(col.count), vDSP_Length(row.count), 405 | vDSP_Length(cols), vDSP_Length(col.count)) 406 | } 407 | 408 | return dst 409 | } 410 | 411 | set { 412 | precondition(indexIsValidForRow(row.lowerBound, col.lowerBound), "Invalid range") 413 | precondition(indexIsValidForRow(row.upperBound, col.upperBound), "Invalid range") 414 | precondition(newValue.cols == col.count && newValue.rows == row.count, "Matrix dimensions must agree") 415 | 416 | flat.withUnsafeMutableBufferPointer { dstBuf in 417 | let dstPtr = dstBuf.baseAddress! + row.lowerBound * cols + col.lowerBound 418 | vDSP_mmovD(newValue.flat, dstPtr, 419 | vDSP_Length(col.count), vDSP_Length(row.count), 420 | vDSP_Length(newValue.cols), vDSP_Length(cols)) 421 | } 422 | } 423 | } 424 | 425 | /// Construct new matrix from source using specified extractor. 426 | /// 427 | /// Alternatively, `m[e]` can be executed with `m ?? e` or `slice(m, e)` 428 | /// 429 | /// - Parameters 430 | /// - e: extractor tuple for rows and columns 431 | /// - Returns: extracted matrix 432 | public subscript(_ e: (er: Extractor, ec: Extractor)) -> Matrix { 433 | return slice(self, e) 434 | } 435 | 436 | internal func indexIsValidForRow(_ row: Int, _ col: Int) -> Bool { 437 | return row >= 0 && row < rows && col >= 0 && col < cols 438 | } 439 | } 440 | 441 | public func toRows(_ A: Matrix, _ d: Dim) -> Matrix { 442 | switch d { 443 | case .Row: 444 | return A 445 | case .Column: 446 | return transpose(A) 447 | } 448 | } 449 | 450 | public func toCols(_ A: Matrix, _ d: Dim) -> Matrix { 451 | switch d { 452 | case .Row: 453 | return transpose(A) 454 | case .Column: 455 | return A 456 | } 457 | } 458 | 459 | // MARK: - Matrix manipulation 460 | 461 | /// Vertically stack (top-to-bottom) rows in matrices. 462 | /// 463 | /// Precondition: All matrices need the same number of columns. 464 | /// 465 | /// - Parameters: 466 | /// - ma: [Matrix] (array of matrix) 467 | /// - Returns: new matrix by vertically stacking rows of input matrices 468 | public func vstack(_ ma: [Matrix]) -> Matrix { 469 | precondition(ma.count > 0, "Must have at least 1 input matrix in array") 470 | var m = ma[0] 471 | for i in 1.. Matrix { 486 | precondition(ma.count > 0, "Must have at least 1 input matrix in array") 487 | var m = ma[0] 488 | for i in 1.. Matrix { 503 | return insert(m, rows: Matrix([row]), at: index) 504 | } 505 | 506 | /// Insert rows to matrix at specified position. 507 | /// 508 | /// - Parameters: 509 | /// - m: matrix 510 | /// - rows: rows values to insert represented as matrix 511 | /// - at: index to insert rows to 512 | /// - Returns: new matrix with inserted rows 513 | public func insert(_ m: Matrix, rows: Matrix, at index: Int) -> Matrix { 514 | precondition(rows.cols == m.cols, "Input dimensions must agree") 515 | precondition(index <= m.rows, "Index out of bounds") 516 | 517 | let res = zeros(m.rows + rows.rows, m.cols) 518 | 519 | if (index > 0) { 520 | vDSP_mmovD(m.flat, &res.flat, vDSP_Length(m.cols), vDSP_Length(index), vDSP_Length(m.cols), vDSP_Length(res.cols)) 521 | } 522 | 523 | vDSP_mmovD(rows.flat, &res.flat[index * res.cols], vDSP_Length(m.cols), vDSP_Length(rows.rows), vDSP_Length(m.cols), vDSP_Length(res.cols)) 524 | 525 | if (index < m.rows) { 526 | m.flat.withUnsafeBufferPointer { bufPtr in 527 | let p = bufPtr.baseAddress! + index * m.cols 528 | vDSP_mmovD(p, &res.flat[(index + rows.rows) * res.cols], vDSP_Length(m.cols), vDSP_Length(m.rows - index), vDSP_Length(m.cols), vDSP_Length(res.cols)) 529 | } 530 | } 531 | 532 | return res 533 | } 534 | 535 | /// Append row to matrix. 536 | /// 537 | /// - Parameters: 538 | /// - m: matrix 539 | /// - row: row values to append represented as row matrix 540 | /// - Returns: new matrix with appended row 541 | public func append(_ m: Matrix, row: Matrix) -> Matrix { 542 | precondition(row.cols == m.cols && row.rows == 1, "Input dimensions must agree") 543 | return insert(m, row: row.flat, at: m.rows) 544 | } 545 | 546 | /// Append row to matrix. 547 | /// 548 | /// Alternatively, `append(m, row: row)` can be executed with `m === row`. 549 | /// 550 | /// - Parameters: 551 | /// - m: matrix 552 | /// - row: row values to append 553 | /// - Returns: new matrix with appended row 554 | public func append(_ m: Matrix, row: Vector) -> Matrix { 555 | let r = Matrix([row]) 556 | return append(m, row: r) 557 | } 558 | 559 | /// Append row to matrix constructed from scalar value. 560 | /// 561 | /// Alternatively, `append(m, row: row)` can be executed with `m === row`. 562 | /// 563 | /// - Parameters: 564 | /// - m: matrix 565 | /// - row: row value to append 566 | /// - Returns: new matrix with appended row 567 | public func append(_ m: Matrix, row: Double) -> Matrix { 568 | let r = Vector(repeating: row, count: m.cols) 569 | return append(m, row: r) 570 | } 571 | 572 | /// Append rows to matrix. 573 | /// 574 | /// Alternatively, `append(m, rows: rows)` can be executed with `m === rows`. 575 | /// 576 | /// - Parameters: 577 | /// - m: matrix 578 | /// - rows: rows values to append represented as matrix 579 | /// - Returns: new matrix with appended rows 580 | public func append(_ m: Matrix, rows: Matrix) -> Matrix { 581 | return insert(m, rows: rows, at: m.rows) 582 | } 583 | 584 | /// Append rows to matrix. 585 | /// 586 | /// - Parameters: 587 | /// - m: matrix 588 | /// - rows: rows values to append represented as array of vectors 589 | /// - Returns: new matrix with appended rows 590 | public func append(_ m: Matrix, rows: [Vector]) -> Matrix { 591 | return append(m, rows: Matrix(rows)) 592 | } 593 | 594 | /// Append row to matrix constructed from scalar value. 595 | /// 596 | /// Alternatively, `m === row` can be executed with `append(m, row: row)`. 597 | /// 598 | /// - Parameters: 599 | /// - m: matrix 600 | /// - row: row value to append 601 | /// - Returns: new matrix with appended row 602 | public func === (_ m: Matrix, _ row: Double) -> Matrix { 603 | return append(m, row: row) 604 | } 605 | 606 | /// Append row to matrix. 607 | /// 608 | /// Alternatively, `m === row` can be executed with `append(m, row: row)`. 609 | /// 610 | /// - Parameters: 611 | /// - m: matrix 612 | /// - row: row values to append 613 | /// - Returns: new matrix with appended row 614 | public func === (_ m: Matrix, _ row: Vector) -> Matrix { 615 | return append(m, row: row) 616 | } 617 | 618 | /// Prepend row to matrix. 619 | /// 620 | /// - Parameters: 621 | /// - m: matrix 622 | /// - row: row values to prepend represented as row matrix 623 | /// - Returns: new matrix with prepended row 624 | public func prepend(_ m: Matrix, row: Matrix) -> Matrix { 625 | precondition(row.cols == m.cols && row.rows == 1, "Input dimensions must agree") 626 | return insert(m, row: row.flat, at: 0) 627 | } 628 | 629 | /// Prepend row to matrix. 630 | /// 631 | /// Alternatively, `prepend(m, row: row)` can be executed with `row === m`. 632 | /// 633 | /// - Parameters: 634 | /// - m: matrix 635 | /// - row: row values to prepend 636 | /// - Returns: new matrix with prepended row 637 | public func prepend(_ m: Matrix, row: Vector) -> Matrix { 638 | let r = Matrix([row]) 639 | return prepend(m, row: r) 640 | } 641 | 642 | /// Prepend row to matrix constructed from scalar value. 643 | /// 644 | /// Alternatively, `prepend(m, row: row)` can be executed with `row === m`. 645 | /// 646 | /// - Parameters: 647 | /// - m: matrix 648 | /// - row: row value to prepend 649 | /// - Returns: new matrix with prepended row 650 | public func prepend(_ m: Matrix, row: Double) -> Matrix { 651 | let r = Vector(repeating: row, count: m.cols) 652 | return prepend(m, row: r) 653 | } 654 | 655 | /// Prepend rows to matrix. 656 | /// 657 | /// Alternatively, `prepend(m, rows: rows)` can be executed with `rows === m`. 658 | /// 659 | /// - Parameters: 660 | /// - m: matrix 661 | /// - rows: rows values to prepend represented as matrix 662 | /// - Returns: new matrix with prepended rows 663 | public func prepend(_ m: Matrix, rows: Matrix) -> Matrix { 664 | return insert(m, rows: rows, at: 0) 665 | } 666 | 667 | /// Prepend rows to matrix. 668 | /// 669 | /// - Parameters: 670 | /// - m: matrix 671 | /// - rows: rows values to prepended represented as array of vectors 672 | /// - Returns: new matrix with prepended rows 673 | public func prepend(_ m: Matrix, rows: [Vector]) -> Matrix { 674 | return prepend(m, rows: Matrix(rows)) 675 | } 676 | 677 | /// Prepend row to matrix constructed from scalar value. 678 | /// 679 | /// Alternatively, `row === m` can be executed with `prepend(m, row: row)`. 680 | /// 681 | /// - Parameters: 682 | /// - row: row value to prepend 683 | /// - m: matrix 684 | /// - Returns: new matrix with prepended row 685 | public func === (_ row: Double, _ m: Matrix) -> Matrix { 686 | return prepend(m, row: row) 687 | } 688 | 689 | /// Prepend row to matrix. 690 | /// 691 | /// Alternatively, `row === m` can be executed with `prepend(m, row: row)`. 692 | /// 693 | /// - Parameters: 694 | /// - row: row values to prepend 695 | /// - m: matrix 696 | /// - Returns: new matrix with prepended row 697 | public func === (_ row: Vector, _ m: Matrix) -> Matrix { 698 | return prepend(m, row: row) 699 | } 700 | 701 | /// Horizontally concatenate two matrices. 702 | /// It is similar to appending rhs as rows to lhs 703 | /// 704 | /// Alternatively, `lhs === rhs` can be executed with `append(lhs, rows: rhs)`. 705 | public func === (_ lhs: Matrix, _ rhs: Matrix) -> Matrix { 706 | return append(lhs, rows: rhs) 707 | } 708 | 709 | /// Insert column to matrix at specified position. 710 | /// 711 | /// - Parameters: 712 | /// - m: matrix 713 | /// - cols: column values to insert 714 | /// - at: index to insert column to 715 | /// - Returns: new matrix with inserted column 716 | public func insert(_ m: Matrix, col: Vector, at index: Int) -> Matrix { 717 | return insert(m, cols: Matrix(col), at: index) 718 | } 719 | 720 | /// Insert columns to matrix at specified position. 721 | /// 722 | /// - Parameters: 723 | /// - m: matrix 724 | /// - cols: columns values to insert represented as matrix 725 | /// - at: index to insert columns to 726 | /// - Returns: new matrix with inserted columns 727 | public func insert(_ m: Matrix, cols: Matrix, at index: Int) -> Matrix { 728 | precondition(cols.rows == m.rows, "Input dimensions must agree") 729 | precondition(index <= m.cols && index >= 0, "Index out of bounds") 730 | 731 | let res = zeros(m.rows, m.cols + cols.cols) 732 | 733 | if (index > 0) { 734 | vDSP_mmovD(m.flat, &res.flat, vDSP_Length(index), vDSP_Length(m.rows), vDSP_Length(m.cols), vDSP_Length(res.cols)) 735 | } 736 | 737 | vDSP_mmovD(cols.flat, &res.flat[index], vDSP_Length(cols.cols), vDSP_Length(m.rows), vDSP_Length(cols.cols), vDSP_Length(res.cols)) 738 | 739 | if (index < m.cols) { 740 | m.flat.withUnsafeBufferPointer { bufPtr in 741 | let p = bufPtr.baseAddress! + index 742 | vDSP_mmovD(p, &res.flat[index + cols.cols], vDSP_Length(m.cols - index), vDSP_Length(m.rows), vDSP_Length(m.cols), vDSP_Length(res.cols)) 743 | } 744 | } 745 | 746 | return res 747 | } 748 | 749 | /// Append column to matrix. 750 | /// 751 | /// - Parameters: 752 | /// - m: matrix 753 | /// - col: column values to append represented as column matrix 754 | /// - Returns: new matrix with appended column 755 | public func append(_ m: Matrix, col: Matrix) -> Matrix { 756 | precondition(col.rows == m.rows && col.cols == 1, "Input dimensions must agree") 757 | return insert(m, col: col.flat, at: m.cols) 758 | } 759 | 760 | /// Append column to matrix. 761 | /// 762 | /// Alternatively, `append(m, col: col)` can be executed with `m ||| col`. 763 | /// 764 | /// - Parameters: 765 | /// - m: matrix 766 | /// - col: column values to append 767 | /// - Returns: new matrix with appended column 768 | public func append(_ m: Matrix, col: Vector) -> Matrix { 769 | let c = Matrix(col.count, 1, col) 770 | return append(m, col: c) 771 | } 772 | 773 | /// Append column to matrix constructed from scalar value. 774 | /// 775 | /// Alternatively, `append(m, col: col)` can be executed with `m ||| col`. 776 | /// 777 | /// - Parameters: 778 | /// - m: matrix 779 | /// - col: column value to append 780 | /// - Returns: new matrix with appended column 781 | public func append(_ m: Matrix, col: Double) -> Matrix { 782 | let c = Vector(repeating: col, count: m.rows) 783 | return append(m, col: c) 784 | } 785 | 786 | /// Append columns to matrix. 787 | /// 788 | /// Alternatively, `append(m, cols: cols)` can be executed with `m ||| cols`. 789 | /// 790 | /// - Parameters: 791 | /// - m: matrix 792 | /// - cols: columns values to append represented as matrix 793 | /// - Returns: new matrix with appended columns 794 | public func append(_ m: Matrix, cols: Matrix) -> Matrix { 795 | return insert(m, cols: cols, at: m.cols) 796 | } 797 | 798 | /// Append columns to matrix. 799 | /// 800 | /// - Parameters: 801 | /// - m: matrix 802 | /// - cols: columns values to append represented as array of vectors 803 | /// - Returns: new matrix with appended columns 804 | public func append(_ m: Matrix, cols: [Vector]) -> Matrix { 805 | return append(m, cols: transpose(Matrix(cols))) 806 | } 807 | 808 | /// Append column to matrix constructed from scalar value. 809 | /// 810 | /// Alternatively, `m ||| col` can be executed with `append(m, col: col)`. 811 | /// 812 | /// - Parameters: 813 | /// - m: matrix 814 | /// - col: column value to append 815 | /// - Returns: new matrix with appended column 816 | public func ||| (_ m: Matrix, _ col: Double) -> Matrix { 817 | return append(m, col: col) 818 | } 819 | 820 | /// Append column to matrix. 821 | /// 822 | /// Alternatively, `m ||| col` can be executed with `append(m, col: col)`. 823 | /// 824 | /// - Parameters: 825 | /// - m: matrix 826 | /// - col: column values to append 827 | /// - Returns: new matrix with appended column 828 | public func ||| (_ m: Matrix, _ col: Vector) -> Matrix { 829 | return append(m, col: col) 830 | } 831 | 832 | /// Prepend column to matrix. 833 | /// 834 | /// - Parameters: 835 | /// - m: matrix 836 | /// - col: column values to prepend represented as column matrix 837 | /// - Returns: new matrix with prepended column 838 | public func prepend(_ m: Matrix, col: Matrix) -> Matrix { 839 | precondition(col.rows == m.rows && col.cols == 1, "Input dimensions must agree") 840 | return insert(m, col: col.flat, at: 0) 841 | } 842 | 843 | /// Prepend column to matrix. 844 | /// 845 | /// Alternatively, `prepend(m, col: col)` can be executed with `col ||| m`. 846 | /// 847 | /// - Parameters: 848 | /// - m: matrix 849 | /// - col: column values to prepend 850 | /// - Returns: new matrix with prepended column 851 | public func prepend(_ m: Matrix, col: Vector) -> Matrix { 852 | let c = Matrix(col) 853 | return prepend(m, col: c) 854 | } 855 | 856 | /// Prepend column to matrix constructed from scalar value. 857 | /// 858 | /// Alternatively, `prepend(m, col: col)` can be executed with `col ||| m`. 859 | /// 860 | /// - Parameters: 861 | /// - m: matrix 862 | /// - col: column value to prepend 863 | /// - Returns: new matrix with prepended column 864 | public func prepend(_ m: Matrix, col: Double) -> Matrix { 865 | let c = Vector(repeating: col, count: m.rows) 866 | return prepend(m, col: c) 867 | } 868 | 869 | /// Prepend columns to matrix. 870 | /// 871 | /// Alternatively, `prepend(m, cols: cols)` can be executed with `cols ||| m`. 872 | /// 873 | /// - Parameters: 874 | /// - m: matrix 875 | /// - cols: columns values to prepend represented as matrix 876 | /// - Returns: new matrix with prepended columns 877 | public func prepend(_ m: Matrix, cols: Matrix) -> Matrix { 878 | return insert(m, cols: cols, at: 0) 879 | } 880 | 881 | /// Prepend columns to matrix. 882 | /// 883 | /// - Parameters: 884 | /// - m: matrix 885 | /// - cols: columns values to prepended represented as array of vectors 886 | /// - Returns: new matrix with prepended columns 887 | public func prepend(_ m: Matrix, cols: [Vector]) -> Matrix { 888 | return prepend(m, cols: transpose(Matrix(cols))) 889 | } 890 | 891 | /// Prepend column to matrix constructed from scalar value. 892 | /// 893 | /// Alternatively, `col ||| m` can be executed with `prepend(m, col: col)`. 894 | /// 895 | /// - Parameters: 896 | /// - col: column value to prepend 897 | /// - m: matrix 898 | /// - Returns: new matrix with prepended column 899 | public func ||| (_ col: Double, _ m: Matrix) -> Matrix { 900 | return prepend(m, col: col) 901 | } 902 | 903 | /// Prepend column to matrix. 904 | /// 905 | /// Alternatively, `col ||| m` can be executed with `prepend(m, col: col)`. 906 | /// 907 | /// - Parameters: 908 | /// - col: column values to prepend 909 | /// - m: matrix 910 | /// - Returns: new matrix with prepended column 911 | public func ||| (_ col: Vector, _ m: Matrix) -> Matrix { 912 | return prepend(m, col: col) 913 | } 914 | 915 | /// Vertically concatenate two matrices. 916 | /// It is similar to appending rhs as columns to lhs 917 | /// 918 | /// Alternatively, `lhs ||| rhs` can be executed with `append(lhs, cols: rhs)`. 919 | public func ||| (_ lhs: Matrix, _ rhs: Matrix) -> Matrix { 920 | return append(lhs, cols: rhs) 921 | } 922 | 923 | // MARK: - Slicing 924 | 925 | /// Matrix extractor. 926 | /// 927 | /// - All: Take all rows/columns from source matrix. 928 | /// - Range: Take rows/columns from source matrix with indices 929 | /// starting at `from` with `stride` ending at `to`. 930 | /// - Pos: Take rows/columns from source matrix at specified positions 931 | /// - PosCyc: Take rows/columns from source matrix at specified cyclic positions 932 | /// - Take: Take first `n` rows/columns from source matrix 933 | /// - TakeLast: Take last `n` rows/columns from source matrix 934 | /// - Drop: Drop first `n` rows/columns from source matrix 935 | /// - DropLast: Drop last `n` rows/columns from source matrix 936 | public enum Extractor { 937 | case All 938 | case Range(Int, Int, Int) 939 | case Pos([Int]) 940 | case PosCyc([Int]) 941 | case Take(Int) 942 | case TakeLast(Int) 943 | case Drop(Int) 944 | case DropLast(Int) 945 | } 946 | 947 | /// Construct new matrix from source using specified extractor 948 | /// 949 | /// Alternatively, `slice(m, e)` can be executed with `m ?? e` or `m[e]`. 950 | /// 951 | /// - Parameters 952 | /// - m: source matrix 953 | /// - e: extractor tuple for rows and columns 954 | /// - Returns: extracted matrix 955 | public func slice(_ m: Matrix, _ e: (er: Extractor, ec: Extractor)) -> Matrix { 956 | switch e { 957 | 958 | case (.All, .All): 959 | return m 960 | 961 | case (.Range(let f, _, let t), _) where f < 0 || t >= m.rows, 962 | (_, .Range(let f, _, let t)) where f < 0 || t >= m.cols, 963 | (.Range(let f, _, let t), _) where f >= m.rows || t < 0, 964 | (_, .Range(let f, _, let t)) where f >= m.cols || t < 0: 965 | preconditionFailure("Range out of bounds") 966 | 967 | case (.Take(let n), _) where n < 0 || n >= m.rows, 968 | (_, .Take(let n)) where n < 0 || n >= m.cols, 969 | (.Drop(let n), _) where n < 0 || n >= m.rows, 970 | (_, .Drop(let n)) where n < 0 || n >= m.cols: 971 | preconditionFailure("Range out of bounds") 972 | 973 | case (.All, _): 974 | return slice(m, (.Pos([Int](0.. 0 && pr.filter { $0 < 0 || $0 > m.rows }.count == 0, "Range out of bounds") 1010 | precondition(pc.count > 0 && pc.filter { $0 < 0 || $0 > m.cols }.count == 0, "Range out of bounds") 1011 | return slice(m, pr, pc) 1012 | 1013 | default: 1014 | preconditionFailure("Invalid range") 1015 | } 1016 | } 1017 | 1018 | func slice(_ m: Matrix, _ rr: [Int], _ cr: [Int]) -> Matrix { 1019 | let res = zeros(rr.count, cr.count) 1020 | 1021 | // vgathrD is using 1-based indices 1022 | let _cr = cr.map { vDSP_Length($0 + 1) } 1023 | 1024 | _ = zip(rr, (0.. () in 1025 | var row = zeros(res.cols) 1026 | m.flat.withUnsafeBufferPointer { bufPtr in 1027 | let p = bufPtr.baseAddress! + i * m.cols 1028 | vDSP_vgathrD(p, _cr, 1, &row, 1, vDSP_Length(res.cols)) 1029 | } 1030 | res.flat.withUnsafeMutableBufferPointer { bufPtr in 1031 | let p = bufPtr.baseAddress! + j * res.cols 1032 | vDSP_mmovD(row, p, vDSP_Length(res.cols), vDSP_Length(1), vDSP_Length(res.cols), vDSP_Length(res.cols)) 1033 | } 1034 | } 1035 | 1036 | return res 1037 | } 1038 | 1039 | /// Construct new matrix from source using specified extractor. 1040 | /// 1041 | /// Alternatively, `m ?? e` can be executed with `slice(m, e)` or `m[e]`. 1042 | /// 1043 | /// - Parameters 1044 | /// - m: source matrix 1045 | /// - e: extractor tuple for rows and columns 1046 | /// - Returns: extracted matrix 1047 | public func ?? (_ m: Matrix, _ e: (er: Extractor, ec: Extractor)) -> Matrix { 1048 | return slice(m, e) 1049 | } 1050 | 1051 | // MARK: - Map-reduce 1052 | 1053 | /// Map all elements of source matrix to new matrix using specified function. 1054 | /// 1055 | /// - Parameters 1056 | /// - A: source matrix 1057 | /// - f: mapping function 1058 | /// - Returns: mapped matrix 1059 | public func map(_ A: Matrix, _ f: ((Double) -> Double)) -> Matrix { 1060 | return Matrix(A.rows, A.cols, A.flat.map(f)) 1061 | } 1062 | 1063 | /// Map all elements of source matrix to new matrix using specified function 1064 | /// which operates on vectors. 1065 | /// 1066 | /// - Parameters 1067 | /// - A: source matrix 1068 | /// - f: mapping function 1069 | /// - Returns: mapped matrix 1070 | public func map(_ A: Matrix, _ f: ((Vector) -> Vector)) -> Matrix { 1071 | return matrixFunction(f, A) 1072 | } 1073 | 1074 | /// Perform reduce operation on a matrix using specified function 1075 | /// within the specified dimension. 1076 | /// 1077 | /// - Parameters 1078 | /// - A: source matrix 1079 | /// - f: reducing function 1080 | /// - d: dimesion to apply reduce within (Row by default) 1081 | /// - Returns: vector of reduced values 1082 | public func reduce(_ A: Matrix, _ f: ((Vector) -> Double), _ d: Dim = .Row) -> Vector { 1083 | return aggMatrixFunction(f, A, d) 1084 | } 1085 | 1086 | // MARK: - Sequence 1087 | 1088 | extension Matrix: Sequence { 1089 | public typealias MatrixIterator = AnyIterator> 1090 | /// Iterate through matrix by rows 1091 | public func makeIterator() -> MatrixIterator { 1092 | let end = rows * cols 1093 | var nextRowStart = 0 1094 | 1095 | return AnyIterator { 1096 | if nextRowStart == end { 1097 | return nil 1098 | } 1099 | 1100 | let currentRowStart = nextRowStart 1101 | nextRowStart += self.cols 1102 | 1103 | return self.flat[currentRowStart.. Bool { 1122 | return lhs.rows == rhs.rows && lhs.cols == rhs.cols && lhs.flat ==~ rhs.flat 1123 | } 1124 | 1125 | /// Check if two matrices are not equal using Double value approximate comparison 1126 | public func != (lhs: Matrix, rhs: Matrix) -> Bool { 1127 | return lhs.rows != rhs.rows || lhs.cols != rhs.cols || lhs.flat !=~ rhs.flat 1128 | } 1129 | 1130 | extension Matrix: Comparable {} 1131 | 1132 | /// Check if one matrix is greater than another using Double value approximate comparison 1133 | public func > (lhs: Matrix, rhs: Matrix) -> Bool { 1134 | return lhs.rows == rhs.rows && lhs.cols == rhs.cols && lhs.flat >~ rhs.flat 1135 | } 1136 | 1137 | /// Check if one matrix is less than another using Double value approximate comparison 1138 | public func < (lhs: Matrix, rhs: Matrix) -> Bool { 1139 | return lhs.rows == rhs.rows && lhs.cols == rhs.cols && lhs.flat <~ rhs.flat 1140 | } 1141 | 1142 | /// Check if one matrix is greater than or equal to another using Double value approximate comparison 1143 | public func >= (lhs: Matrix, rhs: Matrix) -> Bool { 1144 | return lhs.rows == rhs.rows && lhs.cols == rhs.cols && lhs.flat >=~ rhs.flat 1145 | } 1146 | 1147 | /// Check if one matrix is less than or equal to another using Double value approximate comparison 1148 | public func <= (lhs: Matrix, rhs: Matrix) -> Bool { 1149 | return lhs.rows == rhs.rows && lhs.cols == rhs.cols && lhs.flat <=~ rhs.flat 1150 | } 1151 | -------------------------------------------------------------------------------- /Sources/MatrixAlgebra.swift: -------------------------------------------------------------------------------- 1 | // MatrixAlgebra.swift 2 | // 3 | // Copyright (c) 2017 Alexander Taraymovich 4 | // All rights reserved. 5 | // 6 | // This software may be modified and distributed under the terms 7 | // of the BSD license. See the LICENSE file for details. 8 | 9 | import Accelerate 10 | 11 | /// Matrix triangular part. 12 | /// 13 | /// - Upper: Upper triangular part. 14 | /// - Lower: Lower triangular part. 15 | public enum Triangle { 16 | case Upper 17 | case Lower 18 | } 19 | 20 | // MARK: - Linear algebra operations on matrices 21 | 22 | /// Compute the trace of a matrix. 23 | /// 24 | /// - Parameters: 25 | /// - A: matrix to compute trace of 26 | /// - Returns: sum of the elements on the main diagonal 27 | public func trace(_ A: Matrix) -> Double { 28 | precondition(A.rows == A.cols, "Matrix dimensions must agree") 29 | return sum((0.. Matrix { 40 | let C: Matrix = zeros(A.cols, A.rows) 41 | vDSP_mtransD(A.flat, 1, &(C.flat), 1, vDSP_Length(A.cols), vDSP_Length(A.rows)) 42 | return C 43 | } 44 | 45 | /// Transpose matrix. 46 | /// 47 | /// Alternatively, `A′` can be executed with `transpose(A)`. 48 | /// 49 | /// - Parameters 50 | /// - A: matrix 51 | /// - Returns: transposed matrix 52 | public postfix func ′ (_ a: Matrix) -> Matrix { 53 | return transpose(a) 54 | } 55 | 56 | /// Perform matrix multiplication. 57 | /// 58 | /// Alternatively, `mtimes(A, B)` can be executed with `A * B`. 59 | /// 60 | /// - Parameters 61 | /// - A: left matrix 62 | /// - B: right matrix 63 | /// - Returns: matrix product of A and B 64 | public func mtimes(_ A: Matrix, _ B: Matrix) -> Matrix { 65 | precondition(A.cols == B.rows, "Matrix dimensions must agree") 66 | let C: Matrix = zeros(A.rows, B.cols) 67 | vDSP_mmulD(A.flat, 1, B.flat, 1, &(C.flat), 1, vDSP_Length(A.rows), vDSP_Length(B.cols), vDSP_Length(A.cols)) 68 | return C 69 | } 70 | 71 | /// Perform matrix multiplication. 72 | /// 73 | /// Alternatively, `A * B` can be executed with `mtimes(A, B)`. 74 | /// 75 | /// - Parameters 76 | /// - A: left matrix 77 | /// - B: right matrix 78 | /// - Returns: matrix product of A and B 79 | public func * (_ A: Matrix, _ B: Matrix) -> Matrix { 80 | return mtimes(A, B) 81 | } 82 | 83 | /// Raise matrix to specified power (integer value). 84 | /// 85 | /// If power is 1, source matrix is returned 86 | /// If power is -1, inverted matrix is returned 87 | /// If power is > 1, continuous matrix product result is returned (eg `mpower(A, 2) = mtimes(A, A)`) 88 | /// If power is < -1, continuous matrix product of inverted matrix result is returned 89 | /// All other values are invalid 90 | /// 91 | /// Alternatively, `mpower(A, p)` can be executed with `A ^ p`. 92 | /// 93 | /// - Parameters 94 | /// - A: matrix 95 | /// - p: power to raise matrix to (integer) 96 | /// - Returns: matrix A raised to power p 97 | public func mpower(_ A: Matrix, _ p: Int) -> Matrix { 98 | precondition(A.cols == A.rows, "Matrix dimensions must agree") 99 | switch p { 100 | case 1: 101 | return Matrix(A) 102 | case -1: 103 | return inv(A) 104 | case _ where p > 1: 105 | var C = Matrix(A) 106 | var p = p 107 | while (p > 1) { 108 | C = mtimes(A, C) 109 | p -= 1 110 | } 111 | return C 112 | case _ where p < -1: 113 | return inv(mpower(A, -p)) 114 | default: 115 | return eye(A.rows, A.cols) 116 | } 117 | } 118 | 119 | /// Raise matrix to specified power (integer value). 120 | /// 121 | /// If power is 1, source matrix is returned 122 | /// If power is -1, inverted matrix is returned 123 | /// If power is > 1, continuous matrix product result is returned (eg `A ^ 2 = mtimes(A, A)`) 124 | /// If power is < -1, continuous matrix product of inverted matrix result is returned 125 | /// All other values are invalid 126 | /// 127 | /// Alternatively, `A ^ p` can be executed with `mpower(A, p)`. 128 | /// 129 | /// - Parameters 130 | /// - A: matrix 131 | /// - p: power to raise matrix to (integer) 132 | /// - Returns: matrix A raised to power p 133 | public func ^ (_ a: Matrix, _ p: Int) -> Matrix { 134 | return mpower(a, p) 135 | } 136 | 137 | /// Compute the inverse of a given square matrix. 138 | /// 139 | /// A precondition error is thrown if the given matrix is singular or algorithm fails to converge. 140 | /// 141 | /// - Parameters: 142 | /// - A: square matrix to invert 143 | /// - Returns: inverse of A matrix 144 | public func inv(_ A: Matrix) -> Matrix { 145 | precondition(A.rows == A.cols, "Matrix dimensions must agree") 146 | let B = Matrix(A) 147 | 148 | var M = __CLPK_integer(A.rows) 149 | var N = M 150 | var LDA = N 151 | var pivot = [__CLPK_integer](repeating: 0, count: Int(N)) 152 | 153 | var wkOpt = __CLPK_doublereal(0.0) 154 | var lWork = __CLPK_integer(-1) 155 | 156 | var error: __CLPK_integer = 0 157 | 158 | dgetrf_(&M, &N, &(B.flat), &LDA, &pivot, &error) 159 | 160 | precondition(error == 0, "Matrix is non invertible") 161 | 162 | /* Query and allocate the optimal workspace */ 163 | 164 | dgetri_(&N, &(B.flat), &LDA, &pivot, &wkOpt, &lWork, &error) 165 | 166 | lWork = __CLPK_integer(wkOpt) 167 | var work = Vector(repeating: 0.0, count: Int(lWork)) 168 | 169 | /* Compute inversed matrix */ 170 | 171 | dgetri_(&N, &(B.flat), &LDA, &pivot, &work, &lWork, &error) 172 | 173 | precondition(error == 0, "Matrix is non invertible") 174 | 175 | return B 176 | } 177 | 178 | /// Compute eigen values and vectors of a given square matrix. 179 | /// 180 | /// A precondition error is thrown if the algorithm fails to converge. 181 | /// 182 | /// - Parameters: 183 | /// - A: square matrix to calculate eigen values and vectors of 184 | /// - Returns: eigenvectors matrix (by rows) and diagonal matrix with eigenvalues on the main diagonal 185 | public func eig(_ A: Matrix) -> (V: Matrix, D: Matrix) { 186 | precondition(A.rows == A.cols, "Matrix dimensions must agree") 187 | 188 | let V = Matrix(A) 189 | 190 | var N = __CLPK_integer(A.rows) 191 | 192 | var LDA = N 193 | 194 | var wkOpt = __CLPK_doublereal(0.0) 195 | var lWork = __CLPK_integer(-1) 196 | 197 | var jobvl: Int8 = 86 // 'V' 198 | var jobvr: Int8 = 86 // 'V' 199 | 200 | var error = __CLPK_integer(0) 201 | 202 | // Real parts of eigenvalues 203 | var wr = Vector(repeating: 0.0, count: Int(N)) 204 | // Imaginary parts of eigenvalues 205 | var wi = Vector(repeating: 0.0, count: Int(N)) 206 | // Left eigenvectors 207 | var vl = [__CLPK_doublereal](repeating: 0.0, count: Int(N * N)) 208 | // Right eigenvectors 209 | var vr = [__CLPK_doublereal](repeating: 0.0, count: Int(N * N)) 210 | 211 | var ldvl = N 212 | var ldvr = N 213 | 214 | /* Query and allocate the optimal workspace */ 215 | 216 | dgeev_(&jobvl, &jobvr, &N, &V.flat, &LDA, &wr, &wi, &vl, &ldvl, &vr, &ldvr, &wkOpt, &lWork, &error) 217 | 218 | lWork = __CLPK_integer(wkOpt) 219 | var work = Vector(repeating: 0.0, count: Int(lWork)) 220 | 221 | /* Compute eigen vectors */ 222 | 223 | dgeev_(&jobvl, &jobvr, &N, &V.flat, &LDA, &wr, &wi, &vl, &ldvl, &vr, &ldvr, &work, &lWork, &error) 224 | 225 | precondition(error == 0, "Failed to compute eigen vectors") 226 | 227 | return (toRows(Matrix(A.rows, A.cols, vl), .Column), diag(wr)) 228 | } 229 | 230 | /// Perform a singular value decomposition of a given matrix. 231 | /// 232 | /// - Parameters: 233 | /// - A: matrix to find singular values of 234 | /// - Returns: matrices U, S, and V such that `A = U * S * transpose(V)` 235 | public func svd(_ A: Matrix) -> (U: Matrix, S: Matrix, V: Matrix) { 236 | /* LAPACK is using column-major order */ 237 | let _A = toCols(A, .Row) 238 | 239 | var jobz: Int8 = 65 // 'A' 240 | 241 | var M = __CLPK_integer(A.rows); 242 | var N = __CLPK_integer(A.cols); 243 | 244 | var LDA = M; 245 | var LDU = M; 246 | var LDVT = N; 247 | 248 | var wkOpt = __CLPK_doublereal(0.0) 249 | var lWork = __CLPK_integer(-1) 250 | var iWork = [__CLPK_integer](repeating: 0, count: Int(8 * min(M, N))) 251 | 252 | var error = __CLPK_integer(0) 253 | 254 | var s = Vector(repeating: 0.0, count: Int(min(M, N))) 255 | let U = Matrix(Int(LDU), Int(M)) 256 | let VT = Matrix(Int(LDVT), Int(N)) 257 | 258 | /* Query and allocate the optimal workspace */ 259 | dgesdd_(&jobz, &M, &N, &_A.flat, &LDA, &s, &U.flat, &LDU, &VT.flat, &LDVT, &wkOpt, &lWork, &iWork, &error) 260 | 261 | lWork = __CLPK_integer(wkOpt) 262 | var work = Vector(repeating: 0.0, count: Int(lWork)) 263 | 264 | /* Compute SVD */ 265 | dgesdd_(&jobz, &M, &N, &_A.flat, &LDA, &s, &U.flat, &LDU, &VT.flat, &LDVT, &work, &lWork, &iWork, &error) 266 | 267 | precondition(error == 0, "Failed to compute SVD") 268 | 269 | return (toRows(U, .Column), diag(Int(M), Int(N), s), VT) 270 | } 271 | 272 | /// Perform a generalized singular value decomposition of 2 given matrices. 273 | /// 274 | /// - Parameters: 275 | /// - A: first matrix 276 | /// - B: second matrix 277 | /// - Returns: matrices U, V, and Q, plus vectors alpha and beta 278 | public func gsvd(_ A: Matrix, _ B: Matrix) -> (U: Matrix, V: Matrix, Q: Matrix, alpha: Vector, beta: Vector, success: Bool) { 279 | /* LAPACK is using column-major order */ 280 | let _A = toCols(A, .Row) 281 | let _B = toCols(B, .Row) 282 | 283 | var jobu:Int8 = Int8(Array("U".utf8).first!) 284 | var jobv:Int8 = Int8(Array("V".utf8).first!) 285 | var jobq:Int8 = Int8(Array("Q".utf8).first!) 286 | 287 | var M = __CLPK_integer(A.rows) 288 | var N = __CLPK_integer(A.cols) 289 | var P = __CLPK_integer(B.rows) 290 | 291 | var LDA = M 292 | var LDB = P 293 | var LDU = M 294 | var LDV = P 295 | var LDQ = N 296 | 297 | let lWork = max(max(Int(3*N),Int(M)),Int(P))+Int(N) 298 | var iWork = [__CLPK_integer](repeating: 0, count: Int(N)) 299 | var work = Vector(repeating: 0.0, count: Int(lWork) * 4) 300 | var error = __CLPK_integer(0) 301 | 302 | var k = __CLPK_integer() 303 | var l = __CLPK_integer() 304 | 305 | let U = Matrix(Int(LDU), Int(M)) 306 | let V = Matrix(Int(LDV), Int(P)) 307 | let Q = Matrix(Int(LDQ), Int(N)) 308 | var alpha = Vector(repeating: 0.0, count: Int(N)) 309 | var beta = Vector(repeating: 0.0, count: Int(N)) 310 | 311 | dggsvd_(&jobu, &jobv, &jobq, &M, &N, &P, &k, &l, &_A.flat, &LDA, &_B.flat, &LDB, &alpha, &beta, &U.flat, &LDU, &V.flat, &LDV, &Q.flat, &LDQ, &work, &iWork, &error) 312 | 313 | return (toRows(U, .Column), toRows(V, .Column), toRows(Q, .Column), Vector(alpha[Int(k)...Int(k+l)-1]), Vector(beta[Int(k)...Int(k+l)-1]), error == 0) 314 | } 315 | 316 | /// Compute the Cholesky factorization of a real symmetric positive definite matrix. 317 | /// 318 | /// A precondition error is thrown if the algorithm fails to converge. 319 | /// 320 | /// - Parameters: 321 | /// - A: square matrix to compute Cholesky factorization of 322 | /// - t: Triangle value (.Upper, .Lower) 323 | /// - Returns: upper triangular matrix U so that `A = U' * U` or 324 | /// lower triangular matrix L so that `A = L * L'` 325 | public func chol(_ A: Matrix, _ t: Triangle = .Upper) -> Matrix { 326 | precondition(A.rows == A.cols, "Matrix dimensions must agree") 327 | 328 | var uplo: Int8 329 | switch t { 330 | case .Upper: 331 | uplo = 85 // 'U' 332 | case .Lower: 333 | uplo = 76 // 'L' 334 | } 335 | 336 | var N = __CLPK_integer(A.rows) 337 | 338 | /* LAPACK is using column-major order */ 339 | var U = toCols(A, .Row) 340 | 341 | var LDA = N 342 | 343 | var error = __CLPK_integer(0) 344 | 345 | /* Compute Cholesky decomposition */ 346 | 347 | dpotrf_(&uplo, &N, &U.flat, &LDA, &error) 348 | 349 | precondition(error == 0, "Failed to compute Cholesky decomposition") 350 | 351 | U = toRows(U, .Column) 352 | 353 | return tri(U, t) 354 | } 355 | 356 | /// Return the upper/lower triangular part of a given matrix. 357 | /// 358 | /// - Parameters: 359 | /// - A: matrix 360 | /// - t: Triangle value (.Upper, .Lower) 361 | /// - Returns: upper/lower triangular part 362 | public func tri(_ A: Matrix, _ t: Triangle) -> Matrix { 363 | let _A = zeros(A.rows, A.cols) 364 | switch t { 365 | case .Upper: 366 | for i in (0.. Double { 391 | precondition(A.rows == A.cols, "Matrix dimensions must agree") 392 | let B = Matrix(A) 393 | 394 | var M = __CLPK_integer(A.rows) 395 | var N = M 396 | var LDA = N 397 | var pivot = [__CLPK_integer](repeating: 0, count: Int(N)) 398 | 399 | var error: __CLPK_integer = 0 400 | 401 | dgetrf_(&M, &N, &(B.flat), &LDA, &pivot, &error) 402 | 403 | var d = 1.0 404 | 405 | for i in (0.. Matrix { 423 | precondition(A.rows > K.rows && A.cols > K.cols, "The kernel matrix needs to be smaller than the matrix it will convolve.") 424 | 425 | let result: [Double] 426 | 427 | if K.rows == 3 { 428 | result = Accelerate.vDSP.convolve(A.flat, rowCount: A._rows, columnCount: A._cols, with3x3Kernel: K.flat) 429 | } else { 430 | result = Accelerate.vDSP.convolve(A.flat, rowCount: A._rows, columnCount: A._cols, withKernel: K.flat, kernelRowCount: K._rows, kernelColumnCount: K._cols) 431 | } 432 | 433 | return Matrix(A._rows, A._cols, result) 434 | } 435 | -------------------------------------------------------------------------------- /Sources/MatrixArithmetic.swift: -------------------------------------------------------------------------------- 1 | // MatrixArithmetic.swift 2 | // 3 | // Copyright (c) 2017 Alexander Taraymovich 4 | // All rights reserved. 5 | // 6 | // This software may be modified and distributed under the terms 7 | // of the BSD license. See the LICENSE file for details. 8 | 9 | import Darwin 10 | 11 | // MARK: - Matrix-Matrix arithmetics 12 | 13 | /// Perform matrix addition. 14 | /// 15 | /// Alternatively, `plus(A, B)` can be executed with `A + B`. 16 | /// 17 | /// - Parameters 18 | /// - A: left matrix 19 | /// - B: right matrix 20 | /// - Returns: elementwise matrix sum of A and B 21 | public func plus(_ A: Matrix, _ B: Matrix) -> Matrix { 22 | return matrixMatrixOperation(plus, A, B) 23 | } 24 | 25 | /// Perform matrix addition. 26 | /// 27 | /// Alternatively, `A + B` can be executed with `plus(A, B)`. 28 | /// 29 | /// - Parameters 30 | /// - A: left matrix 31 | /// - B: right matrix 32 | /// - Returns: elementwise matrix sum of A and B 33 | public func + (_ A: Matrix, _ B: Matrix) -> Matrix { 34 | return plus(A, B) 35 | } 36 | 37 | /// Perform matrix substraction. 38 | /// 39 | /// Alternatively, `minus(A, B)` can be executed with `A - B`. 40 | /// 41 | /// - Parameters 42 | /// - A: left matrix 43 | /// - B: right matrix 44 | /// - Returns: elementwise matrix difference of A and B 45 | public func minus(_ A: Matrix, _ B: Matrix) -> Matrix { 46 | return matrixMatrixOperation(minus, A, B) 47 | } 48 | 49 | /// Perform matrix substraction. 50 | /// 51 | /// Alternatively, `A - B` can be executed with `minus(A, B)`. 52 | /// 53 | /// - Parameters 54 | /// - A: left matrix 55 | /// - B: right matrix 56 | /// - Returns: elementwise matrix difference of A and B 57 | public func - (_ A: Matrix, _ B: Matrix) -> Matrix { 58 | return minus(A, B) 59 | } 60 | 61 | /// Perform matrix multiplication. 62 | /// 63 | /// Alternatively, `times(A, B)` can be executed with `A .* B`. 64 | /// 65 | /// - Parameters 66 | /// - A: left matrix 67 | /// - B: right matrix 68 | /// - Returns: elementwise matrix product of A and B 69 | public func times(_ A: Matrix, _ B: Matrix) -> Matrix { 70 | return matrixMatrixOperation(times, A, B) 71 | } 72 | 73 | /// Perform matrix multiplication. 74 | /// 75 | /// Alternatively, `A .* B` can be executed with `times(A, B)`. 76 | /// 77 | /// - Parameters 78 | /// - A: left matrix 79 | /// - B: right matrix 80 | /// - Returns: elementwise matrix product of A and B 81 | public func .* (_ A: Matrix, _ B: Matrix) -> Matrix { 82 | return times(A, B) 83 | } 84 | 85 | /// Perform matrix right division. 86 | /// 87 | /// Alternatively, `rdivide(A, B)` can be executed with `A ./ B`. 88 | /// 89 | /// - Parameters 90 | /// - A: left matrix 91 | /// - B: right matrix 92 | /// - Returns: result of elementwise division of A by B 93 | public func rdivide(_ A: Matrix, _ B: Matrix) -> Matrix { 94 | return matrixMatrixOperation(rdivide, A, B) 95 | } 96 | 97 | /// Perform matrix right division. 98 | /// 99 | /// Alternatively, `A ./ B` can be executed with `rdivide(A, B)`. 100 | /// 101 | /// - Parameters 102 | /// - A: left matrix 103 | /// - B: right matrix 104 | /// - Returns: result of elementwise division of A by B 105 | public func ./ (_ A: Matrix, _ B: Matrix) -> Matrix { 106 | return rdivide(A, B) 107 | } 108 | 109 | /// Perform matrix left division. 110 | /// 111 | /// Alternatively, `ldivide(A, B)` can be executed with `A ./. B`. 112 | /// 113 | /// - Parameters 114 | /// - A: left matrix 115 | /// - B: right matrix 116 | /// - Returns: result of elementwise division of B by A 117 | public func ldivide(_ A: Matrix, _ B: Matrix) -> Matrix { 118 | return matrixMatrixOperation(ldivide, A, B) 119 | } 120 | 121 | /// Perform matrix left division. 122 | /// 123 | /// Alternatively, `A ./. B` can be executed with `ldivide(A, B)`. 124 | /// 125 | /// - Parameters 126 | /// - A: left matrix 127 | /// - B: right matrix 128 | /// - Returns: result of elementwise division of B by A 129 | public func ./. (_ A: Matrix, _ B: Matrix) -> Matrix { 130 | return ldivide(A, B) 131 | } 132 | 133 | // MARK: - Matrix-Scalar arithmetics 134 | 135 | /// Perform matrix and scalar addition. 136 | /// 137 | /// Scalar value expands to matrix dimensions 138 | /// and elementwise matrix addition is performed. 139 | /// 140 | /// Alternatively, `plus(A, b)` can be executed with `A + b`. 141 | /// 142 | /// - Parameters 143 | /// - A: matrix 144 | /// - b: scalar 145 | /// - Returns: elementwise sum of matrix A and scalar b 146 | public func plus(_ A: Matrix, _ b: Double) -> Matrix { 147 | return matrixScalarOperation(plus, A, b) 148 | } 149 | 150 | /// Perform matrix and scalar addition. 151 | /// 152 | /// Scalar value expands to matrix dimensions 153 | /// and elementwise matrix addition is performed. 154 | /// 155 | /// Alternatively, `A + b` can be executed with `plus(A, b)`. 156 | /// 157 | /// - Parameters 158 | /// - A: matrix 159 | /// - b: scalar 160 | /// - Returns: elementwise sum of matrix A and scalar b 161 | public func + (_ A: Matrix, _ b: Double) -> Matrix { 162 | return plus(A, b) 163 | } 164 | 165 | /// Perform scalar and matrix addition. 166 | /// 167 | /// Scalar value expands to matrix dimensions 168 | /// and elementwise matrix addition is performed. 169 | /// 170 | /// Alternatively, `plus(a, B)` can be executed with `a + B`. 171 | /// 172 | /// - Parameters 173 | /// - a: scalar 174 | /// - B: matrix 175 | /// - Returns: elementwise sum of scalar a and matrix B 176 | public func plus(_ a: Double, _ B: Matrix) -> Matrix { 177 | return invMatrixScalarOperation(plus, a, B) 178 | } 179 | 180 | /// Perform scalar and matrix addition. 181 | /// 182 | /// Scalar value expands to matrix dimensions 183 | /// and elementwise matrix addition is performed. 184 | /// 185 | /// Alternatively, `a + B` can be executed with `plus(a, B)`. 186 | /// 187 | /// - Parameters 188 | /// - a: scalar 189 | /// - B: matrix 190 | /// - Returns: elementwise sum of scalar a and matrix B 191 | public func + (_ a: Double, _ B: Matrix) -> Matrix { 192 | return plus(a, B) 193 | } 194 | 195 | /// Perform matrix and scalar substraction. 196 | /// 197 | /// Scalar value expands to matrix dimensions 198 | /// and elementwise matrix substraction is performed. 199 | /// 200 | /// Alternatively, `minus(A, b)` can be executed with `A - b`. 201 | /// 202 | /// - Parameters 203 | /// - A: matrix 204 | /// - b: scalar 205 | /// - Returns: elementwise difference of matrix A and scalar b 206 | public func minus(_ A: Matrix, _ b: Double) -> Matrix { 207 | return matrixScalarOperation(minus, A, b) 208 | } 209 | 210 | /// Perform matrix and scalar substraction. 211 | /// 212 | /// Scalar value expands to matrix dimensions 213 | /// and elementwise matrix substraction is performed. 214 | /// 215 | /// Alternatively, `A - b` can be executed with `minus(A, b)`. 216 | /// 217 | /// - Parameters 218 | /// - A: matrix 219 | /// - b: scalar 220 | /// - Returns: elementwise difference of matrix A and scalar b 221 | public func - (_ A: Matrix, _ b: Double) -> Matrix { 222 | return minus(A, b) 223 | } 224 | 225 | /// Perform scalar and matrix substraction. 226 | /// 227 | /// Scalar value expands to matrix dimensions 228 | /// and elementwise matrix addition is performed. 229 | /// 230 | /// Alternatively, `minus(a, B)` can be executed with `a - B`. 231 | /// 232 | /// - Parameters 233 | /// - a: scalar 234 | /// - B: matrix 235 | /// - Returns: elementwise difference of scalar a and matrix B 236 | public func minus(_ a: Double, _ B: Matrix) -> Matrix { 237 | return invMatrixScalarOperation(minus, a, B) 238 | } 239 | 240 | /// Perform scalar and matrix substraction. 241 | /// 242 | /// Scalar value expands to matrix dimensions 243 | /// and elementwise matrix addition is performed. 244 | /// 245 | /// Alternatively, `a - B` can be executed with `minus(a, B)`. 246 | /// 247 | /// - Parameters 248 | /// - a: scalar 249 | /// - B: matrix 250 | /// - Returns: elementwise difference of scalar a and matrix B 251 | public func - (_ a: Double, _ B: Matrix) -> Matrix { 252 | return minus(a, B) 253 | } 254 | 255 | /// Perform matrix and scalar multiplication. 256 | /// 257 | /// Scalar value expands to matrix dimensions 258 | /// and elementwise matrix multiplication is performed. 259 | /// 260 | /// Alternatively, `times(A, b)` can be executed with `A .* b`. 261 | /// 262 | /// - Parameters 263 | /// - A: matrix 264 | /// - b: scalar 265 | /// - Returns: elementwise product of matrix A and scalar b 266 | public func times(_ A: Matrix, _ b: Double) -> Matrix { 267 | return matrixScalarOperation(times, A, b) 268 | } 269 | 270 | /// Perform matrix and scalar multiplication. 271 | /// 272 | /// Scalar value expands to matrix dimensions 273 | /// and elementwise matrix multiplication is performed. 274 | /// 275 | /// Alternatively, `A .* b` can be executed with `times(A, b)`. 276 | /// 277 | /// - Parameters 278 | /// - A: matrix 279 | /// - b: scalar 280 | /// - Returns: elementwise product of matrix A and scalar b 281 | public func .* (_ A: Matrix, _ b: Double) -> Matrix { 282 | return times(A, b) 283 | } 284 | 285 | /// Perform scalar and matrix multiplication. 286 | /// 287 | /// Scalar value expands to matrix dimensions 288 | /// and elementwise matrix multiplication is performed. 289 | /// 290 | /// Alternatively, `times(a, B)` can be executed with `a .* B`. 291 | /// 292 | /// - Parameters 293 | /// - a: scalar 294 | /// - B: matrix 295 | /// - Returns: elementwise product of scalar a and matrix B 296 | public func times(_ a: Double, _ B: Matrix) -> Matrix { 297 | return invMatrixScalarOperation(times, a, B) 298 | } 299 | 300 | /// Perform scalar and matrix multiplication. 301 | /// 302 | /// Scalar value expands to matrix dimensions 303 | /// and elementwise matrix multiplication is performed. 304 | /// 305 | /// Alternatively, `a .* B` can be executed with `times(a, B)`. 306 | /// 307 | /// - Parameters 308 | /// - a: scalar 309 | /// - B: matrix 310 | /// - Returns: elementwise product of scalar a and matrix B 311 | public func .* (_ a: Double, _ B: Matrix) -> Matrix { 312 | return times(a, B) 313 | } 314 | 315 | /// Perform matrix and scalar right division. 316 | /// 317 | /// Scalar value expands to matrix dimensions 318 | /// and elementwise matrix right division is performed. 319 | /// 320 | /// Alternatively, `rdivide(A, b)` can be executed with `A ./ b`. 321 | /// 322 | /// - Parameters 323 | /// - A: matrix 324 | /// - b: scalar 325 | /// - Returns: result of elementwise division of matrix A by scalar b 326 | public func rdivide(_ A: Matrix, _ b: Double) -> Matrix { 327 | return matrixScalarOperation(rdivide, A, b) 328 | } 329 | 330 | /// Perform matrix and scalar right division. 331 | /// 332 | /// Scalar value expands to matrix dimensions 333 | /// and elementwise matrix right division is performed. 334 | /// 335 | /// Alternatively, `A ./ b` can be executed with `rdivide(A, b)`. 336 | /// 337 | /// - Parameters 338 | /// - A: matrix 339 | /// - b: scalar 340 | /// - Returns: result of elementwise division of matrix A by scalar b 341 | public func ./ (_ A: Matrix, _ b: Double) -> Matrix { 342 | return rdivide(A, b) 343 | } 344 | 345 | /// Perform scalar and matrix right division. 346 | /// 347 | /// Scalar value expands to matrix dimensions 348 | /// and elementwise matrix right division is performed. 349 | /// 350 | /// Alternatively, `rdivide(a, B)` can be executed with `a ./ B`. 351 | /// 352 | /// - Parameters 353 | /// - a: scalar 354 | /// - B: matrix 355 | /// - Returns: result of elementwise division of scalar a by matrix B 356 | public func rdivide(_ a: Double, _ B: Matrix) -> Matrix { 357 | return invMatrixScalarOperation(rdivide, a, B) 358 | } 359 | 360 | /// Perform scalar and matrix right division. 361 | /// 362 | /// Scalar value expands to matrix dimension 363 | /// and elementwise matrix right division is performed. 364 | /// 365 | /// Alternatively, `a ./ B` can be executed with `rdivide(a, B)`. 366 | /// 367 | /// - Parameters 368 | /// - a: scalar 369 | /// - B: matrix 370 | /// - Returns: result of elementwise division of scalar a by matrix B 371 | public func ./ (_ a: Double, _ B: Matrix) -> Matrix { 372 | return rdivide(a, B) 373 | } 374 | 375 | /// Perform matrix and scalar left division. 376 | /// 377 | /// Scalar value expands to matrix dimensions 378 | /// and elementwise matrix left division is performed. 379 | /// 380 | /// Alternatively, `ldivide(A, b)` can be executed with `A ./. b`. 381 | /// 382 | /// - Parameters 383 | /// - A: matrix 384 | /// - b: scalar 385 | /// - Returns: result of elementwise division of scalar b by matrix A 386 | public func ldivide(_ A: Matrix, _ b: Double) -> Matrix { 387 | return matrixScalarOperation(ldivide, A, b) 388 | } 389 | 390 | /// Perform matrix and scalar left division. 391 | /// 392 | /// Scalar value expands to matrix dimensions 393 | /// and elementwise matrix left division is performed. 394 | /// 395 | /// Alternatively, `A ./. b` can be executed with `ldivide(A, b)`. 396 | /// 397 | /// - Parameters 398 | /// - A: matrix 399 | /// - b: scalar 400 | /// - Returns: result of elementwise division of scalar b by matrix aA 401 | public func ./. (_ A: Matrix, _ b: Double) -> Matrix { 402 | return ldivide(A, b) 403 | } 404 | 405 | /// Perform scalar and matrix left division. 406 | /// 407 | /// Scalar value expands to matrix dimensions 408 | /// and elementwise matrix left division is performed. 409 | /// 410 | /// Alternatively, `ldivide(a, B)` can be executed with `a ./. B`. 411 | /// 412 | /// - Parameters 413 | /// - a: scalar 414 | /// - B: matrix 415 | /// - Returns: result of elementwise division of matrix B by scalar a 416 | public func ldivide(_ a: Double, _ B: Matrix) -> Matrix { 417 | return invMatrixScalarOperation(ldivide, a, B) 418 | } 419 | 420 | /// Perform scalar and matrix left division. 421 | /// 422 | /// Scalar value expands to matrix dimensions 423 | /// and elementwise matrix left division is performed. 424 | /// 425 | /// Alternatively, `a ./. B` can be executed with `ldivide(a, B)`. 426 | /// 427 | /// - Parameters 428 | /// - a: scalar 429 | /// - B: matrix 430 | /// - Returns: result of elementwise division of matrix B by scalar a 431 | public func ./. (_ a: Double, _ B: Matrix) -> Matrix { 432 | return ldivide(a, B) 433 | } 434 | 435 | // MARK: - Matrix-Vector arithmetics 436 | 437 | /// Perform matrix and vector addition. 438 | /// 439 | /// Vector value expands to matrix dimensions (by rows) 440 | /// and elementwise matrix addition is performed. 441 | /// 442 | /// Alternatively, `plus(A, b)` can be executed with `A + b`. 443 | /// 444 | /// - Parameters 445 | /// - A: matrix 446 | /// - b: vector 447 | /// - Returns: elementwise sum of matrix A and vector b 448 | public func plus(_ A: Matrix, _ b: Vector) -> Matrix { 449 | return matrixVectorOperation(plus, A, b) 450 | } 451 | 452 | /// Perform matrix and vector addition. 453 | /// 454 | /// Vector value expands to matrix dimensions (by rows) 455 | /// and elementwise matrix addition is performed. 456 | /// 457 | /// Alternatively, `A + b` can be executed with `plus(A, b)`. 458 | /// 459 | /// - Parameters 460 | /// - A: matrix 461 | /// - b: vector 462 | /// - Returns: elementwise sum of matrix A and vector b 463 | public func + (_ A: Matrix, _ b: Vector) -> Matrix { 464 | return plus(A, b) 465 | } 466 | 467 | /// Perform vector and matrix addition. 468 | /// 469 | /// Vector value expands to matrix dimensions (by rows) 470 | /// and elementwise matrix addition is performed. 471 | /// 472 | /// Alternatively, `plus(a, B)` can be executed with `a + B`. 473 | /// 474 | /// - Parameters 475 | /// - a: vector 476 | /// - B: matrix 477 | /// - Returns: elementwise sum of vector a and matrix B 478 | public func plus(_ a: Vector, _ B: Matrix) -> Matrix { 479 | return invMatrixVectorOperation(plus, a, B) 480 | } 481 | 482 | /// Perform vector and matrix addition. 483 | /// 484 | /// Vector value expands to matrix dimensions (by rows) 485 | /// and elementwise matrix addition is performed. 486 | /// 487 | /// Alternatively, `a + B` can be executed with `plus(a, B)`. 488 | /// 489 | /// - Parameters 490 | /// - a: vector 491 | /// - B: matrix 492 | /// - Returns: elementwise sum of vector a and matrix B 493 | public func + (_ a: Vector, _ B: Matrix) -> Matrix { 494 | return plus(a, B) 495 | } 496 | 497 | /// Perform matrix and vector substraction. 498 | /// 499 | /// Vector value expands to matrix dimensions (by rows) 500 | /// and elementwise matrix addition is performed. 501 | /// 502 | /// Alternatively, `minus(A, b)` can be executed with `A - b`. 503 | /// 504 | /// - Parameters 505 | /// - A: matrix 506 | /// - b: vector 507 | /// - Returns: elementwise difference of matrix A and vector b 508 | public func minus(_ A: Matrix, _ b: Vector) -> Matrix { 509 | return matrixVectorOperation(minus, A, b) 510 | } 511 | 512 | /// Perform matrix and vector substraction. 513 | /// 514 | /// Vector value expands to matrix dimensions (by rows) 515 | /// and elementwise matrix addition is performed. 516 | /// 517 | /// Alternatively, `A - b` can be executed with `minus(A, b)`. 518 | /// 519 | /// - Parameters 520 | /// - A: matrix 521 | /// - b: vector 522 | /// - Returns: elementwise difference of matrix A and vector b 523 | public func - (_ A: Matrix, _ b: Vector) -> Matrix { 524 | return minus(A, b) 525 | } 526 | 527 | /// Perform vector and matrix substraction. 528 | /// 529 | /// Vector value expands to matrix dimensions (by rows) 530 | /// and elementwise matrix addition is performed. 531 | /// 532 | /// Alternatively, `minus(a, B)` can be executed with `a - B`. 533 | /// 534 | /// - Parameters 535 | /// - a: vector 536 | /// - B: matrix 537 | /// - Returns: elementwise difference of vector a and matrix B 538 | public func minus(_ a: Vector, _ B: Matrix) -> Matrix { 539 | return invMatrixVectorOperation(minus, a, B) 540 | } 541 | 542 | /// Perform vector and matrix substraction. 543 | /// 544 | /// Vector value expands to matrix dimensions (by rows) 545 | /// and elementwise matrix addition is performed. 546 | /// 547 | /// Alternatively, `a - B` can be executed with `minus(a, B)`. 548 | /// 549 | /// - Parameters 550 | /// - a: vector 551 | /// - B: matrix 552 | /// - Returns: elementwise difference of vector a and matrix B 553 | public func - (_ a: Vector, _ B: Matrix) -> Matrix { 554 | return minus(a, B) 555 | } 556 | 557 | /// Perform matrix and vector multiplication. 558 | /// 559 | /// Vector value expands to matrix dimensions (by rows) 560 | /// and elementwise matrix addition is performed. 561 | /// 562 | /// Alternatively, `times(A, b)` can be executed with `A .* b`. 563 | /// 564 | /// - Parameters 565 | /// - A: matrix 566 | /// - b: vector 567 | /// - Returns: elementwise product of matrix A and vector b 568 | public func times(_ A: Matrix, _ b: Vector) -> Matrix { 569 | return matrixVectorOperation(times, A, b) 570 | } 571 | 572 | /// Perform matrix and vector multiplication. 573 | /// 574 | /// Vector value expands to matrix dimensions (by rows) 575 | /// and elementwise matrix addition is performed. 576 | /// 577 | /// Alternatively, `A .* b` can be executed with `times(A, b)`. 578 | /// 579 | /// - Parameters 580 | /// - A: matrix 581 | /// - b: vector 582 | /// - Returns: elementwise product of matrix A and vector b 583 | public func .* (_ A: Matrix, _ b: Vector) -> Matrix { 584 | return times(A, b) 585 | } 586 | 587 | /// Perform vector and matrix multiplication. 588 | /// 589 | /// Vector value expands to matrix dimensions (by rows) 590 | /// and elementwise matrix addition is performed. 591 | /// 592 | /// Alternatively, `times(a, B)` can be executed with `a .* B`. 593 | /// 594 | /// - Parameters 595 | /// - a: vector 596 | /// - B: matrix 597 | /// - Returns: elementwise product of vector a and matrix B 598 | public func times(_ a: Vector, _ B: Matrix) -> Matrix { 599 | return invMatrixVectorOperation(times, a, B) 600 | } 601 | 602 | /// Perform vector and matrix multiplication. 603 | /// 604 | /// Vector value expands to matrix dimensions (by rows) 605 | /// and elementwise matrix addition is performed. 606 | /// 607 | /// Alternatively, `a .* B` can be executed with `times(a, B)`. 608 | /// 609 | /// - Parameters 610 | /// - a: vector 611 | /// - B: matrix 612 | /// - Returns: elementwise product of vector a and matrix B 613 | public func .* (_ a: Vector, _ B: Matrix) -> Matrix { 614 | return times(a, B) 615 | } 616 | 617 | /// Perform matrix and vector right division. 618 | /// 619 | /// Vector value expands to matrix dimensions (by rows) 620 | /// and elementwise matrix addition is performed. 621 | /// 622 | /// Alternatively, `rdivide(A, b)` can be executed with `A ./ b`. 623 | /// 624 | /// - Parameters 625 | /// - A: matrix 626 | /// - b: vector 627 | /// - Returns: result of elementwise division of matrix A by vector b 628 | public func rdivide(_ A: Matrix, _ b: Vector) -> Matrix { 629 | return matrixVectorOperation(rdivide, A, b) 630 | } 631 | 632 | /// Perform matrix and vector right division. 633 | /// 634 | /// Vector value expands to matrix dimensions (by rows) 635 | /// and elementwise matrix addition is performed. 636 | /// 637 | /// Alternatively, `A ./ b` can be executed with `rdivide(A, b)`. 638 | /// 639 | /// - Parameters 640 | /// - A: matrix 641 | /// - b: vector 642 | /// - Returns: result of elementwise division of matrix A by vector b 643 | public func ./ (_ A: Matrix, _ b: Vector) -> Matrix { 644 | return rdivide(A, b) 645 | } 646 | 647 | /// Perform vector and matrix right division. 648 | /// 649 | /// Vector value expands to matrix dimensions (by rows) 650 | /// and elementwise matrix addition is performed. 651 | /// 652 | /// Alternatively, `rdivide(a, B)` can be executed with `a ./ B`. 653 | /// 654 | /// - Parameters 655 | /// - a: vector 656 | /// - B: matrix 657 | /// - Returns: result of elementwise division of vector a by matrix B 658 | public func rdivide(_ a: Vector, _ B: Matrix) -> Matrix { 659 | return invMatrixVectorOperation(rdivide, a, B) 660 | } 661 | 662 | /// Perform vector and matrix right division. 663 | /// 664 | /// Vector value expands to matrix dimensions (by rows) 665 | /// and elementwise matrix addition is performed. 666 | /// 667 | /// Alternatively, `a ./ B` can be executed with `rdivide(a, B)`. 668 | /// 669 | /// - Parameters 670 | /// - a: vector 671 | /// - B: matrix 672 | /// - Returns: result of elementwise division of vector a by matrix B 673 | public func ./ (_ a: Vector, _ B: Matrix) -> Matrix { 674 | return rdivide(a, B) 675 | } 676 | 677 | /// Perform matrix and vector left division. 678 | /// 679 | /// Vector value expands to matrix dimensions (by rows) 680 | /// and elementwise matrix addition is performed. 681 | /// 682 | /// Alternatively, `ldivide(A, b)` can be executed with `A ./. b`. 683 | /// 684 | /// - Parameters 685 | /// - A: matrix 686 | /// - b: vector 687 | /// - Returns: result of elementwise division of vector b by matrix A 688 | public func ldivide(_ A: Matrix, _ b: Vector) -> Matrix { 689 | return matrixVectorOperation(ldivide, A, b) 690 | } 691 | 692 | /// Perform matrix and vector left division. 693 | /// 694 | /// Vector value expands to matrix dimensions (by rows) 695 | /// and elementwise matrix addition is performed. 696 | /// 697 | /// Alternatively, `A ./. b` can be executed with `ldivide(A, b)`. 698 | /// 699 | /// - Parameters 700 | /// - A: matrix 701 | /// - b: vector 702 | /// - Returns: result of elementwise division of vector b by matrix A 703 | public func ./. (_ A: Matrix, _ b: Vector) -> Matrix { 704 | return ldivide(A, b) 705 | } 706 | 707 | /// Perform vector and matrix left division. 708 | /// 709 | /// Vector value expands to matrix dimensions (by rows) 710 | /// and elementwise matrix addition is performed. 711 | /// 712 | /// Alternatively, `ldivide(a, B)` can be executed with `a ./. B`. 713 | /// 714 | /// - Parameters 715 | /// - a: vector 716 | /// - B: matrix 717 | /// - Returns: result of elementwise division of matrix B by vector a 718 | public func ldivide(_ a: Vector, _ B: Matrix) -> Matrix { 719 | return invMatrixVectorOperation(ldivide, a, B) 720 | } 721 | 722 | /// Perform vector and matrix left division. 723 | /// 724 | /// Vector value expands to matrix dimensions (by rows) 725 | /// and elementwise matrix addition is performed. 726 | /// 727 | /// Alternatively, `a ./. B` can be executed with `ldivide(a, B)`. 728 | /// 729 | /// - Parameters 730 | /// - a: vector 731 | /// - B: matrix 732 | /// - Returns: result of elementwise division of matrix B by vector a 733 | public func ./. (_ a: Vector, _ B: Matrix) -> Matrix { 734 | return ldivide(a, B) 735 | } 736 | 737 | // MARK: - Sign operations on matrix 738 | 739 | /// Absolute value of matrix. 740 | /// 741 | /// - Parameters 742 | /// - A: matrix 743 | /// - Returns: matrix of absolute values of elements of matrix A 744 | public func abs(_ A: Matrix) -> Matrix { 745 | return matrixFunction(abs, A) 746 | } 747 | 748 | /// Negation of matrix. 749 | /// 750 | /// Alternatively, `uminus(A)` can be executed with `-A`. 751 | /// 752 | /// - Parameters 753 | /// - A: matrix 754 | /// - Returns: matrix of negated values of elements of matrix A 755 | public func uminus(_ A: Matrix) -> Matrix { 756 | return matrixFunction(uminus, A) 757 | } 758 | 759 | /// Negation of matrix. 760 | /// 761 | /// Alternatively, `-A` can be executed with `uminus(A)`. 762 | /// 763 | /// - Parameters 764 | /// - A: matrix 765 | /// - Returns: matrix of negated values of elements of matrix A 766 | public prefix func - (_ A: Matrix) -> Matrix { 767 | return uminus(A) 768 | } 769 | 770 | /// Threshold function on matrix. 771 | /// 772 | /// - Parameters 773 | /// - A: matrix 774 | /// - Returns: matrix with values less than certain value set to 0 775 | /// and keeps the value otherwise 776 | public func thr(_ A: Matrix, _ t: Double) -> Matrix { 777 | return Matrix(A.rows, A.cols, thr(A.flat, t)) 778 | } 779 | -------------------------------------------------------------------------------- /Sources/MatrixExponent.swift: -------------------------------------------------------------------------------- 1 | // MatrixExponent.swift 2 | // 3 | // Copyright (c) 2017 Alexander Taraymovich 4 | // All rights reserved. 5 | // 6 | // This software may be modified and distributed under the terms 7 | // of the BSD license. See the LICENSE file for details. 8 | 9 | // MARK: - Power and exponential functions 10 | 11 | /// Exponentiation function, returning matrix raised to power. 12 | /// 13 | /// Alternatively, `power(A, p)` can be executed with `A .^ p`. 14 | /// 15 | /// Mathematically, `power` would return a complex number when base is negative and 16 | /// power is not an integral value. `power` can’t do that, 17 | /// so instead it signals domain error (returns `±NaN`). 18 | /// 19 | /// - Parameters 20 | /// - A: matrix 21 | /// - p: power to raise matrix to 22 | /// - Returns: elementwise matrix power of a raised to p 23 | public func power(_ A: Matrix, _ b: Double) -> Matrix { 24 | return matrixScalarOperation(power, A, b) 25 | } 26 | 27 | /// Exponentiation function, returning matrix raised to power. 28 | /// 29 | /// Alternatively, `A .^ p` can be executed with `power(A, p)`. 30 | /// 31 | /// Mathematically, `power` would return a complex number when base is negative and 32 | /// power is not an integral value. `power` can’t do that, 33 | /// so instead it signals domain error (returns `±NaN`). 34 | /// 35 | /// - Parameters 36 | /// - A: matrix 37 | /// - p: power to raise matrix to 38 | /// - Returns: elementwise matrix power of a raised to p 39 | public func .^ (_ A: Matrix, _ p: Double) -> Matrix { 40 | return power(A, p) 41 | } 42 | 43 | /// Exponentiation function, returning matrix raised to power of 2. 44 | /// 45 | /// - Parameters 46 | /// - A: matrix 47 | /// - Returns: elementwise vector power of a matrix to power of 2 48 | public func square(_ A: Matrix) -> Matrix { 49 | return matrixFunction(square, A) 50 | } 51 | 52 | /// Exponentiation function, returning square root of matrix. 53 | /// 54 | /// Mathematically, `sqrt` would return a complex number when base is negative. 55 | /// `sqrt` can’t do that, so instead it signals domain error (returns `±NaN`). 56 | /// 57 | /// - Parameters 58 | /// - A: matrix 59 | /// - Returns: elementwise square root of matrix A 60 | public func sqrt(_ A: Matrix) -> Matrix { 61 | return matrixFunction(sqrt, A) 62 | } 63 | 64 | /// Compute `e` (the base of natural logarithms) raised to the power `A`. 65 | /// 66 | /// If the magnitude of the result is too large to be representable, exp 67 | /// signals overflow (returns `Inf`). 68 | /// 69 | /// - Parameters 70 | /// - A: matrix 71 | /// - Returns: elementwise `e` raised to the power of matrix A 72 | public func exp(_ A: Matrix) -> Matrix { 73 | return matrixFunction(exp, A) 74 | } 75 | 76 | /// Compute the natural logarithm of `A` where `exp(log(A))` equals `A`, exactly in 77 | /// mathematics and approximately in C. 78 | /// 79 | /// If x is negative, log signals a domain error (returns `NaN`). If x is zero, it returns 80 | /// negative infinity (`-Inf`); if x is too close to zero, it may signal overflow. 81 | /// 82 | /// - Parameters 83 | /// - A: matrix 84 | /// - Returns: elementwise natural logarithm of matrix A 85 | public func log(_ A: Matrix) -> Matrix { 86 | return matrixFunction(log, A) 87 | } 88 | 89 | /// Return the base-2 logarithm of `A`, where `log2(A) = log(A)/log(2)`. 90 | /// 91 | /// - Parameters 92 | /// - A: matrix 93 | /// - Returns: elementwise base-2 logarithm of matrix A 94 | public func log2(_ A: Matrix) -> Matrix { 95 | return matrixFunction(log2, A) 96 | } 97 | 98 | /// Return the base-10 logarithm of `A`, where `log10(A) = log(A)/log(10)`. 99 | /// 100 | /// - Parameters 101 | /// - A: matrix 102 | /// - Returns: elementwise base-10 logarithm of matrix A 103 | public func log10(_ A: Matrix) -> Matrix { 104 | return matrixFunction(log10, A) 105 | } 106 | -------------------------------------------------------------------------------- /Sources/MatrixLeastSquare.swift: -------------------------------------------------------------------------------- 1 | // MatrixLeastSquare.swift 2 | // 3 | // Created by Brett Larson on 9/10/20. 4 | // Copyright © 2020 Brett Larson. All rights reserved. 5 | // 6 | // This software may be modified and distributed under the terms 7 | // of the BSD license. See the LICENSE file for details. 8 | 9 | import Accelerate 10 | 11 | /// Compute the least squares solution to the overdetermined linear 12 | /// system A*X = B with full rank matrix A. 13 | /// 14 | /// A precondition errors throw for mismatched matrix dimensions 15 | /// or if algorithm fails. 16 | /// 17 | /// Requires M (rows of A) >= N (cols of A). 18 | /// 19 | /// - Parameters: 20 | /// - A: MxN matrix 21 | /// - B: MxK matrix 22 | /// - Returns: 23 | /// - X: NxK solution matrix 24 | /// - R: 1xK residuals 25 | public func lstsqr(_ A: Matrix, _ B: Matrix) -> (X: Matrix, R: Matrix) { 26 | precondition(A.rows >= A.cols, "A Matrix needs rows >= col") 27 | precondition(A.rows == B.rows, "A and B Matrix rows must be equal") 28 | 29 | // Copies of input that can be modified. 30 | // In the Intel example of dgels() I noticed the flat matrix has elements by rows 31 | // and then columns thus the next transposes. I tried changing TRANS to 'T' 32 | // to eliminate the transpose but could not find the correct set of other changes 33 | // to produce the correct solution. 34 | let at = Matrix(A.T) 35 | let bt = Matrix(B.T) 36 | 37 | var TRANS: Int8 = Int8(Array("N".utf8).first!) // No transpose 38 | var M = __CLPK_integer(A.rows) 39 | var N = __CLPK_integer(A.cols) 40 | var NRHS = __CLPK_integer(B.cols) 41 | var LDA = M 42 | var LDB = M 43 | 44 | var wkOpt = __CLPK_doublereal(0.0) 45 | var lWork = __CLPK_integer(-1) 46 | 47 | var info: __CLPK_integer = 0 48 | 49 | /* Query and allocate the optimal workspace */ 50 | dgels_(&TRANS, &M, &N, &NRHS, &(at.flat), &LDA, &(bt.flat), &LDB, &wkOpt, &lWork, &info); 51 | 52 | precondition(info == 0, "Error finding optimal lWork") 53 | lWork = __CLPK_integer(wkOpt) 54 | var work = Vector(repeating: 0.0, count: Int(lWork)) 55 | 56 | /* Compute least square solution */ 57 | dgels_(&TRANS, &M, &N, &NRHS, &(at.flat), &LDA, &(bt.flat), &LDB, &work, &lWork, &info); 58 | 59 | precondition(info == 0, "Error") 60 | 61 | let X = bt.T[0.. 77 | int dgels_(char *__trans, __CLPK_integer *__m, __CLPK_integer *__n, 78 | __CLPK_integer *__nrhs, __CLPK_doublereal *__a, __CLPK_integer *__lda, 79 | __CLPK_doublereal *__b, __CLPK_integer *__ldb, 80 | __CLPK_doublereal *__work, __CLPK_integer *__lwork, 81 | __CLPK_integer *__info) 82 | */ 83 | 84 | /* 85 | Is there a problem defining Vector = [Double] = Array and 86 | passing dereferenced address with & to LAPACK function? See the 87 | description for ContiguousArray at: 88 | 89 | What I do not know is if "Double" is a class or @objc protocol. Will 90 | [Double] have contiguous data that LAPACK requires. 91 | */ 92 | 93 | /* 94 | Reference Intel example of dgels() at: 95 | 96 | This includes a test case with data. 97 | */ 98 | 99 | /* 100 | Reference LAPACK documentation for dgels() at: 101 | 102 | */ 103 | -------------------------------------------------------------------------------- /Sources/MatrixStatistics.swift: -------------------------------------------------------------------------------- 1 | // MatrixStatistics.swift 2 | // 3 | // Copyright (c) 2017 Alexander Taraymovich 4 | // All rights reserved. 5 | // 6 | // This software may be modified and distributed under the terms 7 | // of the BSD license. See the LICENSE file for details. 8 | 9 | import Accelerate 10 | 11 | // MARK: - Statistic functions on matrix 12 | 13 | /// Return vector of largest elements of matrix in a specified dimension. 14 | /// 15 | /// - Parameters 16 | /// - A: matrix 17 | /// - d: dimension (.Column or .Row, defaults to .Row) 18 | public func max(_ A: Matrix, _ d: Dim = .Row) -> Vector { 19 | return aggMatrixFunction(vDSP_maxvD, A, d) 20 | } 21 | 22 | /// Return vector of indices of largest elements of matrix in a specified dimension. 23 | /// 24 | /// - Parameters 25 | /// - A: matrix 26 | /// - d: dimension (.Column or .Row, defaults to .Row) 27 | public func maxi(_ A: Matrix, _ d: Dim = .Row) -> [Int] { 28 | return aggMatrixIFunction(vDSP_maxviD, A, d) 29 | } 30 | 31 | /// Return vector of smallest elements of matrix in a specified dimension. 32 | /// 33 | /// - Parameters 34 | /// - A: matrix 35 | /// - d: dimension (.Column or .Row, defaults to .Row) 36 | public func min(_ A: Matrix, _ d: Dim = .Row) -> Vector { 37 | return aggMatrixFunction(vDSP_minvD, A, d) 38 | } 39 | 40 | /// Return vector of indices of smallest elements of matrix in a specified dimension. 41 | /// 42 | /// - Parameters 43 | /// - A: matrix 44 | /// - d: dimension (.Column or .Row, defaults to .Row) 45 | public func mini(_ A: Matrix, _ d: Dim = .Row) -> [Int] { 46 | return aggMatrixIFunction(vDSP_minviD, A, d) 47 | } 48 | 49 | /// Return vector of mean values of matrix in a specified dimension. 50 | /// 51 | /// - Parameters 52 | /// - A: matrix 53 | /// - d: dimension (.Column or .Row, defaults to .Row) 54 | public func mean(_ A: Matrix, _ d: Dim = .Row) -> Vector { 55 | return aggMatrixFunction(vDSP_meanvD, A, d) 56 | } 57 | 58 | /// Return vector of standard deviation values of matrix in a specified dimension. 59 | /// 60 | /// - Parameters 61 | /// - A: matrix 62 | /// - d: dimension (.Column or .Row, defaults to .Row) 63 | public func std(_ A: Matrix, _ d: Dim = .Row) -> Vector { 64 | return aggMatrixFunction(std, A, d) 65 | } 66 | 67 | /// Return normalized matrix (substract mean value and divide by standard deviation) 68 | /// in dpecified dimension. 69 | /// 70 | /// - Parameters 71 | /// - A: matrix 72 | /// - d: dimension (.Column or .Row, defaults to .Row) 73 | /// - Returns: normalized matrix A 74 | public func normalize(_ A: Matrix, _ d: Dim = .Row) -> Matrix { 75 | switch d { 76 | case .Row: 77 | return Matrix(A.map { normalize(Vector($0)) }) 78 | case .Column: 79 | return Matrix(transpose(A).map { normalize(Vector($0)) }) 80 | } 81 | } 82 | 83 | /// Return vector of sums of values of matrix in a specified dimension. 84 | /// 85 | /// - Parameters 86 | /// - A: matrix 87 | /// - d: dimension (.Column or .Row, defaults to .Row) 88 | public func sum(_ A: Matrix, _ d: Dim = .Row) -> Vector { 89 | return aggMatrixFunction(vDSP_sveD, A, d) 90 | } 91 | 92 | /// Return vector of sums of squared values of matrix in a specified dimension. 93 | /// 94 | /// - Parameters 95 | /// - A: matrix 96 | /// - d: dimension (.Column or .Row, defaults to .Row) 97 | public func sumsq(_ A: Matrix, _ d: Dim = .Row) -> Vector { 98 | return aggMatrixFunction(vDSP_svesqD, A, d) 99 | } 100 | -------------------------------------------------------------------------------- /Sources/MatrixTrigonometry.swift: -------------------------------------------------------------------------------- 1 | // MatrixTrigonometry.swift 2 | // 3 | // Copyright (c) 2017 Alexander Taraymovich 4 | // All rights reserved. 5 | // 6 | // This software may be modified and distributed under the terms 7 | // of the BSD license. See the LICENSE file for details. 8 | 9 | // MARK: - Trigonometric functions on matrix 10 | 11 | /// Return the sine of `A`, where `A` is given in radians and the return value is 12 | /// in the range -1 to 1. 13 | /// 14 | /// - Parameters 15 | /// - A: matrix 16 | /// - Returns: sine of a matrix values 17 | public func sin(_ A: Matrix) -> Matrix { 18 | return matrixFunction(sin, A) 19 | } 20 | 21 | /// Return the cosine of `A`, where `A` is given in radians and the return value is 22 | /// in the range -1 to 1. 23 | /// 24 | /// - Parameters 25 | /// - A: matrix 26 | /// - Returns: cosine of a matrix values 27 | public func cos(_ A: Matrix) -> Matrix { 28 | return matrixFunction(cos, A) 29 | } 30 | 31 | /// Return the tangent of `A`, where `A` is given in radians. 32 | /// 33 | /// Mathematically, the tangent function has singularities at odd multiples of 34 | /// pi/2. If the argument x is too close to one of these singularities, tan 35 | /// will return extremely large value. 36 | /// 37 | /// - Parameters 38 | /// - A: matrix 39 | /// - Returns: tangent of a matrix values 40 | public func tan(_ A: Matrix) -> Matrix { 41 | return matrixFunction(tan, A) 42 | } 43 | 44 | /// Return the arcsine of `A`, where return value is 45 | /// in the range -pi/2 to pi/2. 46 | /// 47 | /// - Parameters 48 | /// - A: matrix 49 | /// - Returns: arcsine of a matrix values 50 | public func asin(_ A: Matrix) -> Matrix { 51 | return matrixFunction(asin, A) 52 | } 53 | 54 | /// Return the arccosine of `A`, where return value is 55 | /// in the range 0 to pi. 56 | /// 57 | /// - Parameters 58 | /// - A: matrix 59 | /// - Returns: arccosine of a matrix values 60 | public func acos(_ A: Matrix) -> Matrix { 61 | return matrixFunction(acos, A) 62 | } 63 | 64 | /// Return the arctangent of `a`. 65 | /// 66 | /// - Parameters 67 | /// - A: matrix 68 | /// - Returns: arctangent of a matrix values 69 | public func atan(_ A: Matrix) -> Matrix { 70 | return matrixFunction(atan, A) 71 | } 72 | -------------------------------------------------------------------------------- /Sources/Numeric.swift: -------------------------------------------------------------------------------- 1 | // Numeric.swift 2 | // 3 | // Copyright (c) 2017 Alexander Taraymovich 4 | // All rights reserved. 5 | // 6 | // This software may be modified and distributed under the terms 7 | // of the BSD license. See the LICENSE file for details. 8 | 9 | import Darwin 10 | 11 | precedencegroup EquivalencePrecedence { 12 | higherThan: ComparisonPrecedence 13 | lowerThan: AdditionPrecedence 14 | } 15 | 16 | infix operator ==~ : EquivalencePrecedence 17 | infix operator !=~ : EquivalencePrecedence 18 | infix operator <=~ : ComparisonPrecedence 19 | infix operator >=~ : ComparisonPrecedence 20 | infix operator <~ : ComparisonPrecedence 21 | infix operator >~ : ComparisonPrecedence 22 | 23 | // MARK: - Double equality and comparison 24 | 25 | let deps: Double = 1e-14 26 | 27 | func ==~ (left: Double, right: Double) -> Bool 28 | { 29 | return fabs(left.distance(to: right)) <= deps 30 | } 31 | 32 | func !=~ (left: Double, right: Double) -> Bool 33 | { 34 | return !(left ==~ right) 35 | } 36 | 37 | func <=~ (left: Double, right: Double) -> Bool 38 | { 39 | return left ==~ right || left <~ right 40 | } 41 | 42 | func >=~ (left: Double, right: Double) -> Bool 43 | { 44 | return left ==~ right || left >~ right 45 | } 46 | 47 | func <~ (left: Double, right: Double) -> Bool 48 | { 49 | return left.distance(to: right) > deps 50 | } 51 | 52 | func >~ (left: Double, right: Double) -> Bool 53 | { 54 | return left.distance(to: right) < -deps 55 | } 56 | 57 | // MARK: Double array comparison 58 | 59 | func ==~ (left: [Double], right: [Double]) -> Bool 60 | { 61 | return left.count == right.count && 62 | zip(left, right).filter { (l, r) in l !=~ r }.count == 0 63 | } 64 | 65 | func !=~ (left: [Double], right: [Double]) -> Bool 66 | { 67 | return left.count != right.count || 68 | zip(left, right).filter { (l, r) in l !=~ r }.count != 0 69 | } 70 | 71 | func >~ (left: [Double], right: [Double]) -> Bool 72 | { 73 | return left.count != right.count || 74 | zip(left, right).filter { (l, r) in l >~ r }.count != 0 75 | } 76 | 77 | func <~ (left: [Double], right: [Double]) -> Bool 78 | { 79 | return left.count != right.count || 80 | zip(left, right).filter { (l, r) in l <~ r }.count != 0 81 | } 82 | 83 | func >=~ (left: [Double], right: [Double]) -> Bool 84 | { 85 | return left.count != right.count || 86 | zip(left, right).filter { (l, r) in l >=~ r }.count != 0 87 | } 88 | 89 | func <=~ (left: [Double], right: [Double]) -> Bool 90 | { 91 | return left.count != right.count || 92 | zip(left, right).filter { (l, r) in l <=~ r }.count != 0 93 | } 94 | 95 | // MARK: Modulo operation 96 | 97 | infix operator %% : MultiplicationPrecedence 98 | 99 | func mod(_ a: Int, _ n: Int) -> Int { 100 | let r = a % abs(n) 101 | return r >= 0 ? r : r + abs(n) 102 | } 103 | 104 | func %% (_ a: Int, _ n: Int) -> Int { 105 | return mod(a, n) 106 | } 107 | -------------------------------------------------------------------------------- /Sources/Operators.swift: -------------------------------------------------------------------------------- 1 | // Operators.swift 2 | // 3 | // Copyright (c) 2017 Alexander Taraymovich 4 | // All rights reserved. 5 | // 6 | // This software may be modified and distributed under the terms 7 | // of the BSD license. See the LICENSE file for details. 8 | 9 | #if !compiler(>=5) 10 | precedencegroup ExponentiationPrecedence { 11 | associativity: right 12 | higherThan: MultiplicationPrecedence 13 | } 14 | infix operator .^ : ExponentiationPrecedence 15 | #endif 16 | 17 | infix operator .* : MultiplicationPrecedence 18 | infix operator ./ : MultiplicationPrecedence 19 | infix operator ./. : MultiplicationPrecedence 20 | 21 | postfix operator ′ 22 | 23 | infix operator ||| : DefaultPrecedence 24 | -------------------------------------------------------------------------------- /Sources/Random.swift: -------------------------------------------------------------------------------- 1 | // Random.swift 2 | // 3 | // Copyright (c) 2017 Alexander Taraymovich 4 | // All rights reserved. 5 | // 6 | // This software may be modified and distributed under the terms 7 | // of the BSD license. See the LICENSE file for details. 8 | 9 | import Darwin 10 | 11 | /// Random value generator helpers 12 | public struct Random { 13 | /// Return random value uniformly distributed on specified interval 14 | /// 15 | /// - Parameters 16 | /// - range: interval 17 | public static func within(_ range: ClosedRange) -> T 18 | where T: FloatingPoint, T: ExpressibleByFloatLiteral { 19 | return (range.upperBound - range.lowerBound) * 20 | (T(arc4random()) / T(UInt32.max)) + range.lowerBound 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/Util.swift: -------------------------------------------------------------------------------- 1 | // Util.swift 2 | // 3 | // Copyright (c) 2017 Alexander Taraymovich 4 | // All rights reserved. 5 | // 6 | // This software may be modified and distributed under the terms 7 | // of the BSD license. See the LICENSE file for details. 8 | 9 | import Accelerate 10 | 11 | // MARK: - Vector operations 12 | 13 | typealias Scalar = Double 14 | 15 | typealias VectorVectorOperation = ((_: UnsafePointer, _: vDSP_Stride, _: UnsafePointer, _: vDSP_Stride, _: UnsafeMutablePointer, _: vDSP_Stride, _: vDSP_Length) -> ()) 16 | 17 | func vectorVectorOperation(_ op: VectorVectorOperation, _ a: Vector, _ b: Vector) -> Vector { 18 | precondition(a.count == b.count, "Vectors must have equal lenghts") 19 | var c = Vector(repeating: 0.0, count: a.count) 20 | op(a, 1, b, 1, &c, 1, vDSP_Length(a.count)) 21 | return c 22 | } 23 | 24 | typealias VectorScalarOperation = ((_: UnsafePointer, _: vDSP_Stride, _: UnsafePointer, _: UnsafeMutablePointer, _: vDSP_Stride, _: vDSP_Length) -> ()) 25 | 26 | func vectorScalarOperation(_ op: VectorScalarOperation, _ a: Vector, _ b: Double) -> Vector { 27 | var c = Vector(repeating: 0.0, count: a.count) 28 | var _b = b 29 | op(a, 1, &_b, &c, 1, vDSP_Length(a.count)) 30 | return c 31 | } 32 | 33 | typealias UnaryVectorOperation = ((_: UnsafePointer, _: vDSP_Stride, _: UnsafeMutablePointer, _: vDSP_Stride, _: vDSP_Length) -> ()) 34 | 35 | func unaryVectorOperation(_ op: UnaryVectorOperation, _ a: Vector) -> Vector { 36 | var c = Vector(repeating: 0.0, count: a.count) 37 | op(a, 1, &c, 1, vDSP_Length(a.count)) 38 | return c 39 | } 40 | 41 | typealias VectorFunction = ((_: UnsafeMutablePointer, _: UnsafePointer, _: UnsafePointer) -> ()) 42 | 43 | func vectorFunction(_ op: VectorFunction, _ a: Vector) -> Vector { 44 | var c = Vector(repeating: 0.0, count: a.count) 45 | var l = Int32(a.count) 46 | op(&c, a, &l) 47 | return c 48 | } 49 | 50 | typealias AggVectorFunction = ((_: UnsafePointer, _: vDSP_Stride, _: UnsafeMutablePointer, _: vDSP_Length) -> ()) 51 | 52 | func aggVectorFunction(_ op: AggVectorFunction, _ a: Vector) -> Double { 53 | return aggVectorFunction(op, a, a.count) 54 | } 55 | 56 | func aggVectorFunction(_ op: AggVectorFunction, _ a: UnsafePointer, _ count: Int) -> Double { 57 | var c = 0.0 58 | op(a, 1, &c, vDSP_Length(count)) 59 | return c 60 | } 61 | 62 | typealias AggVectorIFunction = ((_: UnsafePointer, _: vDSP_Stride, _: UnsafeMutablePointer, _: UnsafeMutablePointer, _: vDSP_Length) -> ()) 63 | 64 | func aggVectorIFunction(_ op: AggVectorIFunction, _ a: Vector) -> Int { 65 | return aggVectorIFunction(op, a, a.count) 66 | } 67 | 68 | func aggVectorIFunction(_ op: AggVectorIFunction, _ a: UnsafePointer, _ count: Int) -> Int { 69 | var c = 0.0 70 | var i: vDSP_Length = 0 71 | op(a, 1, &c, &i, vDSP_Length(count)) 72 | return Int(i) 73 | } 74 | 75 | // MARK: - Matrix operations 76 | 77 | typealias MatrixMatrixOperation = ((_ A: Vector, _ B: Vector) -> Vector) 78 | 79 | func matrixMatrixOperation(_ op: MatrixMatrixOperation, _ A: Matrix, _ B: Matrix) -> Matrix { 80 | precondition(A.rows == B.rows && A.cols == B.cols, "Matrices must be of same dimensions") 81 | return Matrix(A.rows, A.cols, op(A.flat, B.flat)) 82 | } 83 | 84 | typealias MatrixScalarOperation = ((_ A: Vector, _ b: Double) -> Vector) 85 | 86 | func matrixScalarOperation(_ op: MatrixScalarOperation, _ A: Matrix, _ b: Double) -> Matrix { 87 | return Matrix(A.rows, A.cols, op(A.flat, b)) 88 | } 89 | 90 | typealias InvMatrixScalarOperation = ((_ a: Double, _ B: Vector) -> Vector) 91 | 92 | func invMatrixScalarOperation(_ op: InvMatrixScalarOperation, _ a: Double, _ B: Matrix) -> Matrix { 93 | return Matrix(B.rows, B.cols, op(a, B.flat)) 94 | } 95 | 96 | func matrixVectorOperation(_ op: MatrixMatrixOperation, _ A: Matrix, _ b: Vector) -> Matrix { 97 | let B = Matrix((0.. [Double] in 98 | return b 99 | }) 100 | return Matrix(A.rows, A.cols, op(A.flat, B.flat)) 101 | } 102 | 103 | func invMatrixVectorOperation(_ op: MatrixMatrixOperation, _ a: Vector, _ B: Matrix) -> Matrix { 104 | let A = Matrix((0.. [Double] in 105 | return a 106 | }) 107 | return Matrix(B.rows, B.cols, op(A.flat, B.flat)) 108 | } 109 | 110 | typealias MatrixFunction = ((_ A: Vector) -> Vector) 111 | 112 | func matrixFunction(_ op: MatrixFunction, _ A: Matrix) -> Matrix { 113 | return Matrix(A.rows, A.cols, op(A.flat)) 114 | } 115 | 116 | typealias AggMatrixFunction = ((_ A: Vector) -> Double) 117 | 118 | func aggMatrixFunction(_ op: AggMatrixFunction, _ A: Matrix, _ d: Dim) -> Vector { 119 | let _A = toRows(A, d) 120 | var res = zeros(_A.rows) 121 | for i in (0..<_A.rows) { 122 | res[i] = op(_A[row: i]) 123 | } 124 | return res 125 | } 126 | 127 | func aggMatrixFunction(_ op: AggVectorFunction, _ A: Matrix, _ d: Dim) -> Vector { 128 | let _A = toRows(A, d) 129 | var res = zeros(_A.rows) 130 | for i in (0..<_A.rows) { 131 | _A.flat.withUnsafeBufferPointer { bufPtr in 132 | let p = bufPtr.baseAddress! + i * _A.cols 133 | res[i] = aggVectorFunction(op, p, _A.cols) 134 | } 135 | } 136 | return res 137 | } 138 | 139 | func aggMatrixIFunction(_ op: AggVectorIFunction, _ A: Matrix, _ d: Dim) -> [Int] { 140 | let _A = toRows(A, d) 141 | var res = [Int](repeating: 0, count: _A.rows) 142 | for i in (0..<_A.rows) { 143 | _A.flat.withUnsafeBufferPointer { bufPtr in 144 | let p = bufPtr.baseAddress! + i * _A.cols 145 | res[i] = aggVectorIFunction(op, p, _A.cols) 146 | } 147 | } 148 | return res 149 | } 150 | -------------------------------------------------------------------------------- /Sources/Vector.swift: -------------------------------------------------------------------------------- 1 | // Vector.swift 2 | // 3 | // Copyright (c) 2017 Alexander Taraymovich 4 | // All rights reserved. 5 | // 6 | // This software may be modified and distributed under the terms 7 | // of the BSD license. See the LICENSE file for details. 8 | 9 | import Accelerate 10 | 11 | /// Typealiasing of array of Doubles to Vector 12 | /// to make functions more mathematical. 13 | public typealias Vector = [Double] 14 | 15 | // MARK: - One-line creators for vectors 16 | 17 | /// Create a vector of zeros. 18 | /// 19 | /// - Parameters: 20 | /// - count: number of elements 21 | /// - Returns: zeros vector of specified size 22 | public func zeros(_ count: Int) -> Vector { 23 | return Vector(repeating: 0.0, count: count) 24 | } 25 | 26 | /// Create a vector of ones. 27 | /// 28 | /// - Parameters: 29 | /// - count: number of elements 30 | /// - Returns: ones vector of specified size 31 | public func ones(_ count: Int) -> Vector { 32 | return Vector(repeating: 1.0, count: count) 33 | } 34 | 35 | /// Create a vector of uniformly distributed on [0, 1) interval random values. 36 | /// 37 | /// - Parameters: 38 | /// - count: number of elements 39 | /// - Returns: random values vector of specified size 40 | public func rand(_ count: Int) -> Vector { 41 | var iDist = __CLPK_integer(1) 42 | var iSeed = (0..<4).map { _ in __CLPK_integer(Random.within(0.0...4095.0)) } 43 | var n = __CLPK_integer(count) 44 | var x = Vector(repeating: 0.0, count: count) 45 | dlarnv_(&iDist, &iSeed, &n, &x) 46 | return x 47 | } 48 | 49 | /// Create a vector of normally distributed random values. 50 | /// 51 | /// - Parameters: 52 | /// - count: number of elements 53 | /// - Returns: random values vector of specified size 54 | public func randn(_ count: Int) -> Vector { 55 | var iDist = __CLPK_integer(3) 56 | var iSeed = (0..<4).map { _ in __CLPK_integer(Random.within(0.0...4095.0)) } 57 | var n = __CLPK_integer(count) 58 | var x = Vector(repeating: 0.0, count: count) 59 | dlarnv_(&iDist, &iSeed, &n, &x) 60 | return x 61 | } 62 | 63 | // MARK: - Vector comparison 64 | 65 | /// Check if two vectors are equal using Double value approximate comparison 66 | public func == (lhs: Vector, rhs: Vector) -> Bool { 67 | return lhs ==~ rhs 68 | } 69 | 70 | /// Check if two vectors are not equal using Double value approximate comparison 71 | public func != (lhs: Vector, rhs: Vector) -> Bool { 72 | return lhs !=~ rhs 73 | } 74 | 75 | /// Check if one vector is greater than another using Double value approximate comparison 76 | public func > (lhs: Vector, rhs: Vector) -> Bool { 77 | return lhs >~ rhs 78 | } 79 | 80 | /// Check if one vector is less than another using Double value approximate comparison 81 | public func < (lhs: Vector, rhs: Vector) -> Bool { 82 | return lhs <~ rhs 83 | } 84 | 85 | /// Check if one vector is greater than or equal to another using Double value approximate comparison 86 | public func >= (lhs: Vector, rhs: Vector) -> Bool { 87 | return lhs >=~ rhs 88 | } 89 | 90 | /// Check if one vector is less than or equal to another using Double value approximate comparison 91 | public func <= (lhs: Vector, rhs: Vector) -> Bool { 92 | return lhs <=~ rhs 93 | } 94 | -------------------------------------------------------------------------------- /Sources/VectorArithmetic.swift: -------------------------------------------------------------------------------- 1 | // Vector.swift 2 | // 3 | // Copyright (c) 2017 Alexander Taraymovich 4 | // All rights reserved. 5 | // 6 | // This software may be modified and distributed under the terms 7 | // of the BSD license. See the LICENSE file for details. 8 | 9 | import Darwin 10 | import Accelerate 11 | 12 | // MARK: - Arithmetic operations on two vectors 13 | 14 | /// Perform vector addition. 15 | /// 16 | /// Alternatively, `plus(a, b)` can be executed with `a + b`. 17 | /// 18 | /// - Parameters 19 | /// - a: left vector 20 | /// - b: right vector 21 | /// - Returns: elementwise vector sum of a and b 22 | public func plus(_ a: Vector, _ b: Vector) -> Vector { 23 | return vectorVectorOperation(vDSP_vaddD, a, b) 24 | } 25 | 26 | /// Perform vector addition. 27 | /// 28 | /// Alternatively, `a + b` can be executed with `plus(a, b)`. 29 | /// 30 | /// - Parameters 31 | /// - a: left vector 32 | /// - b: right vector 33 | /// - Returns: elementwise vector sum of a and b 34 | public func + (_ a: Vector, _ b: Vector) -> Vector { 35 | return plus(a, b) 36 | } 37 | 38 | /// Perform vector substraction. 39 | /// 40 | /// Alternatively, `minus(a, b)` can be executed with `a - b`. 41 | /// 42 | /// - Parameters 43 | /// - a: left vector 44 | /// - b: right vector 45 | /// - Returns: elementwise vector difference of a and b 46 | public func minus(_ a: Vector, _ b: Vector) -> Vector { 47 | return vectorVectorOperation(vDSP_vsubD, b, a) 48 | } 49 | 50 | /// Perform vector substraction. 51 | /// 52 | /// Alternatively, `a - b` can be executed with `minus(a, b)`. 53 | /// 54 | /// - Parameters 55 | /// - a: left vector 56 | /// - b: right vector 57 | /// - Returns: elementwise vector difference of a and b 58 | public func - (_ a: Vector, _ b: Vector) -> Vector { 59 | return minus(a, b) 60 | } 61 | 62 | /// Perform vector multiplication. 63 | /// 64 | /// Alternatively, `times(a, b)` can be executed with `a .* b`. 65 | /// 66 | /// - Parameters 67 | /// - a: left vector 68 | /// - b: right vector 69 | /// - Returns: elementwise vector product of a and b 70 | public func times(_ a: Vector, _ b: Vector) -> Vector { 71 | return vectorVectorOperation(vDSP_vmulD, a, b) 72 | } 73 | 74 | /// Perform vector multiplication. 75 | /// 76 | /// Alternatively, `a .* b` can be executed with `times(a, b)`. 77 | /// 78 | /// - Parameters 79 | /// - a: left vector 80 | /// - b: right vector 81 | /// - Returns: elementwise vector product of a and b 82 | public func .* (_ a: Vector, _ b: Vector) -> Vector { 83 | return times(a, b) 84 | } 85 | 86 | /// Perform vector right division. 87 | /// 88 | /// Alternatively, `rdivide(a, b)` can be executed with `a ./ b`. 89 | /// 90 | /// - Parameters 91 | /// - a: left vector 92 | /// - b: right vector 93 | /// - Returns: result of elementwise division of a by b 94 | public func rdivide(_ a: Vector, _ b: Vector) -> Vector { 95 | return vectorVectorOperation(vDSP_vdivD, b, a) 96 | } 97 | 98 | /// Perform vector right division. 99 | /// 100 | /// Alternatively, `a ./ b` can be executed with `rdivide(a, b)`. 101 | /// 102 | /// - Parameters 103 | /// - a: left vector 104 | /// - b: right vector 105 | /// - Returns: result of elementwise division of a by b 106 | public func ./ (_ a: Vector, _ b: Vector) -> Vector { 107 | return rdivide(a, b) 108 | } 109 | 110 | /// Perform vector left division. 111 | /// 112 | /// Alternatively, `ldivide(a, b)` can be executed with `a ./. b`. 113 | /// 114 | /// - Parameters 115 | /// - a: left vector 116 | /// - b: right vector 117 | /// - Returns: result of elementwise division of b by a 118 | public func ldivide(_ a: Vector, _ b: Vector) -> Vector { 119 | return vectorVectorOperation(vDSP_vdivD, a, b) 120 | } 121 | 122 | /// Perform vector left division. 123 | /// 124 | /// Alternatively, `a ./. b` can be executed with `ldivide(a, b)`. 125 | /// 126 | /// - Parameters 127 | /// - a: left vector 128 | /// - b: right vector 129 | /// - Returns: result of elementwise division of b by a 130 | public func ./. (_ a: Vector, _ b: Vector) -> Vector { 131 | return ldivide(a, b) 132 | } 133 | 134 | // MARK: - Dot product operations on two vectors 135 | 136 | /// Perform vector dot product operation. 137 | /// 138 | /// Alternatively, `dot(a, b)` can be executed with `a * b`. 139 | /// 140 | /// - Parameters 141 | /// - a: left vector 142 | /// - b: right vector 143 | /// - Returns: dot product of a and b 144 | public func dot(_ a: Vector, _ b: Vector) -> Double { 145 | precondition(a.count == b.count, "Vectors must have equal lenghts") 146 | var c: Double = 0.0 147 | vDSP_dotprD(a, 1, b, 1, &c, vDSP_Length(a.count)) 148 | return c 149 | } 150 | 151 | /// Perform vector dot product operation. 152 | /// 153 | /// Alternatively, `a * b` can be executed with `dot(a, b)`. 154 | /// 155 | /// - Parameters 156 | /// - a: left vector 157 | /// - b: right vector 158 | /// - Returns: dot product of a and b 159 | public func * (_ a: Vector, _ b: Vector) -> Double { 160 | return dot(a, b) 161 | } 162 | 163 | // MARK: - Arithmetic operations on vector and scalar 164 | 165 | /// Perform vector and scalar addition. 166 | /// 167 | /// Scalar value expands to vector dimension 168 | /// and elementwise vector addition is performed. 169 | /// 170 | /// Alternatively, `plus(a, b)` can be executed with `a + b`. 171 | /// 172 | /// - Parameters 173 | /// - a: vector 174 | /// - b: scalar 175 | /// - Returns: elementwise sum of vector a and scalar b 176 | public func plus(_ a: Vector, _ b: Double) -> Vector { 177 | return vectorScalarOperation(vDSP_vsaddD, a, b) 178 | } 179 | 180 | /// Perform vector and scalar addition. 181 | /// 182 | /// Scalar value expands to vector dimension 183 | /// and elementwise vector addition is performed. 184 | /// 185 | /// Alternatively, `a + b` can be executed with `plus(a, b)`. 186 | /// 187 | /// - Parameters 188 | /// - a: vector 189 | /// - b: scalar 190 | /// - Returns: elementwise sum of vector a and scalar b 191 | public func + (_ a: Vector, _ b: Double) -> Vector { 192 | return plus(a, b) 193 | } 194 | 195 | /// Perform scalar and vector addition. 196 | /// 197 | /// Scalar value expands to vector dimension 198 | /// and elementwise vector addition is performed. 199 | /// 200 | /// Alternatively, `plus(a, b)` can be executed with `a + b`. 201 | /// 202 | /// - Parameters 203 | /// - a: scalar 204 | /// - b: vector 205 | /// - Returns: elementwise sum of scalar a and vector b 206 | public func plus(_ a: Double, _ b: Vector) -> Vector { 207 | return plus(b, a) 208 | } 209 | 210 | /// Perform scalar and vector addition. 211 | /// 212 | /// Scalar value expands to vector dimension 213 | /// and elementwise vector addition is performed. 214 | /// 215 | /// Alternatively, `a + b` can be executed with `plus(a, b)`. 216 | /// 217 | /// - Parameters 218 | /// - a: scalar 219 | /// - b: vector 220 | /// - Returns: elementwise sum of scalar a and vector b 221 | public func + (_ a: Double, _ b: Vector) -> Vector { 222 | return plus(a, b) 223 | } 224 | 225 | /// Perform vector and scalar substraction. 226 | /// 227 | /// Scalar value expands to vector dimension 228 | /// and elementwise vector substraction is performed. 229 | /// 230 | /// Alternatively, `minus(a, b)` can be executed with `a - b`. 231 | /// 232 | /// - Parameters 233 | /// - a: vector 234 | /// - b: scalar 235 | /// - Returns: elementwise difference of vector a and scalar b 236 | public func minus(_ a: Vector, _ b: Double) -> Vector { 237 | return vectorScalarOperation(vDSP_vsaddD, a, -b) 238 | } 239 | 240 | /// Perform vector and scalar substraction. 241 | /// 242 | /// Scalar value expands to vector dimension 243 | /// and elementwise vector substraction is performed. 244 | /// 245 | /// Alternatively, `a - b` can be executed with `minus(a, b)`. 246 | /// 247 | /// - Parameters 248 | /// - a: vector 249 | /// - b: scalar 250 | /// - Returns: elementwise difference of vector a and scalar b 251 | public func - (_ a: Vector, _ b: Double) -> Vector { 252 | return minus(a, b) 253 | } 254 | 255 | /// Perform scalar and vector substraction. 256 | /// 257 | /// Scalar value expands to vector dimension 258 | /// and elementwise vector addition is performed. 259 | /// 260 | /// Alternatively, `minus(a, b)` can be executed with `a - b`. 261 | /// 262 | /// - Parameters 263 | /// - a: scalar 264 | /// - b: vector 265 | /// - Returns: elementwise difference of scalar a and vector b 266 | public func minus(_ a: Double, _ b: Vector) -> Vector { 267 | return uminus(minus(b, a)) 268 | } 269 | 270 | /// Perform scalar and vector substraction. 271 | /// 272 | /// Scalar value expands to vector dimension 273 | /// and elementwise vector addition is performed. 274 | /// 275 | /// Alternatively, `a - b` can be executed with `minus(a, b)`. 276 | /// 277 | /// - Parameters 278 | /// - a: scalar 279 | /// - b: vector 280 | /// - Returns: elementwise difference of scalar a and vector b 281 | public func - (_ a: Double, _ b: Vector) -> Vector { 282 | return minus(a, b) 283 | } 284 | 285 | /// Perform vector and scalar multiplication. 286 | /// 287 | /// Scalar value expands to vector dimension 288 | /// and elementwise vector multiplication is performed. 289 | /// 290 | /// Alternatively, `times(a, b)` can be executed with `a .* b`. 291 | /// 292 | /// - Parameters 293 | /// - a: vector 294 | /// - b: scalar 295 | /// - Returns: elementwise product of vector a and scalar b 296 | public func times(_ a: Vector, _ b: Double) -> Vector { 297 | return vectorScalarOperation(vDSP_vsmulD, a, b) 298 | } 299 | 300 | /// Perform vector and scalar multiplication. 301 | /// 302 | /// Scalar value expands to vector dimension 303 | /// and elementwise vector multiplication is performed. 304 | /// 305 | /// Alternatively, `a .* b` can be executed with `times(a, b)`. 306 | /// 307 | /// - Parameters 308 | /// - a: vector 309 | /// - b: scalar 310 | /// - Returns: elementwise product of vector a and scalar b 311 | public func .* (_ a: Vector, _ b: Double) -> Vector { 312 | return times(a, b) 313 | } 314 | 315 | /// Perform scalar and vector multiplication. 316 | /// 317 | /// Scalar value expands to vector dimension 318 | /// and elementwise vector multiplication is performed. 319 | /// 320 | /// Alternatively, `times(a, b)` can be executed with `a .* b`. 321 | /// 322 | /// - Parameters 323 | /// - a: scalar 324 | /// - b: vector 325 | /// - Returns: elementwise product of scalar a and vector b 326 | public func times(_ a: Double, _ b: Vector) -> Vector { 327 | return times(b, a) 328 | } 329 | 330 | /// Perform scalar and vector multiplication. 331 | /// 332 | /// Scalar value expands to vector dimension 333 | /// and elementwise vector multiplication is performed. 334 | /// 335 | /// Alternatively, `a .* b` can be executed with `times(a, b)`. 336 | /// 337 | /// - Parameters 338 | /// - a: scalar 339 | /// - b: vector 340 | /// - Returns: elementwise product of scalar a and vector b 341 | public func .* (_ a: Double, _ b: Vector) -> Vector { 342 | return times(a, b) 343 | } 344 | 345 | /// Perform vector and scalar right division. 346 | /// 347 | /// Scalar value expands to vector dimension 348 | /// and elementwise vector right division is performed. 349 | /// 350 | /// Alternatively, `rdivide(a, b)` can be executed with `a ./ b`. 351 | /// 352 | /// - Parameters 353 | /// - a: vector 354 | /// - b: scalar 355 | /// - Returns: result of elementwise division of vector a by scalar b 356 | public func rdivide(_ a: Vector, _ b: Double) -> Vector { 357 | return vectorScalarOperation(vDSP_vsdivD, a, b) 358 | } 359 | 360 | /// Perform vector and scalar right division. 361 | /// 362 | /// Scalar value expands to vector dimension 363 | /// and elementwise vector right division is performed. 364 | /// 365 | /// Alternatively, `a ./ b` can be executed with `rdivide(a, b)`. 366 | /// 367 | /// - Parameters 368 | /// - a: vector 369 | /// - b: scalar 370 | /// - Returns: result of elementwise division of vector a by scalar b 371 | public func ./ (_ a: Vector, _ b: Double) -> Vector { 372 | return rdivide(a, b) 373 | } 374 | 375 | /// Perform scalar and vector right division. 376 | /// 377 | /// Scalar value expands to vector dimension 378 | /// and elementwise vector right division is performed. 379 | /// 380 | /// Alternatively, `rdivide(a, b)` can be executed with `a ./ b`. 381 | /// 382 | /// - Parameters 383 | /// - a: scalar 384 | /// - b: vector 385 | /// - Returns: result of elementwise division of scalar a by vector b 386 | public func rdivide(_ a: Double, _ b: Vector) -> Vector { 387 | let c = Vector(repeating: a, count: b.count) 388 | return rdivide(c, b) 389 | } 390 | 391 | /// Perform scalar and vector right division. 392 | /// 393 | /// Scalar value expands to vector dimension 394 | /// and elementwise vector right division is performed. 395 | /// 396 | /// Alternatively, `a ./ b` can be executed with `rdivide(a, b)`. 397 | /// 398 | /// - Parameters 399 | /// - a: scalar 400 | /// - b: vector 401 | /// - Returns: result of elementwise division of scalar a by vector b 402 | public func ./ (_ a: Double, _ b: Vector) -> Vector { 403 | return rdivide(a, b) 404 | } 405 | 406 | /// Perform vector and scalar left division. 407 | /// 408 | /// Scalar value expands to vector dimension 409 | /// and elementwise vector left division is performed. 410 | /// 411 | /// Alternatively, `ldivide(a, b)` can be executed with `a ./. b`. 412 | /// 413 | /// - Parameters 414 | /// - a: vector 415 | /// - b: scalar 416 | /// - Returns: result of elementwise division of scalar b by vector a 417 | public func ldivide(_ a: Vector, _ b: Double) -> Vector { 418 | return rdivide(b, a) 419 | } 420 | 421 | /// Perform vector and scalar left division. 422 | /// 423 | /// Scalar value expands to vector dimension 424 | /// and elementwise vector left division is performed. 425 | /// 426 | /// Alternatively, `a ./. b` can be executed with `ldivide(a, b)`. 427 | /// 428 | /// - Parameters 429 | /// - a: vector 430 | /// - b: scalar 431 | /// - Returns: result of elementwise division of scalar b by vector a 432 | public func ./. (_ a: Vector, _ b: Double) -> Vector { 433 | return ldivide(a, b) 434 | } 435 | 436 | /// Perform scalar and vector left division. 437 | /// 438 | /// Scalar value expands to vector dimension 439 | /// and elementwise vector left division is performed. 440 | /// 441 | /// Alternatively, `ldivide(a, b)` can be executed with `a ./. b`. 442 | /// 443 | /// - Parameters 444 | /// - a: scalar 445 | /// - b: vector 446 | /// - Returns: result of elementwise division of vector b by scalar a 447 | public func ldivide(_ a: Double, _ b: Vector) -> Vector { 448 | return rdivide(b, a) 449 | } 450 | 451 | /// Perform scalar and vector left division. 452 | /// 453 | /// Scalar value expands to vector dimension 454 | /// and elementwise vector left division is performed. 455 | /// 456 | /// Alternatively, `a ./. b` can be executed with `ldivide(a, b)`. 457 | /// 458 | /// - Parameters 459 | /// - a: scalar 460 | /// - b: vector 461 | /// - Returns: result of elementwise division of vector b by scalar a 462 | public func ./. (_ a: Double, _ b: Vector) -> Vector { 463 | return ldivide(a, b) 464 | } 465 | 466 | // MARK: - Sign operations on vector 467 | 468 | /// Absolute value of vector. 469 | /// 470 | /// - Parameters 471 | /// - a: vector 472 | /// - Returns: vector of absolute values of elements of vector a 473 | public func abs(_ a: Vector) -> Vector { 474 | return unaryVectorOperation(vDSP_vabsD, a) 475 | } 476 | 477 | /// Negation of vector. 478 | /// 479 | /// Alternatively, `uminus(a)` can be executed with `-a`. 480 | /// 481 | /// - Parameters 482 | /// - a: vector 483 | /// - Returns: vector of negated values of elements of vector a 484 | public func uminus(_ a: Vector) -> Vector { 485 | return unaryVectorOperation(vDSP_vnegD, a) 486 | } 487 | 488 | /// Negation of vector. 489 | /// 490 | /// Alternatively, `-a` can be executed with `uminus(a)`. 491 | /// 492 | /// - Parameters 493 | /// - a: vector 494 | /// - Returns: vector of negated values of elements of vector a 495 | public prefix func - (_ a: Vector) -> Vector { 496 | return uminus(a) 497 | } 498 | 499 | /// Threshold function on vector. 500 | /// 501 | /// - Parameters 502 | /// - a: vector 503 | /// - Returns: vector with values less than certain value set to 0 504 | /// and keeps the value otherwise 505 | public func thr(_ a: Vector, _ t: Double) -> Vector { 506 | var b = zeros(a.count) 507 | var t = t 508 | vDSP_vthrD(a, 1, &t, &b, 1, vDSP_Length(a.count)) 509 | return b 510 | } 511 | -------------------------------------------------------------------------------- /Sources/VectorExponent.swift: -------------------------------------------------------------------------------- 1 | // VectorExponent.swift 2 | // 3 | // Copyright (c) 2017 Alexander Taraymovich 4 | // All rights reserved. 5 | // 6 | // This software may be modified and distributed under the terms 7 | // of the BSD license. See the LICENSE file for details. 8 | 9 | import Accelerate 10 | 11 | // MARK: - Power and exponential operations on vector 12 | 13 | /// Exponentiation function, returning vector raised to power. 14 | /// 15 | /// Alternatively, `power(a, p)` can be executed with `a .^ p`. 16 | /// 17 | /// Mathematically, `power` would return a complex number when base is negative and 18 | /// power is not an integral value. `power` can’t do that, 19 | /// so instead it signals domain error (returns `±NaN`). 20 | /// 21 | /// - Parameters 22 | /// - a: vector 23 | /// - p: power to raise vector to 24 | /// - Returns: elementwise vector power of a raised to p 25 | public func power(_ a: Vector, _ p: Double) -> Vector { 26 | var c = Vector(repeating: 0.0, count: a.count) 27 | var l = Int32(a.count) 28 | var p = p 29 | vvpows(&c, &p, a, &l) 30 | return c 31 | } 32 | 33 | /// Exponentiation function, returning vector raised to power. 34 | /// 35 | /// Alternatively, `a .^ p` can be executed with `power(a, p)`. 36 | /// 37 | /// Mathematically, `power` would return a complex number when base is negative and 38 | /// power is not an integral value. `power` can’t do that, 39 | /// so instead it signals domain error (returns `±NaN`). 40 | /// 41 | /// - Parameters 42 | /// - a: vector 43 | /// - p: power to raise vector to 44 | /// - Returns: elementwise vector power of a raised to p 45 | public func .^ (_ a: Vector, _ p: Double) -> Vector { 46 | return power(a, p) 47 | } 48 | 49 | /// Exponentiation function, returning vector raised to power of 2. 50 | /// 51 | /// - Parameters 52 | /// - a: vector 53 | /// - Returns: elementwise vector power of a raised to power of 2 54 | public func square(_ a: Vector) -> Vector { 55 | return unaryVectorOperation(vDSP_vsqD, a) 56 | } 57 | 58 | /// Exponentiation function, returning square root of vector. 59 | /// 60 | /// Mathematically, `sqrt` would return a complex number when base is negative. 61 | /// `sqrt` can’t do that, so instead it signals domain error (returns `±NaN`). 62 | /// 63 | /// - Parameters 64 | /// - a: vector 65 | /// - Returns: elementwise square root of vector a 66 | public func sqrt(_ a: Vector) -> Vector { 67 | return vectorFunction(vvsqrt, a) 68 | } 69 | 70 | /// Compute `e` (the base of natural logarithms) raised to the power `a`. 71 | /// 72 | /// If the magnitude of the result is too large to be representable, exp 73 | /// signals overflow (returns `Inf`). 74 | /// 75 | /// - Parameters 76 | /// - a: vector 77 | /// - Returns: elementwise `e` raised to the power of vector a 78 | public func exp(_ a: Vector) -> Vector { 79 | return vectorFunction(vvexp, a) 80 | } 81 | 82 | /// Compute the natural logarithm of `a` where `exp(log(a))` equals `a`, exactly in 83 | /// mathematics and approximately in C. 84 | /// 85 | /// If x is negative, log signals a domain error (returns `NaN`). If x is zero, it returns 86 | /// negative infinity (`-Inf`); if x is too close to zero, it may signal overflow. 87 | /// 88 | /// - Parameters 89 | /// - a: vector 90 | /// - Returns: elementwise natural logarithm of vector a 91 | public func log(_ a: Vector) -> Vector { 92 | return vectorFunction(vvlog, a) 93 | } 94 | 95 | /// Return the base-2 logarithm of `a`, where `log2(a) = log(a)/log(2)`. 96 | /// 97 | /// - Parameters 98 | /// - a: vector 99 | /// - Returns: elementwise base-2 logarithm of vector a 100 | public func log2(_ a: Vector) -> Vector { 101 | return vectorFunction(vvlog2, a) 102 | } 103 | 104 | /// Return the base-10 logarithm of `a`, where `log10(a) = log(a)/log(10)`. 105 | /// 106 | /// - Parameters 107 | /// - a: vector 108 | /// - Returns: elementwise base-10 logarithm of vector a 109 | public func log10(_ a: Vector) -> Vector { 110 | return vectorFunction(vvlog10, a) 111 | } 112 | -------------------------------------------------------------------------------- /Sources/VectorStatistics.swift: -------------------------------------------------------------------------------- 1 | // VectorStatistics.swift 2 | // 3 | // Copyright (c) 2017 Alexander Taraymovich 4 | // All rights reserved. 5 | // 6 | // This software may be modified and distributed under the terms 7 | // of the BSD license. See the LICENSE file for details. 8 | 9 | import Accelerate 10 | 11 | // MARK: - Statistical operations on vector 12 | 13 | /// Return the largest element of vector. 14 | /// 15 | /// - Parameters 16 | /// - a: vector 17 | /// - Returns: largest element of vector a 18 | public func max(_ a: Vector) -> Double { 19 | return aggVectorFunction(vDSP_maxvD, a) 20 | } 21 | 22 | /// Return the index of largest element of vector. 23 | /// 24 | /// - Parameters 25 | /// - a: vector 26 | /// - Returns: index of largest element of vector a 27 | public func maxi(_ a: Vector) -> Int { 28 | return aggVectorIFunction(vDSP_maxviD, a) 29 | } 30 | 31 | /// Return the smallest element of vector. 32 | /// 33 | /// - Parameters 34 | /// - a: vector 35 | /// - Returns: smallest element of vector a 36 | public func min(_ a: Vector) -> Double { 37 | return aggVectorFunction(vDSP_minvD, a) 38 | } 39 | 40 | /// Return the index of smallest element of vector. 41 | /// 42 | /// - Parameters 43 | /// - a: vector 44 | /// - Returns: index of smallest element of vector a 45 | public func mini(_ a: Vector) -> Int { 46 | return aggVectorIFunction(vDSP_minviD, a) 47 | } 48 | 49 | /// Return mean (statistically average) value of vector. 50 | /// 51 | /// - Parameters 52 | /// - a: vector 53 | /// - Returns: mean value of vector a 54 | public func mean(_ a: Vector) -> Double { 55 | return aggVectorFunction(vDSP_meanvD, a) 56 | } 57 | 58 | /// Return standard deviation value of vector. 59 | /// 60 | /// - Parameters 61 | /// - a: vector 62 | /// - Returns: standard deviation value of vector a 63 | public func std(_ a: Vector) -> Double { 64 | var m: Double = 0.0 65 | var s: Double = 0.0 66 | var c = Vector(repeating: 0.0, count: a.count) 67 | vDSP_normalizeD(a, 1, &c, 1, &m, &s, vDSP_Length(a.count)) 68 | return s 69 | } 70 | 71 | /// Return normalized vector (substract mean value and divide by standard deviation). 72 | /// 73 | /// - Parameters 74 | /// - a: vector 75 | /// - Returns: normalized vector a 76 | public func normalize(_ a: Vector) -> Vector { 77 | var m: Double = 0.0 78 | var s: Double = 0.0 79 | var c = Vector(repeating: 0.0, count: a.count) 80 | vDSP_normalizeD(a, 1, &c, 1, &m, &s, vDSP_Length(a.count)) 81 | return c 82 | } 83 | 84 | /// Return sum of vector's elements. 85 | /// 86 | /// - Parameters 87 | /// - a: vector 88 | /// - Returns: sum of elements of vector a 89 | public func sum(_ a: Vector) -> Double { 90 | return aggVectorFunction(vDSP_sveD, a) 91 | } 92 | 93 | /// Return sum of vector's squared elements. 94 | /// 95 | /// - Parameters 96 | /// - a: vector 97 | /// - Returns: sum of squared elements of vector a 98 | public func sumsq(_ a: Vector) -> Double { 99 | return aggVectorFunction(vDSP_svesqD, a) 100 | } 101 | -------------------------------------------------------------------------------- /Sources/VectorTrigonometry.swift: -------------------------------------------------------------------------------- 1 | // VectorTrigonometry.swift 2 | // 3 | // Copyright (c) 2017 Alexander Taraymovich 4 | // All rights reserved. 5 | // 6 | // This software may be modified and distributed under the terms 7 | // of the BSD license. See the LICENSE file for details. 8 | 9 | import Accelerate 10 | 11 | // MARK: - Trigonometric operations on vector 12 | 13 | /// Return the sine of `a`, where `a` is given in radians and the return value is 14 | /// in the range -1 to 1. 15 | /// 16 | /// - Parameters 17 | /// - a: vector 18 | /// - Returns: sine of a vector values 19 | public func sin(_ a: Vector) -> Vector { 20 | return vectorFunction(vvsin, a) 21 | } 22 | 23 | /// Return the cosine of `a`, where `a` is given in radians and the return value is 24 | /// in the range -1 to 1. 25 | /// 26 | /// - Parameters 27 | /// - a: vector 28 | /// - Returns: cosine of a vector values 29 | public func cos(_ a: Vector) -> Vector { 30 | return vectorFunction(vvcos, a) 31 | } 32 | 33 | /// Return the tangent of `a`, where `a` is given in radians. 34 | /// 35 | /// Mathematically, the tangent function has singularities at odd multiples of 36 | /// pi/2. If the argument x is too close to one of these singularities, tan 37 | /// will return extremely large value. 38 | /// 39 | /// - Parameters 40 | /// - a: vector 41 | /// - Returns: tangent of a vector values 42 | public func tan(_ a: Vector) -> Vector { 43 | return vectorFunction(vvtan, a) 44 | } 45 | 46 | /// Return the arcsine of `a`, where return value is 47 | /// in the range -pi/2 to pi/2. 48 | /// 49 | /// - Parameters 50 | /// - a: vector 51 | /// - Returns: arcsine of a vector values 52 | public func asin(_ a: Vector) -> Vector { 53 | return vectorFunction(vvasin, a) 54 | } 55 | 56 | /// Return the arccosine of `a`, where return value is 57 | /// in the range 0 to pi. 58 | /// 59 | /// - Parameters 60 | /// - a: vector 61 | /// - Returns: arccosine of a vector values 62 | public func acos(_ a: Vector) -> Vector { 63 | return vectorFunction(vvacos, a) 64 | } 65 | 66 | /// Return the arctangent of `a`. 67 | /// 68 | /// - Parameters 69 | /// - a: vector 70 | /// - Returns: arctangent of a vector values 71 | public func atan(_ a: Vector) -> Vector { 72 | return vectorFunction(vvatan, a) 73 | } 74 | -------------------------------------------------------------------------------- /Tests/MatrixTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MatrixTests.swift 3 | // LASwift 4 | // 5 | // Created by Alexander Taraymovich on 28/02/2017. 6 | // Copyright © 2017 CocoaPods. All rights reserved. 7 | // 8 | 9 | import Darwin 10 | 11 | import Quick 12 | import Nimble 13 | import LASwift 14 | 15 | class MatrixSpec: QuickSpec { 16 | override func spec() { 17 | describe("Matrix construction tests") { 18 | let count = 10 19 | 20 | let m1 = ones(count, count+1) 21 | 22 | it("ones matrix has correct dimensions") { 23 | expect(m1.rows) == count 24 | expect(m1.cols) == count+1 25 | } 26 | 27 | it("ones matrix all ones") { 28 | for i in 0.. m2 319 | expect(m2) < m1 320 | } 321 | it("greater/less than or equal") { 322 | let m1 = Matrix([[11.0, 12.0], [13.0, 14.0]]) 323 | let m2 = Matrix([[1.0, 2.0], [3.0, 4.0]]) 324 | let m3 = Matrix([[1.0, 2.0], [3.0, 4.0]]) 325 | expect(m1) >= m2 326 | expect(m2) <= m1 327 | expect(m2) >= m3 328 | expect(m2) <= m3 329 | } 330 | } 331 | 332 | describe("Matrix subscript") { 333 | it("[i,j]") { 334 | let m1 = Matrix([[1.0, 2.0], [3.0, 4.0]]) 335 | expect(m1[1, 1]) == 4.0 336 | m1[1, 0] = 10.0 337 | expect(m1[1, 0]) == 10.0 338 | } 339 | it("index") { 340 | let m1 = Matrix([[1.0, 2.0], [3.0, 4.0]]) 341 | expect(m1[3]) == 4.0 342 | m1[2] = 10.0 343 | expect(m1[2]) == 10.0 344 | expect(m1[1, 0]) == 10.0 345 | } 346 | it("row") { 347 | let m1 = Matrix([[1.0, 2.0], [3.0, 4.0]]) 348 | expect(m1[row: 1]) == [3.0, 4.0] 349 | m1[row: 0] = [10.0, 20.0] 350 | expect(m1[row: 0]) == [10.0, 20.0] 351 | expect(m1[0, 0]) == 10.0 352 | expect(m1[0, 1]) == 20.0 353 | } 354 | it("column") { 355 | let m1 = Matrix([[1.0, 2.0], [3.0, 4.0]]) 356 | expect(m1[col: 1]) == [2.0, 4.0] 357 | m1[col: 0] = [10.0, 30.0] 358 | expect(m1[col: 0]) == [10.0, 30.0] 359 | expect(m1[0, 0]) == 10.0 360 | expect(m1[1, 0]) == 30.0 361 | } 362 | } 363 | 364 | describe("Matrix range-based subscript") { 365 | it("[i,j] -> Matrix") { 366 | let m1 = Matrix([[1.0, 2.0, 3.0], 367 | [4.0, 5.0, 6.0], 368 | [7.0, 8.0, 9.0]]) 369 | let m2 = Matrix([[10.0, 11.0], 370 | [12.0, 13.0]]) 371 | expect(m1[0, 1].flat) == [2.0, 3.0, 5.0, 6.0, 8.0, 9.0] 372 | m1[1, 0] = m2 373 | expect(m1[1..<3, 0...1].flat) == [10.0, 11.0, 12.0, 13.0] 374 | } 375 | it("closed") { 376 | let m1 = Matrix([[1.0, 2.0, 3.0], 377 | [4.0, 5.0, 6.0], 378 | [7.0, 8.0, 9.0]]) 379 | let m2 = Matrix([[10.0, 11.0], 380 | [12.0, 13.0]]) 381 | expect(m1[1...2, 0...1].flat) == [4.0, 5.0, 7.0, 8.0] 382 | m1[0...1, 1...2] = m2 383 | expect(m1.flat) == [1.0, 10.0, 11.0, 4.0, 12.0, 13.0, 7.0, 8.0, 9.0] 384 | } 385 | it("open") { 386 | let m1 = Matrix([[1.0, 2.0, 3.0], 387 | [4.0, 5.0, 6.0], 388 | [7.0, 8.0, 9.0]]) 389 | let m2 = Matrix([[10.0, 11.0], 390 | [12.0, 13.0]]) 391 | expect(m1[1..<3, 0..<2].flat) == [4.0, 5.0, 7.0, 8.0] 392 | m1[0..<2, 1..<3] = m2 393 | expect(m1.flat) == [1.0, 10.0, 11.0, 4.0, 12.0, 13.0, 7.0, 8.0, 9.0] 394 | } 395 | it("partial") { 396 | let m1 = Matrix([[1.0, 2.0, 3.0], 397 | [4.0, 5.0, 6.0], 398 | [7.0, 8.0, 9.0]]) 399 | let m2 = Matrix([[10.0, 11.0], 400 | [12.0, 13.0]]) 401 | expect(m1[1..., ..<2].flat) == [4.0, 5.0, 7.0, 8.0] 402 | m1[1..., 1...] = m2 403 | expect(m1.flat) == [1.0, 2.0, 3.0, 4.0, 10.0, 11.0, 7.0, 12.0, 13.0] 404 | } 405 | it("unbounded") { 406 | let m1 = Matrix([[1.0, 2.0, 3.0], 407 | [4.0, 5.0, 6.0], 408 | [7.0, 8.0, 9.0]]) 409 | let m2 = Matrix([[10.0, 11.0, 12.0]]) 410 | expect(m1[..., 2...2].flat) == [3.0, 6.0, 9.0] 411 | m1[1...1, ...] = m2 412 | expect(m1.flat) == [1.0, 2.0, 3.0, 10.0, 11.0, 12.0, 7.0, 8.0, 9.0] 413 | } 414 | } 415 | 416 | describe("Matrix map/reduce") { 417 | it("map") { 418 | let m1 = Matrix([[1.0, 2.0], [3.0, 4.0]]) 419 | let res = Matrix([[1.0, 4.0], [9.0, 16.0]]) 420 | expect(map(m1, { $0 * $0 })) == res 421 | } 422 | it("map vec") { 423 | let m1 = Matrix([[1.0, 2.0], [3.0, 4.0]]) 424 | let res = Matrix([[1.0, 4.0], [9.0, 16.0]]) 425 | expect(map(m1, square)) == res 426 | } 427 | it("reduce rows") { 428 | let m1 = Matrix([[1.0, 2.0], [3.0, 4.0]]) 429 | let res = [3.0, 7.0] 430 | expect(reduce(m1, sum)) == res 431 | expect(reduce(m1, sum, .Row)) == res 432 | } 433 | it("reduce cols") { 434 | let m1 = Matrix([[1.0, 2.0], [3.0, 4.0]]) 435 | let res = [4.0, 6.0] 436 | expect(reduce(m1, sum, .Column)) == res 437 | } 438 | } 439 | 440 | describe("Matrix power and exponential tests") { 441 | it("power") { 442 | let m1 = Matrix([[1.0, 2.0], [3.0, 4.0]]) 443 | let p = 3.0 444 | let res = Matrix([[1.0, 8.0], [27.0, 64.0]]) 445 | expect(m1 .^ p) == res 446 | } 447 | it("square") { 448 | let m1 = Matrix([[1.0, 2.0], [3.0, 4.0]]) 449 | let res = Matrix([[1.0, 4.0], [9.0, 16.0]]) 450 | expect(square(m1)) == res 451 | expect(m1 .^ 2.0) == res 452 | } 453 | it("sqrt") { 454 | let m1 = Matrix([[1.0, 2.0], [3.0, 4.0]]) 455 | let res = Matrix([[sqrt(1.0), sqrt(2.0)], [sqrt(3.0), sqrt(4.0)]]) 456 | expect(sqrt(m1)) == res 457 | expect(m1 .^ 0.5) == res 458 | } 459 | it("exp") { 460 | let m1 = Matrix([[1.0, 2.0], [3.0, 4.0]]) 461 | let res = Matrix([[exp(1.0), exp(2.0)], [exp(3.0), exp(4.0)]]) 462 | expect(exp(m1)) == res 463 | } 464 | it("log") { 465 | let m1 = Matrix([[1.0, 2.0], [3.0, 4.0]]) 466 | let res = Matrix([[log(1.0), log(2.0)], [log(3.0), log(4.0)]]) 467 | expect(log(m1)) == res 468 | } 469 | it("log10") { 470 | let m1 = Matrix([[1.0, 2.0], [3.0, 4.0]]) 471 | let res = Matrix([[log10(1.0), log10(2.0)], [log10(3.0), log10(4.0)]]) 472 | expect(log10(m1)) == res 473 | } 474 | it("log2") { 475 | let m1 = Matrix([[1.0, 2.0], [3.0, 4.0]]) 476 | let res = Matrix([[log2(1.0), log2(2.0)], [log2(3.0), log2(4.0)]]) 477 | expect(log2(m1)) == res 478 | } 479 | } 480 | 481 | describe("Matrix trigonometric tests") { 482 | it("sin") { 483 | let m1 = Matrix([[1.0, 2.0], [3.0, 4.0]]) 484 | let res = Matrix([[sin(1.0), sin(2.0)], [sin(3.0), sin(4.0)]]) 485 | expect(sin(m1)) == res 486 | } 487 | it("cos") { 488 | let m1 = Matrix([[1.0, 2.0], [3.0, 4.0]]) 489 | let res = Matrix([[cos(1.0), cos(2.0)], [cos(3.0), cos(4.0)]]) 490 | expect(cos(m1)) == res 491 | } 492 | it("tan") { 493 | let m1 = Matrix([[1.0, 2.0], [3.0, 4.0]]) 494 | let res = Matrix([[tan(1.0), tan(2.0)], [tan(3.0), tan(4.0)]]) 495 | expect(tan(m1)) == res 496 | } 497 | it("asin") { 498 | let m1 = Matrix([[0.5, -0.5], [1.0, -1.0]]) 499 | let res = Matrix([[asin(0.5), asin(-0.5)], [asin(1.0), asin(-1.0)]]) 500 | expect(asin(m1)) == res 501 | } 502 | it("acos") { 503 | let m1 = Matrix([[0.5, -0.5], [1.0, -1.0]]) 504 | let res = Matrix([[acos(0.5), acos(-0.5)], [acos(1.0), acos(-1.0)]]) 505 | expect(acos(m1)) == res 506 | } 507 | it("atan") { 508 | let m1 = Matrix([[1.0, 2.0], [3.0, 4.0]]) 509 | let res = Matrix([[atan(1.0), atan(2.0)], [atan(3.0), atan(4.0)]]) 510 | expect(atan(m1)) == res 511 | } 512 | } 513 | 514 | describe("Matrix statistics tests") { 515 | it("max") { 516 | let m1 = Matrix([[1.0, 4.0], [3.0, 2.0]]) 517 | let res1 = [4.0, 3.0] 518 | let res2 = [3.0, 4.0] 519 | expect(max(m1)).to(beCloseTo(res1)) 520 | expect(max(m1, .Row)).to(beCloseTo(res1)) 521 | expect(max(m1, .Column)).to(beCloseTo(res2)) 522 | } 523 | it("maxi") { 524 | let m1 = Matrix([[1.0, 4.0], [3.0, 2.0]]) 525 | let res1 = [1, 0] 526 | let res2 = [1, 0] 527 | expect(maxi(m1)) == res1 528 | expect(maxi(m1, .Row)) == res1 529 | expect(maxi(m1, .Column)) == res2 530 | } 531 | it("min") { 532 | let m1 = Matrix([[1.0, 4.0], [3.0, 2.0]]) 533 | let res1 = [1.0, 2.0] 534 | let res2 = [1.0, 2.0] 535 | expect(min(m1)).to(beCloseTo(res1)) 536 | expect(min(m1, .Row)).to(beCloseTo(res1)) 537 | expect(min(m1, .Column)).to(beCloseTo(res2)) 538 | } 539 | it("mini") { 540 | let m1 = Matrix([[1.0, 4.0], [3.0, 2.0]]) 541 | let res1 = [0, 1] 542 | let res2 = [0, 1] 543 | expect(mini(m1)) == res1 544 | expect(mini(m1, .Row)) == res1 545 | expect(mini(m1, .Column)) == res2 546 | } 547 | it("mean") { 548 | let m1 = Matrix([[1.0, 4.0], [3.0, 2.0]]) 549 | let res1 = [2.5, 2.5] 550 | let res2 = [2.0, 3.0] 551 | expect(mean(m1)).to(beCloseTo(res1)) 552 | expect(mean(m1, .Row)).to(beCloseTo(res1)) 553 | expect(mean(m1, .Column)).to(beCloseTo(res2)) 554 | } 555 | it("std") { 556 | let m1 = Matrix([[1.0, 4.0], [3.0, 2.0]]) 557 | let res1 = [sqrt(4.5 / 2.0), sqrt(0.5 / 2.0)] 558 | let res2 = [sqrt(2.0 / 2.0), sqrt(2.0 / 2.0)] 559 | expect(std(m1)).to(beCloseTo(res1)) 560 | expect(std(m1, .Row)).to(beCloseTo(res1)) 561 | expect(std(m1, .Column)).to(beCloseTo(res2)) 562 | } 563 | it("normalize") { 564 | let m1 = Matrix([[1.0, 4.0], [3.0, 2.0]]) 565 | let mr = mean(m1) 566 | let sr = std(m1) 567 | let mc = mean(m1, .Column) 568 | let sc = std(m1, .Column) 569 | let res1 = transpose((transpose(m1) - mr) ./ sr) 570 | let res2 = (m1 - mc) ./ sc 571 | expect(normalize(m1)) == res1 572 | expect(normalize(m1, .Row)) == res1 573 | expect(normalize(m1, .Column)) == res2 574 | } 575 | it("sum") { 576 | let m1 = Matrix([[1.0, 4.0], [3.0, 2.0]]) 577 | let res1 = [5.0, 5.0] 578 | let res2 = [4.0, 6.0] 579 | expect(sum(m1)).to(beCloseTo(res1)) 580 | expect(sum(m1, .Row)).to(beCloseTo(res1)) 581 | expect(sum(m1, .Column)).to(beCloseTo(res2)) 582 | } 583 | it("sumsq") { 584 | let m1 = Matrix([[1.0, 4.0], [3.0, 2.0]]) 585 | let res1 = [17.0, 13.0] 586 | let res2 = [10.0, 20.0] 587 | expect(sumsq(m1)).to(beCloseTo(res1)) 588 | expect(sumsq(m1, .Row)).to(beCloseTo(res1)) 589 | expect(sumsq(m1, .Column)).to(beCloseTo(res2)) 590 | } 591 | } 592 | 593 | describe("Matrix linear algebra tests") { 594 | it("trace") { 595 | let m1 = Matrix([[1.0, 0.0, 2.0], 596 | [-1.0, 5.0, 0.0], 597 | [0.0, 3.0, -9.0]]) 598 | let res = -3.0 599 | expect(trace(m1)) == res 600 | } 601 | it("transpose") { 602 | let m1 = Matrix([[1.0, 4.0], 603 | [3.0, 2.0]]) 604 | let res = Matrix([[1.0, 3.0], 605 | [4.0, 2.0]]) 606 | expect(transpose(m1)) == res 607 | expect(m1′) == res 608 | expect(m1.T) == res 609 | } 610 | it("mtimes") { 611 | let m1 = Matrix([[1.0, 3.0, 5.0], 612 | [2.0, 4.0, 7.0]]) 613 | let m2 = Matrix([[-5.0, 8.0, 11.0], 614 | [3.0, 9.0, 21.0], 615 | [4.0, 0.0, 8.0]]) 616 | let res = Matrix([[24.0, 35.0, 114.0], 617 | [30.0, 52.0, 162.0]]) 618 | expect(m1 * m2) == res 619 | expect(mtimes(m1, m2)) == res 620 | } 621 | it("mpower") { 622 | let m1 = Matrix([[1.0, 2.0], 623 | [3.0, 4.0]]) 624 | let res1 = Matrix([[7.0, 10.0], 625 | [15.0, 22.0]]) 626 | let res2 = inv(m1 * m1) 627 | let eye = Matrix([[1.0, 0.0], 628 | [0.0, 1.0]]) 629 | expect(m1 ^ 1) == m1 630 | expect(m1 ^ 2) == res1 631 | expect(m1 ^ -2) == res2 632 | expect(m1 ^ 0) == eye 633 | } 634 | it("inverse") { 635 | let m1 = Matrix([[1.0, 0.0, 2.0], 636 | [-1.0, 5.0, 0.0], 637 | [0.0, 3.0, -9.0]]) 638 | let res = Matrix([[0.88235294117647067, -0.11764705882352944, 0.19607843137254904], 639 | [0.17647058823529416, 0.17647058823529413, 0.03921568627450981], 640 | [0.058823529411764663, 0.058823529411764719, -0.098039215686274522]]) 641 | expect(inv(m1)) == res 642 | expect(mpower(m1, -1)) == res 643 | expect(m1 ^ -1) == res 644 | let m2 = Matrix([[1.0, 0.0, 2.0], [-1.0, 5.0, 0.0]]) 645 | 646 | #if !SWIFT_PACKAGE 647 | expect { () -> Void in _ = inv(m2) }.to(throwAssertion()) 648 | expect { () -> Void in _ = inv(ones(3, 3)) }.to(throwAssertion()) 649 | #endif 650 | 651 | expect(inv(m1) * m1) == eye(3, 3) 652 | } 653 | it("eigen") { 654 | let m1 = Matrix([[1.0000, 0.5000, 0.3333, 0.2500], 655 | [0.5000, 1.0000, 0.6667, 0.5000], 656 | [0.3333, 0.6667, 1.0000, 0.7500], 657 | [0.2500, 0.5000, 0.7500, 1.0000]]) 658 | let e1 = eig(m1) 659 | expect(m1 * e1.V) == e1.V * e1.D 660 | let m2 = Matrix([[1.0, 0.0, 2.0], 661 | [-1.0, 5.0, 0.0]]) 662 | 663 | #if !SWIFT_PACKAGE 664 | expect { () -> Void in _ = eig(m2) }.to(throwAssertion()) 665 | #endif 666 | let m3 = Matrix([[0, 1], 667 | [-2, -3]]) 668 | let e2 = eig(m3) 669 | expect(m3 * e2.V) == e2.V * e2.D 670 | let v3 = Matrix([[-1.0, 0.0], 671 | [0.0, -2.0]]) 672 | expect(e2.D) == v3 673 | 674 | } 675 | it("svd") { 676 | let m1 = Matrix([[1.0, 2.0], 677 | [3.0, 4.0], 678 | [5.0, 6.0], 679 | [7.0, 8.0]]) 680 | let usv = svd(m1) 681 | expect(usv.U * usv.S * usv.V′) == m1 682 | } 683 | it("gsvd") { 684 | let m1 = Matrix([[1.0, 6.0, 11.0], 685 | [2.0, 7.0, 12.0], 686 | [3.0, 8.0, 13.0], 687 | [4.0, 9.0, 14.0], 688 | [5.0, 10.0, 15.0]]) 689 | let m2 = Matrix([[8.0, 1.0, 6.0], 690 | [3.0, 5.0, 7.0], 691 | [4.0, 9.0, 2.0]]) 692 | let m3 = Matrix([[0.5700, -0.6457, -0.4279], 693 | [-0.7455, -0.3296, -0.4375], 694 | [-0.1702, -0.0135, -0.4470], 695 | [0.2966, 0.3026, -0.4566], 696 | [0.0490, 0.6187, -0.4661]]) 697 | let (U, _, _, _, _, _) = gsvd(m1, m2) 698 | expect(U == m3) 699 | 700 | } 701 | it("chol") { 702 | let m1 = Matrix([[1, 1, 1, 1, 1], 703 | [1, 2, 3, 4, 5], 704 | [1, 3, 6, 10, 15], 705 | [1, 4, 10, 20, 35], 706 | [1, 5, 15, 35, 70]]) 707 | let u = chol(m1) 708 | expect(u′ * u) == m1 709 | let l = chol(m1, .Lower) 710 | expect(l * l′) == m1 711 | } 712 | it("det") { 713 | let m = Matrix([[1.44, -7.84, -4.39, 4.53], 714 | [-9.96, -0.28, -3.24, 3.83], 715 | [-7.55, 3.24, 6.27, -6.64], 716 | [8.34, 8.09, 5.28, 2.06]]) 717 | let d = det(m) 718 | 719 | expect(d).to(beCloseTo(-4044.7754)) 720 | } 721 | it("lstsq") { 722 | // Setup 723 | let a1 = Matrix([[1.44, -7.84, -4.39, 4.53], 724 | [-9.96, -0.28, -3.24, 3.83], 725 | [-7.55, 3.24, 6.27, -6.64], 726 | [8.34, 8.09, 5.28, 2.06], 727 | [7.08, 2.52, 0.74, -2.47], 728 | [-5.45, -5.70, -1.19, 4.70]]) 729 | let b1 = Matrix([[8.58, 9.35], 730 | [8.26, -4.43], 731 | [8.48, -0.70], 732 | [-5.28, -0.26], 733 | [5.72, -7.36], 734 | [8.93, -2.52]]) 735 | let c2 = Matrix([[-0.4506, 0.2497], 736 | [-0.8491, -0.9020], 737 | [0.7066, 0.6323], 738 | [0.1288, 0.1351]]) 739 | let d2 = Matrix([[195.3616, 107.05746]]) 740 | 741 | // Run function 742 | let (c1, d1) = lstsqr(a1, b1) 743 | 744 | // Check solution matrix 745 | expect(c1.rows) == c2.rows 746 | expect(c1.cols) == c2.cols 747 | for i in 0.. Void in _ = m1 ?? (.Range(4, -2, 0), .All) }.to(throwAssertion()) 1002 | expect { () -> Void in _ = m1 ?? (.Range(3, -2, -1), .All) }.to(throwAssertion()) 1003 | expect { () -> Void in _ = m1 ?? (.All, .Range(5, -2, 0)) }.to(throwAssertion()) 1004 | expect { () -> Void in _ = m1 ?? (.All, .Range(3, -2, -1)) }.to(throwAssertion()) 1005 | expect { () -> Void in _ = m1 ?? (.Range(0, -2, 4), .All) }.to(throwAssertion()) 1006 | expect { () -> Void in _ = m1 ?? (.Range(-1, -2, 3), .All) }.to(throwAssertion()) 1007 | expect { () -> Void in _ = m1 ?? (.All, .Range(0, -2, 5)) }.to(throwAssertion()) 1008 | expect { () -> Void in _ = m1 ?? (.All, .Range(-1, -2, 3)) }.to(throwAssertion()) 1009 | expect { () -> Void in _ = m1 ?? (.Take(5), .All) }.to(throwAssertion()) 1010 | expect { () -> Void in _ = m1 ?? (.Take(-1), .All) }.to(throwAssertion()) 1011 | expect { () -> Void in _ = m1 ?? (.All, .Take(6)) }.to(throwAssertion()) 1012 | expect { () -> Void in _ = m1 ?? (.All, .Take(-1)) }.to(throwAssertion()) 1013 | expect { () -> Void in _ = m1 ?? (.Drop(5), .All) }.to(throwAssertion()) 1014 | expect { () -> Void in _ = m1 ?? (.Drop(-1), .All) }.to(throwAssertion()) 1015 | expect { () -> Void in _ = m1 ?? (.All, .Drop(6)) }.to(throwAssertion()) 1016 | expect { () -> Void in _ = m1 ?? (.All, .Drop(-1)) }.to(throwAssertion()) 1017 | #endif 1018 | } 1019 | } 1020 | } 1021 | } 1022 | -------------------------------------------------------------------------------- /Tests/PerformanceTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PerformanceTests.swift 3 | // LASwift 4 | // 5 | // Created by Alexander Taraymovich on 15/03/2017. 6 | // Copyright © 2017 CocoaPods. All rights reserved. 7 | // 8 | 9 | import LASwift 10 | import XCTest 11 | 12 | class PerformanceTests: XCTestCase { 13 | let a = rand(1000, 1000) 14 | let b = rand(1000, 1000) 15 | let x = rand(1000000) 16 | let y = rand(1000000) 17 | 18 | func testRandomMatrix() { 19 | measure { 20 | _ = rand(1000, 1000) 21 | } 22 | } 23 | 24 | func testInvert() { 25 | measure { 26 | _ = inv(self.a) 27 | } 28 | } 29 | 30 | func testCholesky() { 31 | let aTa = transpose(a) * a 32 | measure { 33 | _ = chol(aTa) 34 | } 35 | } 36 | 37 | func testDot() { 38 | measure { 39 | _ = self.x * self.y 40 | } 41 | } 42 | 43 | func testTranspose() { 44 | measure { 45 | _ = transpose(self.a) 46 | } 47 | } 48 | 49 | func testMultiply() { 50 | measure { 51 | _ = self.a * self.b 52 | } 53 | } 54 | 55 | func testSigmoid() { 56 | measure { 57 | _ = 1 ./ (1 + exp(-self.a)) 58 | } 59 | } 60 | 61 | func testReLU() { 62 | measure { 63 | _ = thr(self.a, 0) 64 | } 65 | } 66 | 67 | func testSumByRows() { 68 | measure { 69 | _ = sum(self.a, .Row) 70 | } 71 | } 72 | 73 | func testSumByColumns() { 74 | measure { 75 | _ = sum(self.a, .Column) 76 | } 77 | } 78 | 79 | func testMaxIndicesByRows() { 80 | measure { 81 | _ = maxi(self.a, .Row) 82 | } 83 | } 84 | 85 | func testMaxIndicesByColumns() { 86 | measure { 87 | _ = maxi(self.a, .Column) 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Tests/VectorTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VectorTests.swift 3 | // LASwift 4 | // 5 | // Created by Alexander Taraymovich on 22/02/2017. 6 | // Copyright © 2017 CocoaPods. All rights reserved. 7 | // 8 | 9 | import Darwin 10 | import Quick 11 | import Nimble 12 | import LASwift 13 | 14 | class VectorSpec: QuickSpec { 15 | override func spec() { 16 | describe("Vector construction tests") { 17 | 18 | let vec1 = ones(10) 19 | 20 | it("ones vector has correct size") { 21 | expect(vec1.count) == 10 22 | } 23 | 24 | it("ones vector all ones") { 25 | for elem in vec1 { 26 | expect(elem) == 1.0 27 | } 28 | } 29 | 30 | let vec2 = zeros(10) 31 | 32 | it("zeros vector has correct size") { 33 | expect(vec2.count) == 10 34 | } 35 | 36 | it("zeros vector all zeros") { 37 | for elem in vec2 { 38 | expect(elem) == 0.0 39 | } 40 | } 41 | 42 | let vec3 = zeros(10) 43 | 44 | it("zeros vectors are equal") { 45 | expect(vec3) == vec2 46 | } 47 | 48 | it("zeros and ones vectors are not equal") { 49 | expect(vec2) != vec1 50 | } 51 | 52 | let vec4 = [Double](repeating: 1.0, count: 10) 53 | 54 | it("differently constructed ones vectors are equal") { 55 | expect(vec1) == vec4 56 | } 57 | } 58 | 59 | describe("Vector comparison") { 60 | it("equal") { 61 | let v1 = [1.0, 2.0] 62 | let v2 = [1.0, 2.0] 63 | expect(v1 == v2) == true 64 | } 65 | it("not equal") { 66 | let v1 = [1.0, 2.0] 67 | let v2 = [1.0, 4.0] 68 | expect(v1 != v2) == true 69 | } 70 | it("greater/less than") { 71 | let v1 = [11.0, 12.0] 72 | let v2 = [1.0, 2.0] 73 | expect(v1 > v2) == true 74 | expect(v2 < v1) == true 75 | } 76 | it("greater/less than or equal") { 77 | let v1 = [11.0, 12.0] 78 | let v2 = [1.0, 2.0] 79 | let v3 = [1.0, 2.0] 80 | expect(v1 >= v2) == true 81 | expect(v2 <= v1) == true 82 | expect(v2 >= v3) == true 83 | expect(v2 <= v3) == true 84 | } 85 | } 86 | describe("Vector arithmetic tests") { 87 | it("addition") { 88 | let vec1 = [1.0, 2.0, 3.0] 89 | let vec2 = [4.0, 5.0, 6.0] 90 | let res = [5.0, 7.0, 9.0] 91 | expect(vec1 + vec2).to(beCloseTo(res)) 92 | } 93 | 94 | it("substraction") { 95 | let vec1 = [1.0, 2.0, 3.0] 96 | let vec2 = [4.0, 5.0, 6.0] 97 | let res = [-3.0, -3.0, -3.0] 98 | expect(vec1 - vec2).to(beCloseTo(res)) 99 | } 100 | 101 | it("multiplication") { 102 | let vec1 = [1.0, 2.0, 3.0] 103 | let vec2 = [4.0, 5.0, 6.0] 104 | let res = [4.0, 10.0, 18.0] 105 | expect(vec1 .* vec2).to(beCloseTo(res)) 106 | } 107 | 108 | it("right division") { 109 | let vec1 = [1.0, 2.0, 3.0] 110 | let vec2 = [4.0, 5.0, 6.0] 111 | let res = [1.0 / 4.0, 2.0 / 5.0, 3.0 / 6.0] 112 | expect(vec1 ./ vec2).to(beCloseTo(res)) 113 | } 114 | 115 | it("left division") { 116 | let vec1 = [1.0, 2.0, 3.0] 117 | let vec2 = [4.0, 5.0, 6.0] 118 | let res = [4.0 / 1.0, 5.0 / 2.0, 6.0 / 3.0] 119 | expect(vec1 ./. vec2).to(beCloseTo(res)) 120 | } 121 | 122 | it("scalar addition") { 123 | let vec = [1.0, 2.0, 3.0] 124 | let s = 7.0 125 | let res = [8.0, 9.0, 10.0] 126 | expect(vec + s).to(beCloseTo(res)) 127 | expect(s + vec).to(beCloseTo(res)) 128 | } 129 | 130 | it("scalar substraction") { 131 | let vec = [1.0, 2.0, 3.0] 132 | let s = 7.0 133 | let res1 = [-6.0, -5.0, -4.0] 134 | let res2 = [6.0, 5.0, 4.0] 135 | expect(vec - s).to(beCloseTo(res1)) 136 | expect(s - vec).to(beCloseTo(res2)) 137 | } 138 | 139 | it("scalar multiplication") { 140 | let vec = [1.0, 2.0, 3.0] 141 | let s = 7.0 142 | let res = [7.0, 14.0, 21.0] 143 | expect(vec .* s).to(beCloseTo(res)) 144 | expect(s .* vec).to(beCloseTo(res)) 145 | } 146 | 147 | it("scalar right division") { 148 | let vec = [1.0, 2.0, 3.0] 149 | let s = 7.0 150 | let res1 = [1.0 / 7.0, 2.0 / 7.0, 3.0 / 7.0] 151 | let res2 = [7.0 / 1.0, 7.0 / 2.0, 7.0 / 3.0] 152 | expect(vec ./ s).to(beCloseTo(res1)) 153 | expect(s ./ vec).to(beCloseTo(res2)) 154 | } 155 | 156 | it("scalar left division") { 157 | let vec = [1.0, 2.0, 3.0] 158 | let s = 7.0 159 | let res1 = [7.0 / 1.0, 7.0 / 2.0, 7.0 / 3.0] 160 | let res2 = [1.0 / 7.0, 2.0 / 7.0, 3.0 / 7.0] 161 | expect(vec ./. s).to(beCloseTo(res1)) 162 | expect(s ./. vec).to(beCloseTo(res2)) 163 | } 164 | 165 | it("negation") { 166 | let vec = [1.0, -2.0, 3.0] 167 | let res = [-1.0, 2.0, -3.0] 168 | expect(-vec).to(beCloseTo(res)) 169 | } 170 | 171 | it("absolute") { 172 | let vec = [1.0, -2.0, 3.0] 173 | let res = [1.0, 2.0, 3.0] 174 | expect(abs(vec)).to(beCloseTo(res)) 175 | } 176 | 177 | it("threshold") { 178 | let vec = [1.0, -2.0, 3.0] 179 | let res = [1.0, 0.0, 3.0] 180 | expect(thr(vec, 0.0)).to(beCloseTo(res)) 181 | } 182 | 183 | it("dot product") { 184 | let vec1 = [1.0, 2.0, 3.0] 185 | let vec2 = [4.0, 5.0, 6.0] 186 | let res = 32.0 187 | expect(vec1 * vec2).to(beCloseTo(res)) 188 | } 189 | } 190 | 191 | describe("Vector power and exponential tests") { 192 | it("power") { 193 | let vec = [1.0, 2.0, 3.0] 194 | let p = 3.0 195 | let res = [1.0, 8.0, 27.0] 196 | expect(vec .^ p).to(beCloseTo(res)) 197 | } 198 | it("square") { 199 | let vec = [1.0, 2.0, 3.0] 200 | let res = [1.0, 4.0, 9.0] 201 | expect(square(vec)).to(beCloseTo(res)) 202 | expect(square(vec)).to(beCloseTo(vec .^ 2)) 203 | } 204 | it("sqrt") { 205 | let vec = [1.0, 2.0, 3.0] 206 | let res = [sqrt(1.0), sqrt(2.0), sqrt(3.0)] 207 | expect(sqrt(vec)).to(beCloseTo(res)) 208 | expect(sqrt(vec)).to(beCloseTo(vec .^ 0.5)) 209 | } 210 | it("exp") { 211 | let vec = [1.0, 2.0, 3.0] 212 | let res = [exp(1.0), exp(2.0), exp(3.0)] 213 | expect(exp(vec)).to(beCloseTo(res)) 214 | } 215 | it("log") { 216 | let vec = [1.0, 2.0, 3.0] 217 | let res = [log(1.0), log(2.0), log(3.0)] 218 | expect(log(vec)).to(beCloseTo(res)) 219 | } 220 | it("log10") { 221 | let vec = [1.0, 2.0, 3.0] 222 | let res = [log10(1.0), log10(2.0), log10(3.0)] 223 | expect(log10(vec)).to(beCloseTo(res)) 224 | } 225 | it("log2") { 226 | let vec = [1.0, 2.0, 3.0] 227 | let res = [log2(1.0), log2(2.0), log2(3.0)] 228 | expect(log2(vec)).to(beCloseTo(res)) 229 | } 230 | } 231 | 232 | describe("Vector trigonometric tests") { 233 | it("sin") { 234 | let vec = [1.0, 2.0, 3.0] 235 | let res = [sin(1.0), sin(2.0), sin(3.0)] 236 | expect(sin(vec)).to(beCloseTo(res)) 237 | } 238 | it("cos") { 239 | let vec = [1.0, 2.0, 3.0] 240 | let res = [cos(1.0), cos(2.0), cos(3.0)] 241 | expect(cos(vec)).to(beCloseTo(res)) 242 | } 243 | it("tan") { 244 | let vec = [1.0, 2.0, 3.0] 245 | let res = [tan(1.0), tan(2.0), tan(3.0)] 246 | expect(tan(vec)).to(beCloseTo(res)) 247 | } 248 | it("asin") { 249 | let vec = [1.0, 2.0, 3.0] 250 | let res = [asin(1.0), asin(2.0), asin(3.0)] 251 | expect(asin(vec)).to(beCloseTo(res)) 252 | } 253 | it("acos") { 254 | let vec = [1.0, 2.0, 3.0] 255 | let res = [acos(1.0), acos(2.0), acos(3.0)] 256 | expect(acos(vec)).to(beCloseTo(res)) 257 | } 258 | it("atan") { 259 | let vec = [1.0, 2.0, 3.0] 260 | let res = [atan(1.0), atan(2.0), atan(3.0)] 261 | expect(atan(vec)).to(beCloseTo(res)) 262 | } 263 | } 264 | 265 | describe("Vector statistics tests") { 266 | it("max") { 267 | let vec = [1.0, 3.0, 2.0] 268 | let res = 3.0 269 | expect(max(vec)).to(beCloseTo(res)) 270 | } 271 | it("maxi") { 272 | let vec = [1.0, 3.0, 2.0] 273 | let res = 1 274 | expect(maxi(vec)) == res 275 | } 276 | it("min") { 277 | let vec = [3.0, 1.0, 2.0] 278 | let res = 1.0 279 | expect(min(vec)).to(beCloseTo(res)) 280 | } 281 | it("mini") { 282 | let vec = [3.0, 1.0, 2.0] 283 | let res = 1 284 | expect(mini(vec)) == res 285 | } 286 | it("mean") { 287 | let vec = [1.0, 2.0, 3.0] 288 | let res = 2.0 289 | expect(mean(vec)).to(beCloseTo(res)) 290 | } 291 | it("std") { 292 | let vec = [1.0, 2.0, 3.0] 293 | let res = sqrt(2.0 / 3.0) 294 | expect(std(vec)).to(beCloseTo(res)) 295 | } 296 | it("sum") { 297 | let vec = [1.0, 2.0, 3.0] 298 | let res = 6.0 299 | expect(sum(vec)).to(beCloseTo(res)) 300 | } 301 | it("sumsq") { 302 | let vec = [1.0, 2.0, 3.0] 303 | let res = 14.0 304 | expect(sumsq(vec)).to(beCloseTo(res)) 305 | } 306 | it("normalize") { 307 | let vec = [1.0, 2.0, 3.0] 308 | let m = mean(vec) 309 | let s = std(vec) 310 | let res = [(1.0 - m) / s, (2.0 - m) / s, (3.0 - m) / s] 311 | expect(normalize(vec)).to(beCloseTo(res)) 312 | } 313 | } 314 | } 315 | } 316 | --------------------------------------------------------------------------------