├── docs ├── img │ ├── gh.png │ ├── carat.png │ └── dash.png ├── docsets │ ├── .tgz │ └── .docset │ │ └── Contents │ │ ├── Resources │ │ ├── docSet.dsidx │ │ └── Documents │ │ │ ├── img │ │ │ ├── gh.png │ │ │ ├── carat.png │ │ │ └── dash.png │ │ │ ├── badge.svg │ │ │ ├── js │ │ │ └── jazzy.js │ │ │ ├── undocumented.json │ │ │ ├── Protocols.html │ │ │ ├── Classes.html │ │ │ ├── css │ │ │ ├── highlight.css │ │ │ └── jazzy.css │ │ │ ├── Enums.html │ │ │ ├── Extensions.html │ │ │ ├── search.json │ │ │ ├── index.html │ │ │ ├── Classes │ │ │ └── AnnoyIndex │ │ │ │ └── DistanceMetric.html │ │ │ └── Enums │ │ │ └── AnnoyIndexError.html │ │ └── Info.plist ├── badge.svg ├── js │ └── jazzy.js ├── undocumented.json ├── Protocols.html ├── Classes.html ├── css │ ├── highlight.css │ └── jazzy.css ├── Enums.html ├── Extensions.html ├── search.json ├── index.html ├── Classes │ └── AnnoyIndex │ │ └── DistanceMetric.html └── Enums │ └── AnnoyIndexError.html ├── .gitignore ├── .gitattributes ├── Tests ├── LinuxMain.swift └── SwiftAnnoyTests │ ├── XCTestManifests.swift │ ├── Array+toFloat.swift │ ├── TestData.swift │ ├── HelperFuncs.swift │ ├── AnnoyIndexBaseTests.swift │ ├── AnnoyIndexFloatTests.swift │ └── AnnoyIndexDoubleTests.swift ├── Sources ├── SwiftAnnoy │ ├── AnnoyOperable.swift │ ├── AnnoyError.swift │ └── AnnoyIndex.swift └── CAnnoyWrapper │ ├── Include │ └── cannoylib.hpp │ └── kissrandom.h ├── Package.swift ├── LICENSE └── README.md /docs/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbadger3/SwiftAnnoy/HEAD/docs/img/gh.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | .swiftpm -------------------------------------------------------------------------------- /docs/docsets/.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbadger3/SwiftAnnoy/HEAD/docs/docsets/.tgz -------------------------------------------------------------------------------- /docs/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbadger3/SwiftAnnoy/HEAD/docs/img/carat.png -------------------------------------------------------------------------------- /docs/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbadger3/SwiftAnnoy/HEAD/docs/img/dash.png -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.h linguist-detectable=false 2 | *.hpp linguist-detectable=false 3 | *.cpp linguist-detectable=false 4 | *.swift linguist-detectable=true 5 | -------------------------------------------------------------------------------- /docs/docsets/.docset/Contents/Resources/docSet.dsidx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbadger3/SwiftAnnoy/HEAD/docs/docsets/.docset/Contents/Resources/docSet.dsidx -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import SwiftAnnoyTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += SwiftAnnoyTests.allTests() 7 | XCTMain(tests) 8 | -------------------------------------------------------------------------------- /docs/docsets/.docset/Contents/Resources/Documents/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbadger3/SwiftAnnoy/HEAD/docs/docsets/.docset/Contents/Resources/Documents/img/gh.png -------------------------------------------------------------------------------- /docs/docsets/.docset/Contents/Resources/Documents/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbadger3/SwiftAnnoy/HEAD/docs/docsets/.docset/Contents/Resources/Documents/img/carat.png -------------------------------------------------------------------------------- /docs/docsets/.docset/Contents/Resources/Documents/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbadger3/SwiftAnnoy/HEAD/docs/docsets/.docset/Contents/Resources/Documents/img/dash.png -------------------------------------------------------------------------------- /Tests/SwiftAnnoyTests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | #if !canImport(ObjectiveC) 4 | public func allTests() -> [XCTestCaseEntry] { 5 | return [ 6 | testCase(SwiftAnnoyTests.allTests), 7 | ] 8 | } 9 | #endif 10 | -------------------------------------------------------------------------------- /Tests/SwiftAnnoyTests/Array+toFloat.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File 2.swift 3 | // 4 | // 5 | // Created by Jonathan Badger on 5/27/20. 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | extension Array where Element: BinaryFloatingPoint{ 12 | func toFloat() -> [Float] { 13 | return self.map({Float($0)}) 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /Sources/SwiftAnnoy/AnnoyOperable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by Jonathan Badger on 5/19/20. 6 | // 7 | 8 | import Foundation 9 | 10 | /** 11 | Datatypes currently supported by SwiftAnnoy. 12 | 13 | 1. Float 14 | 2. Double 15 | */ 16 | public protocol AnnoyOperable { 17 | 18 | } 19 | 20 | extension Float: AnnoyOperable { 21 | 22 | } 23 | 24 | extension Double: AnnoyOperable { 25 | 26 | } 27 | -------------------------------------------------------------------------------- /Tests/SwiftAnnoyTests/TestData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File 2.swift 3 | // 4 | // 5 | // Created by Jonathan Badger on 5/27/20. 6 | // 7 | 8 | func testData() -> [[Double]] { 9 | let item0 = [1.0, 0.0] 10 | let item1 = [0.0, 1.0] 11 | let item2 = [-1.0, 0.0] 12 | let item3 = [1.0, 1.0] 13 | let item4 = [4.0, 5.0] 14 | let item5 = [7.0, 9.0] 15 | return [item0, item1, item2, item3, item4, item5] 16 | } 17 | 18 | func floatTestData() -> [[Float]] { 19 | var floatData: [[Float]] = [] 20 | for item in testData(){ 21 | floatData.append(item.toFloat()) 22 | } 23 | return floatData 24 | } 25 | -------------------------------------------------------------------------------- /Tests/SwiftAnnoyTests/HelperFuncs.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by Jonathan Badger on 5/18/20. 6 | // 7 | 8 | import XCTest 9 | 10 | func assertVecsEqual(vec1: [T], vec2: [T]) { 11 | for (val1, val2) in zip(vec1, vec2) { 12 | switch T.self { 13 | case is Float.Type: 14 | XCTAssertEqual(val1 as! Float , val2 as! Float , accuracy: Float(0.000001)) 15 | case is Double.Type: 16 | XCTAssertEqual(val1 as! Double, val2 as! Double, accuracy: Double(0.0000001)) 17 | case is Int.Type: 18 | XCTAssertEqual(val1 , val2) 19 | default: 20 | XCTFail() 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /docs/docsets/.docset/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | com.jazzy. 7 | CFBundleName 8 | 9 | DocSetPlatformFamily 10 | 11 | isDashDocset 12 | 13 | dashIndexFilePath 14 | index.html 15 | isJavaScriptEnabled 16 | 17 | DashDocSetFamily 18 | dashtoc 19 | 20 | 21 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.2 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "SwiftAnnoy", 8 | products: [ 9 | .library( 10 | name: "SwiftAnnoy", 11 | targets: ["SwiftAnnoy", "CAnnoyWrapper"]), 12 | ], 13 | dependencies: [], 14 | targets: [ 15 | .target( 16 | name: "CAnnoyWrapper", 17 | dependencies: [], 18 | path: "./Sources/CAnnoyWrapper"), 19 | .target( 20 | name: "SwiftAnnoy", 21 | dependencies: ["CAnnoyWrapper"]), 22 | .testTarget( 23 | name: "SwiftAnnoyTests", 24 | dependencies: ["SwiftAnnoy"]), 25 | ] 26 | ) 27 | 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Jonathan Badger 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Sources/SwiftAnnoy/AnnoyError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by Jonathan Badger on 5/19/20. 6 | // 7 | 8 | import Foundation 9 | 10 | /** 11 | Errors thrown by AnnoyIndex. 12 | 13 | *values* 14 | 15 | `invalidVectorLenght` 16 | `addItemFailed` 17 | `buildFailed` 18 | `unbuildFailed` 19 | `saveFailed` 20 | `loadFailed` 21 | */ 22 | 23 | public enum AnnoyIndexError: Error, LocalizedError { 24 | case invalidVectorLength(message: String) 25 | case addItemFailed 26 | case buildFailed 27 | case unbuildFailed 28 | case saveFailed 29 | case loadFailed 30 | 31 | public var errorDescription: String? { 32 | switch self { 33 | case .invalidVectorLength(let message): 34 | return message 35 | case .addItemFailed: 36 | return "Adding item to index failed." 37 | case .buildFailed: 38 | return nil 39 | case .unbuildFailed: 40 | return nil 41 | case .saveFailed: 42 | return nil //Errors should be printed to stdout by Annoy C++ lib 43 | case .loadFailed: 44 | return nil 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /docs/badge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | documentation 17 | 18 | 19 | documentation 20 | 21 | 22 | 83% 23 | 24 | 25 | 83% 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/docsets/.docset/Contents/Resources/Documents/badge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | documentation 17 | 18 | 19 | documentation 20 | 21 | 22 | 83% 23 | 24 | 25 | 83% 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Sources/CAnnoyWrapper/Include/cannoylib.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // cannoylib.hpp 3 | // SwiftAnnoy 4 | // 5 | // Created by Jonathan Badger on 3/15/20. 6 | // Copyright © 2020 Jonathan Badger. All rights reserved. 7 | // 8 | 9 | #ifndef cannoylib_hpp 10 | #define cannoylib_hpp 11 | 12 | #include 13 | #include 14 | 15 | #ifdef __cplusplus 16 | 17 | extern "C" { 18 | #endif 19 | 20 | const void * C_initializeAnnoyIndex(int f, char* dist_metric, char* dtype); 21 | bool C_add_item(int i, void* vector, char* dist_metric, char* dtype, const void* indexPointer); 22 | bool C_build(int num_trees, char* dist_metric, char* dtype, const void* indexPointer); 23 | bool C_unbuild(const void* indexPointer); 24 | bool C_save(const char* filename, bool prefault, const void* indexPointer); 25 | void C_unload(const void* indexPointer); 26 | bool C_load(const char* filename, char* dist_metric, char* dtype, bool prefault, const void* indexPointer); 27 | void C_get_distance(int i, int j, void* result, char* dist_metric, char* dtype, const void* indexPointer); 28 | void C_get_nns_by_item(int item, int n, int search_k, int* results, void* distances,char* dist_metric, char* dtype, const void* indexPointer); 29 | void C_get_nns_by_vector(void* vector, int n, int search_k, int* results, void* distances, char* dist_metric, char* dtype, const void* indexPointer); 30 | int C_get_n_items(const void* indexPointer); 31 | int C_get_n_trees(const void* indexPointer); 32 | void C_verbose(bool v, const void* indexPointer); 33 | void C_get_item(int item, void* vector, char* dist_metric, char* dtype, const void* indexPointer); 34 | void C_set_seed(int q, const void* indexPointer); 35 | bool C_on_disk_build(const char* filename, const void* indexPointer); 36 | void C_deleteAnnoyIndex(const void* indexPointer); 37 | 38 | #ifdef __cplusplus 39 | } 40 | #endif 41 | #endif 42 | 43 | 44 | -------------------------------------------------------------------------------- /docs/js/jazzy.js: -------------------------------------------------------------------------------- 1 | window.jazzy = {'docset': false} 2 | if (typeof window.dash != 'undefined') { 3 | document.documentElement.className += ' dash' 4 | window.jazzy.docset = true 5 | } 6 | if (navigator.userAgent.match(/xcode/i)) { 7 | document.documentElement.className += ' xcode' 8 | window.jazzy.docset = true 9 | } 10 | 11 | function toggleItem($link, $content) { 12 | var animationDuration = 300; 13 | $link.toggleClass('token-open'); 14 | $content.slideToggle(animationDuration); 15 | } 16 | 17 | function itemLinkToContent($link) { 18 | return $link.parent().parent().next(); 19 | } 20 | 21 | // On doc load + hash-change, open any targetted item 22 | function openCurrentItemIfClosed() { 23 | if (window.jazzy.docset) { 24 | return; 25 | } 26 | var $link = $(`a[name="${location.hash.substring(1)}"]`).nextAll('.token'); 27 | $content = itemLinkToContent($link); 28 | if ($content.is(':hidden')) { 29 | toggleItem($link, $content); 30 | } 31 | } 32 | 33 | $(openCurrentItemIfClosed); 34 | $(window).on('hashchange', openCurrentItemIfClosed); 35 | 36 | // On item link ('token') click, toggle its discussion 37 | $('.token').on('click', function(event) { 38 | if (window.jazzy.docset) { 39 | return; 40 | } 41 | var $link = $(this); 42 | toggleItem($link, itemLinkToContent($link)); 43 | 44 | // Keeps the document from jumping to the hash. 45 | var href = $link.attr('href'); 46 | if (history.pushState) { 47 | history.pushState({}, '', href); 48 | } else { 49 | location.hash = href; 50 | } 51 | event.preventDefault(); 52 | }); 53 | 54 | // Clicks on links to the current, closed, item need to open the item 55 | $("a:not('.token')").on('click', function() { 56 | if (location == this.href) { 57 | openCurrentItemIfClosed(); 58 | } 59 | }); 60 | 61 | // KaTeX rendering 62 | if ("katex" in window) { 63 | $($('.math').each( (_, element) => { 64 | katex.render(element.textContent, element, { 65 | displayMode: $(element).hasClass('m-block'), 66 | throwOnError: false, 67 | trust: true 68 | }); 69 | })) 70 | } 71 | -------------------------------------------------------------------------------- /docs/docsets/.docset/Contents/Resources/Documents/js/jazzy.js: -------------------------------------------------------------------------------- 1 | window.jazzy = {'docset': false} 2 | if (typeof window.dash != 'undefined') { 3 | document.documentElement.className += ' dash' 4 | window.jazzy.docset = true 5 | } 6 | if (navigator.userAgent.match(/xcode/i)) { 7 | document.documentElement.className += ' xcode' 8 | window.jazzy.docset = true 9 | } 10 | 11 | function toggleItem($link, $content) { 12 | var animationDuration = 300; 13 | $link.toggleClass('token-open'); 14 | $content.slideToggle(animationDuration); 15 | } 16 | 17 | function itemLinkToContent($link) { 18 | return $link.parent().parent().next(); 19 | } 20 | 21 | // On doc load + hash-change, open any targetted item 22 | function openCurrentItemIfClosed() { 23 | if (window.jazzy.docset) { 24 | return; 25 | } 26 | var $link = $(`a[name="${location.hash.substring(1)}"]`).nextAll('.token'); 27 | $content = itemLinkToContent($link); 28 | if ($content.is(':hidden')) { 29 | toggleItem($link, $content); 30 | } 31 | } 32 | 33 | $(openCurrentItemIfClosed); 34 | $(window).on('hashchange', openCurrentItemIfClosed); 35 | 36 | // On item link ('token') click, toggle its discussion 37 | $('.token').on('click', function(event) { 38 | if (window.jazzy.docset) { 39 | return; 40 | } 41 | var $link = $(this); 42 | toggleItem($link, itemLinkToContent($link)); 43 | 44 | // Keeps the document from jumping to the hash. 45 | var href = $link.attr('href'); 46 | if (history.pushState) { 47 | history.pushState({}, '', href); 48 | } else { 49 | location.hash = href; 50 | } 51 | event.preventDefault(); 52 | }); 53 | 54 | // Clicks on links to the current, closed, item need to open the item 55 | $("a:not('.token')").on('click', function() { 56 | if (location == this.href) { 57 | openCurrentItemIfClosed(); 58 | } 59 | }); 60 | 61 | // KaTeX rendering 62 | if ("katex" in window) { 63 | $($('.math').each( (_, element) => { 64 | katex.render(element.textContent, element, { 65 | displayMode: $(element).hasClass('m-block'), 66 | throwOnError: false, 67 | trust: true 68 | }); 69 | })) 70 | } 71 | -------------------------------------------------------------------------------- /docs/undocumented.json: -------------------------------------------------------------------------------- 1 | { 2 | "warnings": [ 3 | { 4 | "file": "/Users/jonathanbadger/Library/Mobile Documents/com~apple~CloudDocs/projects/SwiftAnnoy/Sources/SwiftAnnoy/AnnoyError.swift", 5 | "line": 24, 6 | "symbol": "AnnoyIndexError.invalidVectorLength(message:)", 7 | "symbol_kind": "source.lang.swift.decl.enumelement", 8 | "warning": "undocumented" 9 | }, 10 | { 11 | "file": "/Users/jonathanbadger/Library/Mobile Documents/com~apple~CloudDocs/projects/SwiftAnnoy/Sources/SwiftAnnoy/AnnoyError.swift", 12 | "line": 25, 13 | "symbol": "AnnoyIndexError.addItemFailed", 14 | "symbol_kind": "source.lang.swift.decl.enumelement", 15 | "warning": "undocumented" 16 | }, 17 | { 18 | "file": "/Users/jonathanbadger/Library/Mobile Documents/com~apple~CloudDocs/projects/SwiftAnnoy/Sources/SwiftAnnoy/AnnoyError.swift", 19 | "line": 26, 20 | "symbol": "AnnoyIndexError.buildFailed", 21 | "symbol_kind": "source.lang.swift.decl.enumelement", 22 | "warning": "undocumented" 23 | }, 24 | { 25 | "file": "/Users/jonathanbadger/Library/Mobile Documents/com~apple~CloudDocs/projects/SwiftAnnoy/Sources/SwiftAnnoy/AnnoyError.swift", 26 | "line": 27, 27 | "symbol": "AnnoyIndexError.unbuildFailed", 28 | "symbol_kind": "source.lang.swift.decl.enumelement", 29 | "warning": "undocumented" 30 | }, 31 | { 32 | "file": "/Users/jonathanbadger/Library/Mobile Documents/com~apple~CloudDocs/projects/SwiftAnnoy/Sources/SwiftAnnoy/AnnoyError.swift", 33 | "line": 28, 34 | "symbol": "AnnoyIndexError.saveFailed", 35 | "symbol_kind": "source.lang.swift.decl.enumelement", 36 | "warning": "undocumented" 37 | }, 38 | { 39 | "file": "/Users/jonathanbadger/Library/Mobile Documents/com~apple~CloudDocs/projects/SwiftAnnoy/Sources/SwiftAnnoy/AnnoyError.swift", 40 | "line": 29, 41 | "symbol": "AnnoyIndexError.loadFailed", 42 | "symbol_kind": "source.lang.swift.decl.enumelement", 43 | "warning": "undocumented" 44 | } 45 | ], 46 | "source_directory": "/Users/jonathanbadger/Library/Mobile Documents/com~apple~CloudDocs/projects/SwiftAnnoy" 47 | } -------------------------------------------------------------------------------- /docs/docsets/.docset/Contents/Resources/Documents/undocumented.json: -------------------------------------------------------------------------------- 1 | { 2 | "warnings": [ 3 | { 4 | "file": "/Users/jonathanbadger/Library/Mobile Documents/com~apple~CloudDocs/projects/SwiftAnnoy/Sources/SwiftAnnoy/AnnoyError.swift", 5 | "line": 24, 6 | "symbol": "AnnoyIndexError.invalidVectorLength(message:)", 7 | "symbol_kind": "source.lang.swift.decl.enumelement", 8 | "warning": "undocumented" 9 | }, 10 | { 11 | "file": "/Users/jonathanbadger/Library/Mobile Documents/com~apple~CloudDocs/projects/SwiftAnnoy/Sources/SwiftAnnoy/AnnoyError.swift", 12 | "line": 25, 13 | "symbol": "AnnoyIndexError.addItemFailed", 14 | "symbol_kind": "source.lang.swift.decl.enumelement", 15 | "warning": "undocumented" 16 | }, 17 | { 18 | "file": "/Users/jonathanbadger/Library/Mobile Documents/com~apple~CloudDocs/projects/SwiftAnnoy/Sources/SwiftAnnoy/AnnoyError.swift", 19 | "line": 26, 20 | "symbol": "AnnoyIndexError.buildFailed", 21 | "symbol_kind": "source.lang.swift.decl.enumelement", 22 | "warning": "undocumented" 23 | }, 24 | { 25 | "file": "/Users/jonathanbadger/Library/Mobile Documents/com~apple~CloudDocs/projects/SwiftAnnoy/Sources/SwiftAnnoy/AnnoyError.swift", 26 | "line": 27, 27 | "symbol": "AnnoyIndexError.unbuildFailed", 28 | "symbol_kind": "source.lang.swift.decl.enumelement", 29 | "warning": "undocumented" 30 | }, 31 | { 32 | "file": "/Users/jonathanbadger/Library/Mobile Documents/com~apple~CloudDocs/projects/SwiftAnnoy/Sources/SwiftAnnoy/AnnoyError.swift", 33 | "line": 28, 34 | "symbol": "AnnoyIndexError.saveFailed", 35 | "symbol_kind": "source.lang.swift.decl.enumelement", 36 | "warning": "undocumented" 37 | }, 38 | { 39 | "file": "/Users/jonathanbadger/Library/Mobile Documents/com~apple~CloudDocs/projects/SwiftAnnoy/Sources/SwiftAnnoy/AnnoyError.swift", 40 | "line": 29, 41 | "symbol": "AnnoyIndexError.loadFailed", 42 | "symbol_kind": "source.lang.swift.decl.enumelement", 43 | "warning": "undocumented" 44 | } 45 | ], 46 | "source_directory": "/Users/jonathanbadger/Library/Mobile Documents/com~apple~CloudDocs/projects/SwiftAnnoy" 47 | } -------------------------------------------------------------------------------- /Sources/CAnnoyWrapper/kissrandom.h: -------------------------------------------------------------------------------- 1 | #ifndef ANNOY_KISSRANDOM_H 2 | #define ANNOY_KISSRANDOM_H 3 | 4 | #if defined(_MSC_VER) && _MSC_VER == 1500 5 | typedef unsigned __int32 uint32_t; 6 | typedef unsigned __int64 uint64_t; 7 | #else 8 | #include 9 | #endif 10 | 11 | namespace Annoy { 12 | 13 | // KISS = "keep it simple, stupid", but high quality random number generator 14 | // http://www0.cs.ucl.ac.uk/staff/d.jones/GoodPracticeRNG.pdf -> "Use a good RNG and build it into your code" 15 | // http://mathforum.org/kb/message.jspa?messageID=6627731 16 | // https://de.wikipedia.org/wiki/KISS_(Zufallszahlengenerator) 17 | 18 | // 32 bit KISS 19 | struct Kiss32Random { 20 | uint32_t x; 21 | uint32_t y; 22 | uint32_t z; 23 | uint32_t c; 24 | 25 | static const uint32_t default_seed = 123456789; 26 | #if __cplusplus < 201103L 27 | typedef uint32_t seed_type; 28 | #endif 29 | 30 | // seed must be != 0 31 | Kiss32Random(uint32_t seed = default_seed) { 32 | x = seed; 33 | y = 362436000; 34 | z = 521288629; 35 | c = 7654321; 36 | } 37 | 38 | uint32_t kiss() { 39 | // Linear congruence generator 40 | x = 69069 * x + 12345; 41 | 42 | // Xor shift 43 | y ^= y << 13; 44 | y ^= y >> 17; 45 | y ^= y << 5; 46 | 47 | // Multiply-with-carry 48 | uint64_t t = 698769069ULL * z + c; 49 | c = t >> 32; 50 | z = (uint32_t) t; 51 | 52 | return x + y + z; 53 | } 54 | inline int flip() { 55 | // Draw random 0 or 1 56 | return kiss() & 1; 57 | } 58 | inline size_t index(size_t n) { 59 | // Draw random integer between 0 and n-1 where n is at most the number of data points you have 60 | return kiss() % n; 61 | } 62 | inline void set_seed(uint32_t seed) { 63 | x = seed; 64 | } 65 | }; 66 | 67 | // 64 bit KISS. Use this if you have more than about 2^24 data points ("big data" ;) ) 68 | struct Kiss64Random { 69 | uint64_t x; 70 | uint64_t y; 71 | uint64_t z; 72 | uint64_t c; 73 | 74 | static const uint64_t default_seed = 1234567890987654321ULL; 75 | #if __cplusplus < 201103L 76 | typedef uint64_t seed_type; 77 | #endif 78 | 79 | // seed must be != 0 80 | Kiss64Random(uint64_t seed = default_seed) { 81 | x = seed; 82 | y = 362436362436362436ULL; 83 | z = 1066149217761810ULL; 84 | c = 123456123456123456ULL; 85 | } 86 | 87 | uint64_t kiss() { 88 | // Linear congruence generator 89 | z = 6906969069LL*z+1234567; 90 | 91 | // Xor shift 92 | y ^= (y<<13); 93 | y ^= (y>>17); 94 | y ^= (y<<43); 95 | 96 | // Multiply-with-carry (uint128_t t = (2^58 + 1) * x + c; c = t >> 64; x = (uint64_t) t) 97 | uint64_t t = (x<<58)+c; 98 | c = (x>>6); 99 | x += t; 100 | c += (x.DistanceMetric 13 | 14 | var sut:AnnoyIndex! 15 | 16 | override func setUp() { 17 | super.setUp() 18 | } 19 | 20 | override func tearDown() { 21 | sut = nil 22 | super.tearDown() 23 | } 24 | 25 | //MARK: Given 26 | func givenIndex(itemLength: Int = 2, metric: DistanceMetric = DistanceMetric.euclidean) { 27 | sut = AnnoyIndex(itemLength: itemLength, metric: metric) 28 | } 29 | //MARK: - Given 30 | func givenTestData() { 31 | var data = testData() 32 | for (index, _) in data.enumerated() { 33 | try! sut.addItem(index: index, vector: &data[index]) 34 | } 35 | } 36 | 37 | func test_init_setsNumFeatures() { 38 | // Given 39 | let itemLength = 5 40 | 41 | // When 42 | givenIndex(itemLength: itemLength) 43 | 44 | // Then 45 | XCTAssertEqual(sut.itemLength, itemLength) 46 | } 47 | 48 | //TODO: Add build tests 49 | 50 | //TODO: Add unbuild tests 51 | 52 | func test_save_whenFails_throwsSaveFailedError() { 53 | // Given 54 | givenIndex() 55 | let url = URL(string: "/test/url/path")! 56 | 57 | // When/Then 58 | XCTAssertThrowsError(try sut.save(url: url)) { error in 59 | guard case AnnoyIndexError.saveFailed = error else { 60 | return XCTFail() 61 | } 62 | } 63 | } 64 | 65 | func test_save_whenSuccess_newFileCreated() { 66 | // Given 67 | givenIndex() 68 | let fileManager = FileManager.default 69 | var url = try! fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) 70 | url.appendPathComponent("test.index") 71 | givenTestData() 72 | try! sut.build(numTrees: 1) 73 | 74 | // When 75 | try! sut.save(url: url) 76 | 77 | // Then 78 | let success = fileManager.fileExists(atPath: url.path) 79 | XCTAssertTrue(success) 80 | } 81 | 82 | //TODO: Add build tests 83 | 84 | //TODO: Add unbuild tests 85 | 86 | //TODO: Add unload tests 87 | 88 | func test_load_whenSuccessful_itemsLoaded() { 89 | // Given 90 | givenIndex() 91 | let fileManager = FileManager.default 92 | var url = try! fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) 93 | url.appendPathComponent("test.index") 94 | givenTestData() 95 | 96 | try! sut.build(numTrees: 1) 97 | try! sut.save(url: url) 98 | givenIndex() 99 | XCTAssertEqual(sut.numberOfItems, 0) //verify new index with no loaded items 100 | 101 | // When 102 | try! sut.load(url: url) 103 | 104 | // Then 105 | XCTAssertEqual(sut.numberOfItems, testData().count) 106 | } 107 | 108 | func test_load_whenFails_throwsLoadFailedError() { 109 | //Given 110 | givenIndex() 111 | let url = URL(string: "/test/url/fake.file")! 112 | 113 | XCTAssertThrowsError(try sut.load(url: url)) { error in 114 | guard case AnnoyIndexError.loadFailed = error else { 115 | return XCTFail() 116 | } 117 | } 118 | } 119 | 120 | //TODO: setVerbose 121 | 122 | //TODO: getItem 123 | 124 | //TODO: setSeed 125 | 126 | //TODO: onDiskBuild 127 | } 128 | -------------------------------------------------------------------------------- /docs/Protocols.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Protocols Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

Docs (83% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 70 |
71 |
72 |
73 |

Protocols

74 |

The following protocols are available globally.

75 | 76 |
77 |
78 |
79 |
    80 |
  • 81 |
    82 | 83 | 84 | 85 | AnnoyOperable 86 | 87 |
    88 |
    89 |
    90 |
    91 |
    92 |
    93 |

    Datatypes currently supported by SwiftAnnoy.

    94 |
    1. Float
     95 | 2. Double
     96 | 
    97 | 98 |
    99 |
    100 |

    Declaration

    101 |
    102 |

    Swift

    103 |
    public protocol AnnoyOperable
    104 | 105 |
    106 |
    107 |
    108 |
    109 |
  • 110 |
111 |
112 |
113 |
114 | 118 |
119 |
120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /docs/docsets/.docset/Contents/Resources/Documents/Protocols.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Protocols Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

Docs (83% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 70 |
71 |
72 |
73 |

Protocols

74 |

The following protocols are available globally.

75 | 76 |
77 |
78 |
79 |
    80 |
  • 81 |
    82 | 83 | 84 | 85 | AnnoyOperable 86 | 87 |
    88 |
    89 |
    90 |
    91 |
    92 |
    93 |

    Datatypes currently supported by SwiftAnnoy.

    94 |
    1. Float
     95 | 2. Double
     96 | 
    97 | 98 |
    99 |
    100 |

    Declaration

    101 |
    102 |

    Swift

    103 |
    public protocol AnnoyOperable
    104 | 105 |
    106 |
    107 |
    108 |
    109 |
  • 110 |
111 |
112 |
113 |
114 | 118 |
119 |
120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /docs/Classes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Classes Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

Docs (83% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 70 |
71 |
72 |
73 |

Classes

74 |

The following classes are available globally.

75 | 76 |
77 |
78 |
79 |
    80 |
  • 81 |
    82 | 83 | 84 | 85 | AnnoyIndex 86 | 87 |
    88 |
    89 |
    90 |
    91 |
    92 |
    93 |

    The primary generic class used to create an index.

    94 | 95 | See more 96 |
    97 |
    98 |

    Declaration

    99 |
    100 |

    Swift

    101 |
    public class AnnoyIndex<T> where T : AnnoyOperable
    102 | 103 |
    104 |
    105 |
    106 |
    107 |
  • 108 |
109 |
110 |
111 |
112 | 116 |
117 |
118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /docs/docsets/.docset/Contents/Resources/Documents/Classes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Classes Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

Docs (83% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 70 |
71 |
72 |
73 |

Classes

74 |

The following classes are available globally.

75 | 76 |
77 |
78 |
79 |
    80 |
  • 81 |
    82 | 83 | 84 | 85 | AnnoyIndex 86 | 87 |
    88 |
    89 |
    90 |
    91 |
    92 |
    93 |

    The primary generic class used to create an index.

    94 | 95 | See more 96 |
    97 |
    98 |

    Declaration

    99 |
    100 |

    Swift

    101 |
    public class AnnoyIndex<T> where T : AnnoyOperable
    102 | 103 |
    104 |
    105 |
    106 |
    107 |
  • 108 |
109 |
110 |
111 |
112 | 116 |
117 |
118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /docs/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* Credit to https://gist.github.com/wataru420/2048287 */ 2 | .highlight { 3 | /* Comment */ 4 | /* Error */ 5 | /* Keyword */ 6 | /* Operator */ 7 | /* Comment.Multiline */ 8 | /* Comment.Preproc */ 9 | /* Comment.Single */ 10 | /* Comment.Special */ 11 | /* Generic.Deleted */ 12 | /* Generic.Deleted.Specific */ 13 | /* Generic.Emph */ 14 | /* Generic.Error */ 15 | /* Generic.Heading */ 16 | /* Generic.Inserted */ 17 | /* Generic.Inserted.Specific */ 18 | /* Generic.Output */ 19 | /* Generic.Prompt */ 20 | /* Generic.Strong */ 21 | /* Generic.Subheading */ 22 | /* Generic.Traceback */ 23 | /* Keyword.Constant */ 24 | /* Keyword.Declaration */ 25 | /* Keyword.Pseudo */ 26 | /* Keyword.Reserved */ 27 | /* Keyword.Type */ 28 | /* Literal.Number */ 29 | /* Literal.String */ 30 | /* Name.Attribute */ 31 | /* Name.Builtin */ 32 | /* Name.Class */ 33 | /* Name.Constant */ 34 | /* Name.Entity */ 35 | /* Name.Exception */ 36 | /* Name.Function */ 37 | /* Name.Namespace */ 38 | /* Name.Tag */ 39 | /* Name.Variable */ 40 | /* Operator.Word */ 41 | /* Text.Whitespace */ 42 | /* Literal.Number.Float */ 43 | /* Literal.Number.Hex */ 44 | /* Literal.Number.Integer */ 45 | /* Literal.Number.Oct */ 46 | /* Literal.String.Backtick */ 47 | /* Literal.String.Char */ 48 | /* Literal.String.Doc */ 49 | /* Literal.String.Double */ 50 | /* Literal.String.Escape */ 51 | /* Literal.String.Heredoc */ 52 | /* Literal.String.Interpol */ 53 | /* Literal.String.Other */ 54 | /* Literal.String.Regex */ 55 | /* Literal.String.Single */ 56 | /* Literal.String.Symbol */ 57 | /* Name.Builtin.Pseudo */ 58 | /* Name.Variable.Class */ 59 | /* Name.Variable.Global */ 60 | /* Name.Variable.Instance */ 61 | /* Literal.Number.Integer.Long */ } 62 | .highlight .c { 63 | color: #999988; 64 | font-style: italic; } 65 | .highlight .err { 66 | color: #a61717; 67 | background-color: #e3d2d2; } 68 | .highlight .k { 69 | color: #000000; 70 | font-weight: bold; } 71 | .highlight .o { 72 | color: #000000; 73 | font-weight: bold; } 74 | .highlight .cm { 75 | color: #999988; 76 | font-style: italic; } 77 | .highlight .cp { 78 | color: #999999; 79 | font-weight: bold; } 80 | .highlight .c1 { 81 | color: #999988; 82 | font-style: italic; } 83 | .highlight .cs { 84 | color: #999999; 85 | font-weight: bold; 86 | font-style: italic; } 87 | .highlight .gd { 88 | color: #000000; 89 | background-color: #ffdddd; } 90 | .highlight .gd .x { 91 | color: #000000; 92 | background-color: #ffaaaa; } 93 | .highlight .ge { 94 | color: #000000; 95 | font-style: italic; } 96 | .highlight .gr { 97 | color: #aa0000; } 98 | .highlight .gh { 99 | color: #999999; } 100 | .highlight .gi { 101 | color: #000000; 102 | background-color: #ddffdd; } 103 | .highlight .gi .x { 104 | color: #000000; 105 | background-color: #aaffaa; } 106 | .highlight .go { 107 | color: #888888; } 108 | .highlight .gp { 109 | color: #555555; } 110 | .highlight .gs { 111 | font-weight: bold; } 112 | .highlight .gu { 113 | color: #aaaaaa; } 114 | .highlight .gt { 115 | color: #aa0000; } 116 | .highlight .kc { 117 | color: #000000; 118 | font-weight: bold; } 119 | .highlight .kd { 120 | color: #000000; 121 | font-weight: bold; } 122 | .highlight .kp { 123 | color: #000000; 124 | font-weight: bold; } 125 | .highlight .kr { 126 | color: #000000; 127 | font-weight: bold; } 128 | .highlight .kt { 129 | color: #445588; } 130 | .highlight .m { 131 | color: #009999; } 132 | .highlight .s { 133 | color: #d14; } 134 | .highlight .na { 135 | color: #008080; } 136 | .highlight .nb { 137 | color: #0086B3; } 138 | .highlight .nc { 139 | color: #445588; 140 | font-weight: bold; } 141 | .highlight .no { 142 | color: #008080; } 143 | .highlight .ni { 144 | color: #800080; } 145 | .highlight .ne { 146 | color: #990000; 147 | font-weight: bold; } 148 | .highlight .nf { 149 | color: #990000; } 150 | .highlight .nn { 151 | color: #555555; } 152 | .highlight .nt { 153 | color: #000080; } 154 | .highlight .nv { 155 | color: #008080; } 156 | .highlight .ow { 157 | color: #000000; 158 | font-weight: bold; } 159 | .highlight .w { 160 | color: #bbbbbb; } 161 | .highlight .mf { 162 | color: #009999; } 163 | .highlight .mh { 164 | color: #009999; } 165 | .highlight .mi { 166 | color: #009999; } 167 | .highlight .mo { 168 | color: #009999; } 169 | .highlight .sb { 170 | color: #d14; } 171 | .highlight .sc { 172 | color: #d14; } 173 | .highlight .sd { 174 | color: #d14; } 175 | .highlight .s2 { 176 | color: #d14; } 177 | .highlight .se { 178 | color: #d14; } 179 | .highlight .sh { 180 | color: #d14; } 181 | .highlight .si { 182 | color: #d14; } 183 | .highlight .sx { 184 | color: #d14; } 185 | .highlight .sr { 186 | color: #009926; } 187 | .highlight .s1 { 188 | color: #d14; } 189 | .highlight .ss { 190 | color: #990073; } 191 | .highlight .bp { 192 | color: #999999; } 193 | .highlight .vc { 194 | color: #008080; } 195 | .highlight .vg { 196 | color: #008080; } 197 | .highlight .vi { 198 | color: #008080; } 199 | .highlight .il { 200 | color: #009999; } 201 | -------------------------------------------------------------------------------- /docs/Enums.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Enumerations Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

Docs (83% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 70 |
71 |
72 |
73 |

Enumerations

74 |

The following enumerations are available globally.

75 | 76 |
77 |
78 |
79 |
    80 |
  • 81 |
    82 | 83 | 84 | 85 | AnnoyIndexError 86 | 87 |
    88 |
    89 |
    90 |
    91 |
    92 |
    93 |

    Errors thrown by AnnoyIndex.

    94 | 95 |

    values

    96 | 97 |

    invalidVectorLenght 98 | addItemFailed 99 | buildFailed 100 | unbuildFailed 101 | saveFailed 102 | loadFailed

    103 | 104 | See more 105 |
    106 |
    107 |

    Declaration

    108 |
    109 |

    Swift

    110 |
    public enum AnnoyIndexError : Error, LocalizedError
    111 | 112 |
    113 |
    114 |
    115 |
    116 |
  • 117 |
118 |
119 |
120 |
121 | 125 |
126 |
127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /docs/docsets/.docset/Contents/Resources/Documents/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* Credit to https://gist.github.com/wataru420/2048287 */ 2 | .highlight { 3 | /* Comment */ 4 | /* Error */ 5 | /* Keyword */ 6 | /* Operator */ 7 | /* Comment.Multiline */ 8 | /* Comment.Preproc */ 9 | /* Comment.Single */ 10 | /* Comment.Special */ 11 | /* Generic.Deleted */ 12 | /* Generic.Deleted.Specific */ 13 | /* Generic.Emph */ 14 | /* Generic.Error */ 15 | /* Generic.Heading */ 16 | /* Generic.Inserted */ 17 | /* Generic.Inserted.Specific */ 18 | /* Generic.Output */ 19 | /* Generic.Prompt */ 20 | /* Generic.Strong */ 21 | /* Generic.Subheading */ 22 | /* Generic.Traceback */ 23 | /* Keyword.Constant */ 24 | /* Keyword.Declaration */ 25 | /* Keyword.Pseudo */ 26 | /* Keyword.Reserved */ 27 | /* Keyword.Type */ 28 | /* Literal.Number */ 29 | /* Literal.String */ 30 | /* Name.Attribute */ 31 | /* Name.Builtin */ 32 | /* Name.Class */ 33 | /* Name.Constant */ 34 | /* Name.Entity */ 35 | /* Name.Exception */ 36 | /* Name.Function */ 37 | /* Name.Namespace */ 38 | /* Name.Tag */ 39 | /* Name.Variable */ 40 | /* Operator.Word */ 41 | /* Text.Whitespace */ 42 | /* Literal.Number.Float */ 43 | /* Literal.Number.Hex */ 44 | /* Literal.Number.Integer */ 45 | /* Literal.Number.Oct */ 46 | /* Literal.String.Backtick */ 47 | /* Literal.String.Char */ 48 | /* Literal.String.Doc */ 49 | /* Literal.String.Double */ 50 | /* Literal.String.Escape */ 51 | /* Literal.String.Heredoc */ 52 | /* Literal.String.Interpol */ 53 | /* Literal.String.Other */ 54 | /* Literal.String.Regex */ 55 | /* Literal.String.Single */ 56 | /* Literal.String.Symbol */ 57 | /* Name.Builtin.Pseudo */ 58 | /* Name.Variable.Class */ 59 | /* Name.Variable.Global */ 60 | /* Name.Variable.Instance */ 61 | /* Literal.Number.Integer.Long */ } 62 | .highlight .c { 63 | color: #999988; 64 | font-style: italic; } 65 | .highlight .err { 66 | color: #a61717; 67 | background-color: #e3d2d2; } 68 | .highlight .k { 69 | color: #000000; 70 | font-weight: bold; } 71 | .highlight .o { 72 | color: #000000; 73 | font-weight: bold; } 74 | .highlight .cm { 75 | color: #999988; 76 | font-style: italic; } 77 | .highlight .cp { 78 | color: #999999; 79 | font-weight: bold; } 80 | .highlight .c1 { 81 | color: #999988; 82 | font-style: italic; } 83 | .highlight .cs { 84 | color: #999999; 85 | font-weight: bold; 86 | font-style: italic; } 87 | .highlight .gd { 88 | color: #000000; 89 | background-color: #ffdddd; } 90 | .highlight .gd .x { 91 | color: #000000; 92 | background-color: #ffaaaa; } 93 | .highlight .ge { 94 | color: #000000; 95 | font-style: italic; } 96 | .highlight .gr { 97 | color: #aa0000; } 98 | .highlight .gh { 99 | color: #999999; } 100 | .highlight .gi { 101 | color: #000000; 102 | background-color: #ddffdd; } 103 | .highlight .gi .x { 104 | color: #000000; 105 | background-color: #aaffaa; } 106 | .highlight .go { 107 | color: #888888; } 108 | .highlight .gp { 109 | color: #555555; } 110 | .highlight .gs { 111 | font-weight: bold; } 112 | .highlight .gu { 113 | color: #aaaaaa; } 114 | .highlight .gt { 115 | color: #aa0000; } 116 | .highlight .kc { 117 | color: #000000; 118 | font-weight: bold; } 119 | .highlight .kd { 120 | color: #000000; 121 | font-weight: bold; } 122 | .highlight .kp { 123 | color: #000000; 124 | font-weight: bold; } 125 | .highlight .kr { 126 | color: #000000; 127 | font-weight: bold; } 128 | .highlight .kt { 129 | color: #445588; } 130 | .highlight .m { 131 | color: #009999; } 132 | .highlight .s { 133 | color: #d14; } 134 | .highlight .na { 135 | color: #008080; } 136 | .highlight .nb { 137 | color: #0086B3; } 138 | .highlight .nc { 139 | color: #445588; 140 | font-weight: bold; } 141 | .highlight .no { 142 | color: #008080; } 143 | .highlight .ni { 144 | color: #800080; } 145 | .highlight .ne { 146 | color: #990000; 147 | font-weight: bold; } 148 | .highlight .nf { 149 | color: #990000; } 150 | .highlight .nn { 151 | color: #555555; } 152 | .highlight .nt { 153 | color: #000080; } 154 | .highlight .nv { 155 | color: #008080; } 156 | .highlight .ow { 157 | color: #000000; 158 | font-weight: bold; } 159 | .highlight .w { 160 | color: #bbbbbb; } 161 | .highlight .mf { 162 | color: #009999; } 163 | .highlight .mh { 164 | color: #009999; } 165 | .highlight .mi { 166 | color: #009999; } 167 | .highlight .mo { 168 | color: #009999; } 169 | .highlight .sb { 170 | color: #d14; } 171 | .highlight .sc { 172 | color: #d14; } 173 | .highlight .sd { 174 | color: #d14; } 175 | .highlight .s2 { 176 | color: #d14; } 177 | .highlight .se { 178 | color: #d14; } 179 | .highlight .sh { 180 | color: #d14; } 181 | .highlight .si { 182 | color: #d14; } 183 | .highlight .sx { 184 | color: #d14; } 185 | .highlight .sr { 186 | color: #009926; } 187 | .highlight .s1 { 188 | color: #d14; } 189 | .highlight .ss { 190 | color: #990073; } 191 | .highlight .bp { 192 | color: #999999; } 193 | .highlight .vc { 194 | color: #008080; } 195 | .highlight .vg { 196 | color: #008080; } 197 | .highlight .vi { 198 | color: #008080; } 199 | .highlight .il { 200 | color: #009999; } 201 | -------------------------------------------------------------------------------- /docs/docsets/.docset/Contents/Resources/Documents/Enums.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Enumerations Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

Docs (83% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 70 |
71 |
72 |
73 |

Enumerations

74 |

The following enumerations are available globally.

75 | 76 |
77 |
78 |
79 |
    80 |
  • 81 |
    82 | 83 | 84 | 85 | AnnoyIndexError 86 | 87 |
    88 |
    89 |
    90 |
    91 |
    92 |
    93 |

    Errors thrown by AnnoyIndex.

    94 | 95 |

    values

    96 | 97 |

    invalidVectorLenght 98 | addItemFailed 99 | buildFailed 100 | unbuildFailed 101 | saveFailed 102 | loadFailed

    103 | 104 | See more 105 |
    106 |
    107 |

    Declaration

    108 |
    109 |

    Swift

    110 |
    public enum AnnoyIndexError : Error, LocalizedError
    111 | 112 |
    113 |
    114 |
    115 |
    116 |
  • 117 |
118 |
119 |
120 |
121 | 125 |
126 |
127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwiftAnnoy 2 | ![GitHub tag (latest SemVer pre-release)](https://img.shields.io/github/v/tag/jbadger3/SwiftAnnoy?include_prereleases) 3 | ![GitHub All Releases](https://img.shields.io/github/downloads/jbadger3/SwiftAnnoy/total) 4 | [![GitHub stars](https://img.shields.io/github/stars/jbadger3/SwiftAnnoy)](https://github.com/jbadger3/SwiftAnnoy/stargazers) 5 | [![GitHub license](https://img.shields.io/github/license/jbadger3/SwiftAnnoy)](https://github.com/jbadger3/SwiftAnnoy/blob/master/LICENSE) 6 | 7 | SwiftAnnoy provides an interface (bindings) to the [Annoy](https://github.com/spotify/annoy) (Approximate Nearest Neighbors Oh Yeah) C++ library. 8 | 9 | ### Supported data types 10 | - [ ] Float16 (half-precision) 11 | - [x] Float (32 bit) 12 | - [x] Double (64 bit) 13 | 14 | ### Supported distance metrics (similarity measures) 15 | - [x] angular 16 | - [x] dotProcut 17 | - [x] euclidean 18 | - [ ] hamming 19 | - [x] manhattan 20 | 21 | ## Background 22 | Nearest neighbors search (NNS) algorithms are used in a variety of tasks that include classification, clustering, image retreval, and recommendation. The fundamental task in NNS is to look for points (examples) in a data set that are closest to a given query using some metric of distance (manhattan, euclidean, etc.). Brute force search quickly becomes inefficient as the size of a dataset and number of dimensions in the data grow (see [the curse of dimensionality](https://en.wikipedia.org/wiki/Curse_of_dimensionality)). Searching can be made more efficient by intelligent indexing (k-d trees, ball tree, etc.), but even here large datasets quickly lead to large indexes, memory constraints, and suboptimal search speed. 23 | 24 | Approximate nearest neighbors algorithms make a trade-off in reduced accuracy for improved query performance. Annoy offers benefits in both speed and memory footprint (indexes are memory mapped) that make it an attractive alternative to brute force search and exact nearest neighbor methods. 25 | 26 | ## Installation 27 | Swift Package Manager (SPM) 28 | If you are using Xcode you can do the following: 29 | 1. Select File > Swift Packages > Add Package Dependency. 30 | 2. copy and paste https://github.com/jbadger3/SwiftAnnoy into the search URL 31 | 3. Select SwiftAnnoy and click next. 32 | 4. Choose a rule for dependency management. Click next. 33 | 5. Click Finish. 34 | 35 | ## Usage 36 | Using SwiftAnnoy is fairly straitforward. Follow the code snippets below to get started or have a look at the Jazzy generated [docs](https://jbadger3.github.io/SwiftAnnoy/). 37 | 38 | ### Create an index 39 | First, create an `AnnoyIndex` as in: 40 | ```swift 41 | let index = AnnoyIndex(itemLength: 2, metric: .euclidean) 42 | ``` 43 | Currently supported types are `Float` and `Double`. 44 | 45 | ### Adding items 46 | Next, add some data to your index. There are two functions that can be used to populate an index: `addItem` and `addItems`. 47 | ```swift 48 | var item0 = [1.0, 1.0] 49 | var item1 = [3.0, 4.0] 50 | var item2 = [6.0, 8.0] 51 | var items = [item0, item1, item2] 52 | 53 | // add one item 54 | try? index.addItem(index: 0, vector: &item0) 55 | 56 | // add multple items 57 | try? index.addItems(items: &items) 58 | ``` 59 | Note: Annoy expects indices in chronological order from 0...n-1. If you need/intend to use some other id numbering system create your own mapping as memory will be allocated for max(id) + 1 items. 60 | 61 | ### Build the index 62 | In order to run queries on an `AnnoyIndex` the index must first be built. 63 | ```swift 64 | try? index.build(numTrees: 1) 65 | ``` 66 | This is a one-time operation. You can't call `index.build` a second time. 67 | 68 | The parameter `numTrees` specifies the number of trees you want Annoy to use to construct the index. The more trees you include in the index the more accurate the search results will be, but it will take longer to build, take up more space, and require more time to search. Experiment with this parameter to optimize the tradeoffs. 69 | 70 | ### Running queries 71 | An `AnnoyIndex` can be queried using either an item index or a vector. 72 | ```swift 73 | // by item 74 | let results = index.getNNsForItem(item: 3, neighbors: 3) 75 | print(results) 76 | "Optional((indices: [3, 2, 0], distances: [0.0, 5.0, 8.602325267042627]))" 77 | 78 | // by vector 79 | var vector = [3.0, 4.0] 80 | let results2 = index.getNNsForVector(vector: &vector, neighbors: 3) 81 | print(results2) 82 | "Optional((indices: [2, 0, 1], distances: [0.0, 3.605551275463989, 3.605551275463989]))" 83 | ``` 84 | 85 | ### Saving an index 86 | ```swift 87 | let fileURL = FileManager.default.temporaryDirectory.appending(path: "index.annoy") 88 | try? index.save(fileURL) 89 | ``` 90 | 91 | ### Loading a saved index 92 | ```swift 93 | let fileURL = FileManager.default.temporaryDirectory.appending(path: "index.annoy") 94 | try? index.load(fileURL) 95 | 96 | ``` 97 | ## Contributing 98 | Improvements and suggestions are welcome. Have a look at the TODO list below or open up an issue for other areas of improvement. 99 | 100 | ## TODO 101 | 102 | ### Features 103 | - [ ] hamming distance 104 | - [ ] bounds checking for getItem, getNNsForItem, and getNNsForVector 105 | - [ ] half-precision support 106 | 107 | ### Unit testing 108 | - [ ] build 109 | - [ ] unbuild 110 | - [ ] unload 111 | - [ ] setVerbose 112 | - [ ] getItem 113 | - [ ] setSeed 114 | - [ ] onDiskBuild 115 | 116 | ### Memory management 117 | - [ ] test for memory leaks 118 | - [ ] verify mmap and prefault function properly 119 | 120 | ### OS/device testing 121 | - [ ] macOS 122 | - [ ] iOS 123 | - [ ] Linux 124 | 125 | ## License 126 | SwiftAnnoy is released with an MIT [license](https://github.com/jbadger3/SwiftAnnoy/blob/master/LICENSE). If you use SwiftAnnoy in another project, please add a link to this repository in your acknowledgements. 127 | -------------------------------------------------------------------------------- /docs/Extensions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Extensions Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

Docs (83% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 70 |
71 |
72 |
73 |

Extensions

74 |

The following extensions are available globally.

75 | 76 |
77 |
78 |
79 |
    80 |
  • 81 |
    82 | 83 | 84 | 85 | Float 86 | 87 |
    88 |
    89 |
    90 |
    91 |
    92 |
    93 | 94 |
    95 |
    96 |

    Declaration

    97 |
    98 |

    Swift

    99 |
    extension Float: AnnoyOperable
    100 | 101 |
    102 |
    103 |
    104 |
    105 |
  • 106 |
  • 107 |
    108 | 109 | 110 | 111 | Double 112 | 113 |
    114 |
    115 |
    116 |
    117 |
    118 |
    119 | 120 |
    121 |
    122 |

    Declaration

    123 |
    124 |

    Swift

    125 |
    extension Double: AnnoyOperable
    126 | 127 |
    128 |
    129 |
    130 |
    131 |
  • 132 |
133 |
134 |
135 |
136 | 140 |
141 |
142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /docs/docsets/.docset/Contents/Resources/Documents/Extensions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Extensions Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

Docs (83% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 70 |
71 |
72 |
73 |

Extensions

74 |

The following extensions are available globally.

75 | 76 |
77 |
78 |
79 |
    80 |
  • 81 |
    82 | 83 | 84 | 85 | Float 86 | 87 |
    88 |
    89 |
    90 |
    91 |
    92 |
    93 | 94 |
    95 |
    96 |

    Declaration

    97 |
    98 |

    Swift

    99 |
    extension Float: AnnoyOperable
    100 | 101 |
    102 |
    103 |
    104 |
    105 |
  • 106 |
  • 107 |
    108 | 109 | 110 | 111 | Double 112 | 113 |
    114 |
    115 |
    116 |
    117 |
    118 |
    119 | 120 |
    121 |
    122 |

    Declaration

    123 |
    124 |

    Swift

    125 |
    extension Double: AnnoyOperable
    126 | 127 |
    128 |
    129 |
    130 |
    131 |
  • 132 |
133 |
134 |
135 |
136 | 140 |
141 |
142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /docs/search.json: -------------------------------------------------------------------------------- 1 | {"Protocols.html#/s:10SwiftAnnoy0B8OperableP":{"name":"AnnoyOperable","abstract":"

Datatypes currently supported by SwiftAnnoy.

"},"Extensions.html#/s:Sf":{"name":"Float"},"Extensions.html#/s:Sd":{"name":"Double"},"Enums/AnnoyIndexError.html#/s:10SwiftAnnoy0B10IndexErrorO19invalidVectorLengthyACSS_tcACmF":{"name":"invalidVectorLength(message:)","abstract":"

Undocumented

","parent_name":"AnnoyIndexError"},"Enums/AnnoyIndexError.html#/s:10SwiftAnnoy0B10IndexErrorO13addItemFailedyA2CmF":{"name":"addItemFailed","abstract":"

Undocumented

","parent_name":"AnnoyIndexError"},"Enums/AnnoyIndexError.html#/s:10SwiftAnnoy0B10IndexErrorO11buildFailedyA2CmF":{"name":"buildFailed","abstract":"

Undocumented

","parent_name":"AnnoyIndexError"},"Enums/AnnoyIndexError.html#/s:10SwiftAnnoy0B10IndexErrorO13unbuildFailedyA2CmF":{"name":"unbuildFailed","abstract":"

Undocumented

","parent_name":"AnnoyIndexError"},"Enums/AnnoyIndexError.html#/s:10SwiftAnnoy0B10IndexErrorO10saveFailedyA2CmF":{"name":"saveFailed","abstract":"

Undocumented

","parent_name":"AnnoyIndexError"},"Enums/AnnoyIndexError.html#/s:10SwiftAnnoy0B10IndexErrorO10loadFailedyA2CmF":{"name":"loadFailed","abstract":"

Undocumented

","parent_name":"AnnoyIndexError"},"Enums/AnnoyIndexError.html#/s:10Foundation14LocalizedErrorP16errorDescriptionSSSgvp":{"name":"errorDescription","parent_name":"AnnoyIndexError"},"Enums/AnnoyIndexError.html":{"name":"AnnoyIndexError","abstract":"

Errors thrown by AnnoyIndex.

"},"Classes/AnnoyIndex/DistanceMetric.html#/s:10SwiftAnnoy0B5IndexC14DistanceMetricO7angularyAEyx_GAGmAA0B8OperableRzlF":{"name":"angular","abstract":"

The angular distance used is the Euclidean distance of normalized vectors, which is sqrt(2(1-cos(u,v)))

","parent_name":"DistanceMetric"},"Classes/AnnoyIndex/DistanceMetric.html#/s:10SwiftAnnoy0B5IndexC14DistanceMetricO10dotProductyAEyx_GAGmAA0B8OperableRzlF":{"name":"dotProduct","abstract":"

The dot product also called the inner product.

","parent_name":"DistanceMetric"},"Classes/AnnoyIndex/DistanceMetric.html#/s:10SwiftAnnoy0B5IndexC14DistanceMetricO9euclideanyAEyx_GAGmAA0B8OperableRzlF":{"name":"euclidean","abstract":"

The L2 or straight line distance as calculated using the Pythagorean formula

","parent_name":"DistanceMetric"},"Classes/AnnoyIndex/DistanceMetric.html#/s:10SwiftAnnoy0B5IndexC14DistanceMetricO9manhattanyAEyx_GAGmAA0B8OperableRzlF":{"name":"manhattan","abstract":"

The L1 or city block distance

","parent_name":"DistanceMetric"},"Classes/AnnoyIndex/DistanceMetric.html":{"name":"DistanceMetric","abstract":"

Supported distance metrics / similarity measures

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC10itemLengthSivp":{"name":"itemLength","abstract":"

The length of each vector (Array count if you wish) for each item in the index.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC14distanceMetricSays4Int8VGvp":{"name":"distanceMetric","abstract":"

The distance metric used to measure the similarity between vectors.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC8dataTypeSays4Int8VGvp":{"name":"dataType","abstract":"

The type of data being used in each vector.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC13numberOfItemsSivp":{"name":"numberOfItems","abstract":"

The number of items in the index.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC13numberOfTreesSivp":{"name":"numberOfTrees","abstract":"

The number of trees in the index. (if built)

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC10itemLength6metricACyxGSi_AC14DistanceMetricOyx_Gtcfc":{"name":"init(itemLength:metric:)","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC7addItem5index6vectorySi_SayxGztKF":{"name":"addItem(index:vector:)","abstract":"

Adds an a new item to the index.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC8addItems5itemsySaySayxGGz_tKF":{"name":"addItems(items:)","abstract":"

Adds multiple new items to the index.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC5build8numTreesySi_tKF":{"name":"build(numTrees:)","abstract":"

Builds the index to allow for fast approximate nearest neighbors lookup.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC7unbuildyyKF":{"name":"unbuild()","abstract":"

Unbuilds the current AnnoyIndex which allows additional items to be added or for the index to be rebuilt with a","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC4save3url8prefaulty10Foundation3URLV_SbtKF":{"name":"save(url:prefault:)","abstract":"

Saves the current index to a file then immediately loads (memory maps) the index from disk.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC6unloadyyF":{"name":"unload()","abstract":"

Unloads the underlying index and all associated items.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC4load3urly10Foundation3URLV_tKF":{"name":"load(url:)","abstract":"

Loads a previously saved index.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC11getDistance5item15item2xSgSi_SitF":{"name":"getDistance(item1:item2:)","abstract":"

Calculates the distance between two items in the index.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC13getNNsForItem4item9neighbors8search_kSaySiG7indices_SayxG9distancestSgSi_S2itF":{"name":"getNNsForItem(item:neighbors:search_k:)","abstract":"

Gathers the approximate nearest neighbors for an item.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC15getNNsForVector6vector9neighbors8search_kSaySiG7indices_SayxG9distancestSgAJz_S2itF":{"name":"getNNsForVector(vector:neighbors:search_k:)","abstract":"

Gathers the approximate nearest neighbors for a vector.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC9setVerbos7boolValySb_tF":{"name":"setVerbos(boolVal:)","abstract":"

When set to true provides additional information about operations carried out by Annoy via stdout.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC7getItem5indexSayxGSgSi_tF":{"name":"getItem(index:)","abstract":"

Retrieves the vector for an item in the index.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC7setSeed7seedValySi_tF":{"name":"setSeed(seedVal:)","abstract":"

Sets the random seed used for generating the index. Setting this value is useful when running performance testing to ensure consistent results.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC11onDiskBuild3urly10Foundation3URLV_tF":{"name":"onDiskBuild(url:)","abstract":"

Prepares the index to be built on disk rather than in RAM. Should be set before adding items to the index.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html":{"name":"AnnoyIndex","abstract":"

The primary generic class used to create an index.

"},"Classes.html":{"name":"Classes","abstract":"

The following classes are available globally.

"},"Enums.html":{"name":"Enumerations","abstract":"

The following enumerations are available globally.

"},"Extensions.html":{"name":"Extensions","abstract":"

The following extensions are available globally.

"},"Protocols.html":{"name":"Protocols","abstract":"

The following protocols are available globally.

"}} -------------------------------------------------------------------------------- /docs/docsets/.docset/Contents/Resources/Documents/search.json: -------------------------------------------------------------------------------- 1 | {"Protocols.html#/s:10SwiftAnnoy0B8OperableP":{"name":"AnnoyOperable","abstract":"

Datatypes currently supported by SwiftAnnoy.

"},"Extensions.html#/s:Sf":{"name":"Float"},"Extensions.html#/s:Sd":{"name":"Double"},"Enums/AnnoyIndexError.html#/s:10SwiftAnnoy0B10IndexErrorO19invalidVectorLengthyACSS_tcACmF":{"name":"invalidVectorLength(message:)","abstract":"

Undocumented

","parent_name":"AnnoyIndexError"},"Enums/AnnoyIndexError.html#/s:10SwiftAnnoy0B10IndexErrorO13addItemFailedyA2CmF":{"name":"addItemFailed","abstract":"

Undocumented

","parent_name":"AnnoyIndexError"},"Enums/AnnoyIndexError.html#/s:10SwiftAnnoy0B10IndexErrorO11buildFailedyA2CmF":{"name":"buildFailed","abstract":"

Undocumented

","parent_name":"AnnoyIndexError"},"Enums/AnnoyIndexError.html#/s:10SwiftAnnoy0B10IndexErrorO13unbuildFailedyA2CmF":{"name":"unbuildFailed","abstract":"

Undocumented

","parent_name":"AnnoyIndexError"},"Enums/AnnoyIndexError.html#/s:10SwiftAnnoy0B10IndexErrorO10saveFailedyA2CmF":{"name":"saveFailed","abstract":"

Undocumented

","parent_name":"AnnoyIndexError"},"Enums/AnnoyIndexError.html#/s:10SwiftAnnoy0B10IndexErrorO10loadFailedyA2CmF":{"name":"loadFailed","abstract":"

Undocumented

","parent_name":"AnnoyIndexError"},"Enums/AnnoyIndexError.html#/s:10Foundation14LocalizedErrorP16errorDescriptionSSSgvp":{"name":"errorDescription","parent_name":"AnnoyIndexError"},"Enums/AnnoyIndexError.html":{"name":"AnnoyIndexError","abstract":"

Errors thrown by AnnoyIndex.

"},"Classes/AnnoyIndex/DistanceMetric.html#/s:10SwiftAnnoy0B5IndexC14DistanceMetricO7angularyAEyx_GAGmAA0B8OperableRzlF":{"name":"angular","abstract":"

The angular distance used is the Euclidean distance of normalized vectors, which is sqrt(2(1-cos(u,v)))

","parent_name":"DistanceMetric"},"Classes/AnnoyIndex/DistanceMetric.html#/s:10SwiftAnnoy0B5IndexC14DistanceMetricO10dotProductyAEyx_GAGmAA0B8OperableRzlF":{"name":"dotProduct","abstract":"

The dot product also called the inner product.

","parent_name":"DistanceMetric"},"Classes/AnnoyIndex/DistanceMetric.html#/s:10SwiftAnnoy0B5IndexC14DistanceMetricO9euclideanyAEyx_GAGmAA0B8OperableRzlF":{"name":"euclidean","abstract":"

The L2 or straight line distance as calculated using the Pythagorean formula

","parent_name":"DistanceMetric"},"Classes/AnnoyIndex/DistanceMetric.html#/s:10SwiftAnnoy0B5IndexC14DistanceMetricO9manhattanyAEyx_GAGmAA0B8OperableRzlF":{"name":"manhattan","abstract":"

The L1 or city block distance

","parent_name":"DistanceMetric"},"Classes/AnnoyIndex/DistanceMetric.html":{"name":"DistanceMetric","abstract":"

Supported distance metrics / similarity measures

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC10itemLengthSivp":{"name":"itemLength","abstract":"

The length of each vector (Array count if you wish) for each item in the index.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC14distanceMetricSays4Int8VGvp":{"name":"distanceMetric","abstract":"

The distance metric used to measure the similarity between vectors.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC8dataTypeSays4Int8VGvp":{"name":"dataType","abstract":"

The type of data being used in each vector.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC13numberOfItemsSivp":{"name":"numberOfItems","abstract":"

The number of items in the index.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC13numberOfTreesSivp":{"name":"numberOfTrees","abstract":"

The number of trees in the index. (if built)

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC10itemLength6metricACyxGSi_AC14DistanceMetricOyx_Gtcfc":{"name":"init(itemLength:metric:)","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC7addItem5index6vectorySi_SayxGztKF":{"name":"addItem(index:vector:)","abstract":"

Adds an a new item to the index.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC8addItems5itemsySaySayxGGz_tKF":{"name":"addItems(items:)","abstract":"

Adds multiple new items to the index.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC5build8numTreesySi_tKF":{"name":"build(numTrees:)","abstract":"

Builds the index to allow for fast approximate nearest neighbors lookup.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC7unbuildyyKF":{"name":"unbuild()","abstract":"

Unbuilds the current AnnoyIndex which allows additional items to be added or for the index to be rebuilt with a","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC4save3url8prefaulty10Foundation3URLV_SbtKF":{"name":"save(url:prefault:)","abstract":"

Saves the current index to a file then immediately loads (memory maps) the index from disk.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC6unloadyyF":{"name":"unload()","abstract":"

Unloads the underlying index and all associated items.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC4load3urly10Foundation3URLV_tKF":{"name":"load(url:)","abstract":"

Loads a previously saved index.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC11getDistance5item15item2xSgSi_SitF":{"name":"getDistance(item1:item2:)","abstract":"

Calculates the distance between two items in the index.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC13getNNsForItem4item9neighbors8search_kSaySiG7indices_SayxG9distancestSgSi_S2itF":{"name":"getNNsForItem(item:neighbors:search_k:)","abstract":"

Gathers the approximate nearest neighbors for an item.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC15getNNsForVector6vector9neighbors8search_kSaySiG7indices_SayxG9distancestSgAJz_S2itF":{"name":"getNNsForVector(vector:neighbors:search_k:)","abstract":"

Gathers the approximate nearest neighbors for a vector.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC9setVerbos7boolValySb_tF":{"name":"setVerbos(boolVal:)","abstract":"

When set to true provides additional information about operations carried out by Annoy via stdout.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC7getItem5indexSayxGSgSi_tF":{"name":"getItem(index:)","abstract":"

Retrieves the vector for an item in the index.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC7setSeed7seedValySi_tF":{"name":"setSeed(seedVal:)","abstract":"

Sets the random seed used for generating the index. Setting this value is useful when running performance testing to ensure consistent results.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html#/s:10SwiftAnnoy0B5IndexC11onDiskBuild3urly10Foundation3URLV_tF":{"name":"onDiskBuild(url:)","abstract":"

Prepares the index to be built on disk rather than in RAM. Should be set before adding items to the index.

","parent_name":"AnnoyIndex"},"Classes/AnnoyIndex.html":{"name":"AnnoyIndex","abstract":"

The primary generic class used to create an index.

"},"Classes.html":{"name":"Classes","abstract":"

The following classes are available globally.

"},"Enums.html":{"name":"Enumerations","abstract":"

The following enumerations are available globally.

"},"Extensions.html":{"name":"Extensions","abstract":"

The following extensions are available globally.

"},"Protocols.html":{"name":"Protocols","abstract":"

The following protocols are available globally.

"}} -------------------------------------------------------------------------------- /docs/css/jazzy.css: -------------------------------------------------------------------------------- 1 | html, body, div, span, h1, h3, h4, p, a, code, em, img, ul, li, table, tbody, tr, td { 2 | background: transparent; 3 | border: 0; 4 | margin: 0; 5 | outline: 0; 6 | padding: 0; 7 | vertical-align: baseline; } 8 | 9 | body { 10 | background-color: #f2f2f2; 11 | font-family: Helvetica, freesans, Arial, sans-serif; 12 | font-size: 14px; 13 | -webkit-font-smoothing: subpixel-antialiased; 14 | word-wrap: break-word; } 15 | 16 | h1, h2, h3 { 17 | margin-top: 0.8em; 18 | margin-bottom: 0.3em; 19 | font-weight: 100; 20 | color: black; } 21 | 22 | h1 { 23 | font-size: 2.5em; } 24 | 25 | h2 { 26 | font-size: 2em; 27 | border-bottom: 1px solid #e2e2e2; } 28 | 29 | h4 { 30 | font-size: 13px; 31 | line-height: 1.5; 32 | margin-top: 21px; } 33 | 34 | h5 { 35 | font-size: 1.1em; } 36 | 37 | h6 { 38 | font-size: 1.1em; 39 | color: #777; } 40 | 41 | .section-name { 42 | color: gray; 43 | display: block; 44 | font-family: Helvetica; 45 | font-size: 22px; 46 | font-weight: 100; 47 | margin-bottom: 15px; } 48 | 49 | pre, code { 50 | font: 0.95em Menlo, monospace; 51 | color: #777; 52 | word-wrap: normal; } 53 | 54 | p code, li code { 55 | background-color: #eee; 56 | padding: 2px 4px; 57 | border-radius: 4px; } 58 | 59 | a { 60 | color: #0088cc; 61 | text-decoration: none; } 62 | 63 | ul { 64 | padding-left: 15px; } 65 | 66 | li { 67 | line-height: 1.8em; } 68 | 69 | img { 70 | max-width: 100%; } 71 | 72 | blockquote { 73 | margin-left: 0; 74 | padding: 0 10px; 75 | border-left: 4px solid #ccc; } 76 | 77 | .content-wrapper { 78 | margin: 0 auto; 79 | width: 980px; } 80 | 81 | header { 82 | font-size: 0.85em; 83 | line-height: 26px; 84 | background-color: #414141; 85 | position: fixed; 86 | width: 100%; 87 | z-index: 2; } 88 | header img { 89 | padding-right: 6px; 90 | vertical-align: -4px; 91 | height: 16px; } 92 | header a { 93 | color: #fff; } 94 | header p { 95 | float: left; 96 | color: #999; } 97 | header .header-right { 98 | float: right; 99 | margin-left: 16px; } 100 | 101 | #breadcrumbs { 102 | background-color: #f2f2f2; 103 | height: 27px; 104 | padding-top: 17px; 105 | position: fixed; 106 | width: 100%; 107 | z-index: 2; 108 | margin-top: 26px; } 109 | #breadcrumbs #carat { 110 | height: 10px; 111 | margin: 0 5px; } 112 | 113 | .sidebar { 114 | background-color: #f9f9f9; 115 | border: 1px solid #e2e2e2; 116 | overflow-y: auto; 117 | overflow-x: hidden; 118 | position: fixed; 119 | top: 70px; 120 | bottom: 0; 121 | width: 230px; 122 | word-wrap: normal; } 123 | 124 | .nav-groups { 125 | list-style-type: none; 126 | background: #fff; 127 | padding-left: 0; } 128 | 129 | .nav-group-name { 130 | border-bottom: 1px solid #e2e2e2; 131 | font-size: 1.1em; 132 | font-weight: 100; 133 | padding: 15px 0 15px 20px; } 134 | .nav-group-name > a { 135 | color: #333; } 136 | 137 | .nav-group-tasks { 138 | margin-top: 5px; } 139 | 140 | .nav-group-task { 141 | font-size: 0.9em; 142 | list-style-type: none; 143 | white-space: nowrap; } 144 | .nav-group-task a { 145 | color: #888; } 146 | 147 | .main-content { 148 | background-color: #fff; 149 | border: 1px solid #e2e2e2; 150 | margin-left: 246px; 151 | position: absolute; 152 | overflow: hidden; 153 | padding-bottom: 20px; 154 | top: 70px; 155 | width: 734px; } 156 | .main-content p, .main-content a, .main-content code, .main-content em, .main-content ul, .main-content table, .main-content blockquote { 157 | margin-bottom: 1em; } 158 | .main-content p { 159 | line-height: 1.8em; } 160 | .main-content section .section:first-child { 161 | margin-top: 0; 162 | padding-top: 0; } 163 | .main-content section .task-group-section .task-group:first-of-type { 164 | padding-top: 10px; } 165 | .main-content section .task-group-section .task-group:first-of-type .section-name { 166 | padding-top: 15px; } 167 | .main-content section .heading:before { 168 | content: ""; 169 | display: block; 170 | padding-top: 70px; 171 | margin: -70px 0 0; } 172 | .main-content .section-name p { 173 | margin-bottom: inherit; 174 | line-height: inherit; } 175 | .main-content .section-name code { 176 | background-color: inherit; 177 | padding: inherit; 178 | color: inherit; } 179 | 180 | .section { 181 | padding: 0 25px; } 182 | 183 | .highlight { 184 | background-color: #eee; 185 | padding: 10px 12px; 186 | border: 1px solid #e2e2e2; 187 | border-radius: 4px; 188 | overflow-x: auto; } 189 | 190 | .declaration .highlight { 191 | overflow-x: initial; 192 | padding: 0 40px 40px 0; 193 | margin-bottom: -25px; 194 | background-color: transparent; 195 | border: none; } 196 | 197 | .section-name { 198 | margin: 0; 199 | margin-left: 18px; } 200 | 201 | .task-group-section { 202 | padding-left: 6px; 203 | border-top: 1px solid #e2e2e2; } 204 | 205 | .task-group { 206 | padding-top: 0px; } 207 | 208 | .task-name-container a[name]:before { 209 | content: ""; 210 | display: block; 211 | padding-top: 70px; 212 | margin: -70px 0 0; } 213 | 214 | .section-name-container { 215 | position: relative; 216 | display: inline-block; } 217 | .section-name-container .section-name-link { 218 | position: absolute; 219 | top: 0; 220 | left: 0; 221 | bottom: 0; 222 | right: 0; 223 | margin-bottom: 0; } 224 | .section-name-container .section-name { 225 | position: relative; 226 | pointer-events: none; 227 | z-index: 1; } 228 | .section-name-container .section-name a { 229 | pointer-events: auto; } 230 | 231 | .item { 232 | padding-top: 8px; 233 | width: 100%; 234 | list-style-type: none; } 235 | .item a[name]:before { 236 | content: ""; 237 | display: block; 238 | padding-top: 70px; 239 | margin: -70px 0 0; } 240 | .item code { 241 | background-color: transparent; 242 | padding: 0; } 243 | .item .token, .item .direct-link { 244 | display: inline-block; 245 | text-indent: -20px; 246 | padding-left: 3px; 247 | margin-left: 35px; 248 | font-size: 11.9px; 249 | transition: all 300ms; } 250 | .item .token-open { 251 | margin-left: 20px; } 252 | .item .discouraged { 253 | text-decoration: line-through; } 254 | .item .declaration-note { 255 | font-size: .85em; 256 | color: gray; 257 | font-style: italic; } 258 | 259 | .pointer-container { 260 | border-bottom: 1px solid #e2e2e2; 261 | left: -23px; 262 | padding-bottom: 13px; 263 | position: relative; 264 | width: 110%; } 265 | 266 | .pointer { 267 | background: #f9f9f9; 268 | border-left: 1px solid #e2e2e2; 269 | border-top: 1px solid #e2e2e2; 270 | height: 12px; 271 | left: 21px; 272 | top: -7px; 273 | -webkit-transform: rotate(45deg); 274 | -moz-transform: rotate(45deg); 275 | -o-transform: rotate(45deg); 276 | transform: rotate(45deg); 277 | position: absolute; 278 | width: 12px; } 279 | 280 | .height-container { 281 | display: none; 282 | left: -25px; 283 | padding: 0 25px; 284 | position: relative; 285 | width: 100%; 286 | overflow: hidden; } 287 | .height-container .section { 288 | background: #f9f9f9; 289 | border-bottom: 1px solid #e2e2e2; 290 | left: -25px; 291 | position: relative; 292 | width: 100%; 293 | padding-top: 10px; 294 | padding-bottom: 5px; } 295 | 296 | .aside, .language { 297 | padding: 6px 12px; 298 | margin: 12px 0; 299 | border-left: 5px solid #dddddd; 300 | overflow-y: hidden; } 301 | .aside .aside-title, .language .aside-title { 302 | font-size: 9px; 303 | letter-spacing: 2px; 304 | text-transform: uppercase; 305 | padding-bottom: 0; 306 | margin: 0; 307 | color: #aaa; 308 | -webkit-user-select: none; } 309 | .aside p:last-child, .language p:last-child { 310 | margin-bottom: 0; } 311 | 312 | .language { 313 | border-left: 5px solid #cde9f4; } 314 | .language .aside-title { 315 | color: #4b8afb; } 316 | 317 | .aside-warning, .aside-deprecated, .aside-unavailable { 318 | border-left: 5px solid #ff6666; } 319 | .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { 320 | color: #ff0000; } 321 | 322 | .graybox { 323 | border-collapse: collapse; 324 | width: 100%; } 325 | .graybox p { 326 | margin: 0; 327 | word-break: break-word; 328 | min-width: 50px; } 329 | .graybox td { 330 | border: 1px solid #e2e2e2; 331 | padding: 5px 25px 5px 10px; 332 | vertical-align: middle; } 333 | .graybox tr td:first-of-type { 334 | text-align: right; 335 | padding: 7px; 336 | vertical-align: top; 337 | word-break: normal; 338 | width: 40px; } 339 | 340 | .slightly-smaller { 341 | font-size: 0.9em; } 342 | 343 | #footer { 344 | position: relative; 345 | top: 10px; 346 | bottom: 0px; 347 | margin-left: 25px; } 348 | #footer p { 349 | margin: 0; 350 | color: #aaa; 351 | font-size: 0.8em; } 352 | 353 | html.dash header, html.dash #breadcrumbs, html.dash .sidebar { 354 | display: none; } 355 | 356 | html.dash .main-content { 357 | width: 980px; 358 | margin-left: 0; 359 | border: none; 360 | width: 100%; 361 | top: 0; 362 | padding-bottom: 0; } 363 | 364 | html.dash .height-container { 365 | display: block; } 366 | 367 | html.dash .item .token { 368 | margin-left: 0; } 369 | 370 | html.dash .content-wrapper { 371 | width: auto; } 372 | 373 | html.dash #footer { 374 | position: static; } 375 | -------------------------------------------------------------------------------- /docs/docsets/.docset/Contents/Resources/Documents/css/jazzy.css: -------------------------------------------------------------------------------- 1 | html, body, div, span, h1, h3, h4, p, a, code, em, img, ul, li, table, tbody, tr, td { 2 | background: transparent; 3 | border: 0; 4 | margin: 0; 5 | outline: 0; 6 | padding: 0; 7 | vertical-align: baseline; } 8 | 9 | body { 10 | background-color: #f2f2f2; 11 | font-family: Helvetica, freesans, Arial, sans-serif; 12 | font-size: 14px; 13 | -webkit-font-smoothing: subpixel-antialiased; 14 | word-wrap: break-word; } 15 | 16 | h1, h2, h3 { 17 | margin-top: 0.8em; 18 | margin-bottom: 0.3em; 19 | font-weight: 100; 20 | color: black; } 21 | 22 | h1 { 23 | font-size: 2.5em; } 24 | 25 | h2 { 26 | font-size: 2em; 27 | border-bottom: 1px solid #e2e2e2; } 28 | 29 | h4 { 30 | font-size: 13px; 31 | line-height: 1.5; 32 | margin-top: 21px; } 33 | 34 | h5 { 35 | font-size: 1.1em; } 36 | 37 | h6 { 38 | font-size: 1.1em; 39 | color: #777; } 40 | 41 | .section-name { 42 | color: gray; 43 | display: block; 44 | font-family: Helvetica; 45 | font-size: 22px; 46 | font-weight: 100; 47 | margin-bottom: 15px; } 48 | 49 | pre, code { 50 | font: 0.95em Menlo, monospace; 51 | color: #777; 52 | word-wrap: normal; } 53 | 54 | p code, li code { 55 | background-color: #eee; 56 | padding: 2px 4px; 57 | border-radius: 4px; } 58 | 59 | a { 60 | color: #0088cc; 61 | text-decoration: none; } 62 | 63 | ul { 64 | padding-left: 15px; } 65 | 66 | li { 67 | line-height: 1.8em; } 68 | 69 | img { 70 | max-width: 100%; } 71 | 72 | blockquote { 73 | margin-left: 0; 74 | padding: 0 10px; 75 | border-left: 4px solid #ccc; } 76 | 77 | .content-wrapper { 78 | margin: 0 auto; 79 | width: 980px; } 80 | 81 | header { 82 | font-size: 0.85em; 83 | line-height: 26px; 84 | background-color: #414141; 85 | position: fixed; 86 | width: 100%; 87 | z-index: 2; } 88 | header img { 89 | padding-right: 6px; 90 | vertical-align: -4px; 91 | height: 16px; } 92 | header a { 93 | color: #fff; } 94 | header p { 95 | float: left; 96 | color: #999; } 97 | header .header-right { 98 | float: right; 99 | margin-left: 16px; } 100 | 101 | #breadcrumbs { 102 | background-color: #f2f2f2; 103 | height: 27px; 104 | padding-top: 17px; 105 | position: fixed; 106 | width: 100%; 107 | z-index: 2; 108 | margin-top: 26px; } 109 | #breadcrumbs #carat { 110 | height: 10px; 111 | margin: 0 5px; } 112 | 113 | .sidebar { 114 | background-color: #f9f9f9; 115 | border: 1px solid #e2e2e2; 116 | overflow-y: auto; 117 | overflow-x: hidden; 118 | position: fixed; 119 | top: 70px; 120 | bottom: 0; 121 | width: 230px; 122 | word-wrap: normal; } 123 | 124 | .nav-groups { 125 | list-style-type: none; 126 | background: #fff; 127 | padding-left: 0; } 128 | 129 | .nav-group-name { 130 | border-bottom: 1px solid #e2e2e2; 131 | font-size: 1.1em; 132 | font-weight: 100; 133 | padding: 15px 0 15px 20px; } 134 | .nav-group-name > a { 135 | color: #333; } 136 | 137 | .nav-group-tasks { 138 | margin-top: 5px; } 139 | 140 | .nav-group-task { 141 | font-size: 0.9em; 142 | list-style-type: none; 143 | white-space: nowrap; } 144 | .nav-group-task a { 145 | color: #888; } 146 | 147 | .main-content { 148 | background-color: #fff; 149 | border: 1px solid #e2e2e2; 150 | margin-left: 246px; 151 | position: absolute; 152 | overflow: hidden; 153 | padding-bottom: 20px; 154 | top: 70px; 155 | width: 734px; } 156 | .main-content p, .main-content a, .main-content code, .main-content em, .main-content ul, .main-content table, .main-content blockquote { 157 | margin-bottom: 1em; } 158 | .main-content p { 159 | line-height: 1.8em; } 160 | .main-content section .section:first-child { 161 | margin-top: 0; 162 | padding-top: 0; } 163 | .main-content section .task-group-section .task-group:first-of-type { 164 | padding-top: 10px; } 165 | .main-content section .task-group-section .task-group:first-of-type .section-name { 166 | padding-top: 15px; } 167 | .main-content section .heading:before { 168 | content: ""; 169 | display: block; 170 | padding-top: 70px; 171 | margin: -70px 0 0; } 172 | .main-content .section-name p { 173 | margin-bottom: inherit; 174 | line-height: inherit; } 175 | .main-content .section-name code { 176 | background-color: inherit; 177 | padding: inherit; 178 | color: inherit; } 179 | 180 | .section { 181 | padding: 0 25px; } 182 | 183 | .highlight { 184 | background-color: #eee; 185 | padding: 10px 12px; 186 | border: 1px solid #e2e2e2; 187 | border-radius: 4px; 188 | overflow-x: auto; } 189 | 190 | .declaration .highlight { 191 | overflow-x: initial; 192 | padding: 0 40px 40px 0; 193 | margin-bottom: -25px; 194 | background-color: transparent; 195 | border: none; } 196 | 197 | .section-name { 198 | margin: 0; 199 | margin-left: 18px; } 200 | 201 | .task-group-section { 202 | padding-left: 6px; 203 | border-top: 1px solid #e2e2e2; } 204 | 205 | .task-group { 206 | padding-top: 0px; } 207 | 208 | .task-name-container a[name]:before { 209 | content: ""; 210 | display: block; 211 | padding-top: 70px; 212 | margin: -70px 0 0; } 213 | 214 | .section-name-container { 215 | position: relative; 216 | display: inline-block; } 217 | .section-name-container .section-name-link { 218 | position: absolute; 219 | top: 0; 220 | left: 0; 221 | bottom: 0; 222 | right: 0; 223 | margin-bottom: 0; } 224 | .section-name-container .section-name { 225 | position: relative; 226 | pointer-events: none; 227 | z-index: 1; } 228 | .section-name-container .section-name a { 229 | pointer-events: auto; } 230 | 231 | .item { 232 | padding-top: 8px; 233 | width: 100%; 234 | list-style-type: none; } 235 | .item a[name]:before { 236 | content: ""; 237 | display: block; 238 | padding-top: 70px; 239 | margin: -70px 0 0; } 240 | .item code { 241 | background-color: transparent; 242 | padding: 0; } 243 | .item .token, .item .direct-link { 244 | display: inline-block; 245 | text-indent: -20px; 246 | padding-left: 3px; 247 | margin-left: 35px; 248 | font-size: 11.9px; 249 | transition: all 300ms; } 250 | .item .token-open { 251 | margin-left: 20px; } 252 | .item .discouraged { 253 | text-decoration: line-through; } 254 | .item .declaration-note { 255 | font-size: .85em; 256 | color: gray; 257 | font-style: italic; } 258 | 259 | .pointer-container { 260 | border-bottom: 1px solid #e2e2e2; 261 | left: -23px; 262 | padding-bottom: 13px; 263 | position: relative; 264 | width: 110%; } 265 | 266 | .pointer { 267 | background: #f9f9f9; 268 | border-left: 1px solid #e2e2e2; 269 | border-top: 1px solid #e2e2e2; 270 | height: 12px; 271 | left: 21px; 272 | top: -7px; 273 | -webkit-transform: rotate(45deg); 274 | -moz-transform: rotate(45deg); 275 | -o-transform: rotate(45deg); 276 | transform: rotate(45deg); 277 | position: absolute; 278 | width: 12px; } 279 | 280 | .height-container { 281 | display: none; 282 | left: -25px; 283 | padding: 0 25px; 284 | position: relative; 285 | width: 100%; 286 | overflow: hidden; } 287 | .height-container .section { 288 | background: #f9f9f9; 289 | border-bottom: 1px solid #e2e2e2; 290 | left: -25px; 291 | position: relative; 292 | width: 100%; 293 | padding-top: 10px; 294 | padding-bottom: 5px; } 295 | 296 | .aside, .language { 297 | padding: 6px 12px; 298 | margin: 12px 0; 299 | border-left: 5px solid #dddddd; 300 | overflow-y: hidden; } 301 | .aside .aside-title, .language .aside-title { 302 | font-size: 9px; 303 | letter-spacing: 2px; 304 | text-transform: uppercase; 305 | padding-bottom: 0; 306 | margin: 0; 307 | color: #aaa; 308 | -webkit-user-select: none; } 309 | .aside p:last-child, .language p:last-child { 310 | margin-bottom: 0; } 311 | 312 | .language { 313 | border-left: 5px solid #cde9f4; } 314 | .language .aside-title { 315 | color: #4b8afb; } 316 | 317 | .aside-warning, .aside-deprecated, .aside-unavailable { 318 | border-left: 5px solid #ff6666; } 319 | .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { 320 | color: #ff0000; } 321 | 322 | .graybox { 323 | border-collapse: collapse; 324 | width: 100%; } 325 | .graybox p { 326 | margin: 0; 327 | word-break: break-word; 328 | min-width: 50px; } 329 | .graybox td { 330 | border: 1px solid #e2e2e2; 331 | padding: 5px 25px 5px 10px; 332 | vertical-align: middle; } 333 | .graybox tr td:first-of-type { 334 | text-align: right; 335 | padding: 7px; 336 | vertical-align: top; 337 | word-break: normal; 338 | width: 40px; } 339 | 340 | .slightly-smaller { 341 | font-size: 0.9em; } 342 | 343 | #footer { 344 | position: relative; 345 | top: 10px; 346 | bottom: 0px; 347 | margin-left: 25px; } 348 | #footer p { 349 | margin: 0; 350 | color: #aaa; 351 | font-size: 0.8em; } 352 | 353 | html.dash header, html.dash #breadcrumbs, html.dash .sidebar { 354 | display: none; } 355 | 356 | html.dash .main-content { 357 | width: 980px; 358 | margin-left: 0; 359 | border: none; 360 | width: 100%; 361 | top: 0; 362 | padding-bottom: 0; } 363 | 364 | html.dash .height-container { 365 | display: block; } 366 | 367 | html.dash .item .token { 368 | margin-left: 0; } 369 | 370 | html.dash .content-wrapper { 371 | width: auto; } 372 | 373 | html.dash #footer { 374 | position: static; } 375 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |

Docs (83% documented)

17 |
18 |
19 |
20 | 25 |
26 |
27 | 69 |
70 |
71 |
72 | 73 |

SwiftAnnoy

74 | 75 |

SwiftAnnoy provides an interface (bindings) to the Annoy (Approximate Nearest Neighbors Oh Yeah) C++ library.

76 |

Supported data types

77 | 78 |
    79 |
  • [ ] Float16 (half-precision)
  • 80 |
  • [x] Float (32 bit)
  • 81 |
  • [x] Double (64 bit)
  • 82 |
83 |

Supported distance metrics (similarity measures)

84 | 85 |
    86 |
  • [x] angular
  • 87 |
  • [x] dotProcut
  • 88 |
  • [x] euclidean
  • 89 |
  • [ ] hamming
  • 90 |
  • [x] manhattan
  • 91 |
92 |

Background

93 | 94 |

Nearest neighbors search (NNS) algorithms are used in a variety of tasks that include classification, clustering, image retreval, and recommendation. The fundamental task in NNS is to look for points (examples) in a data set that are closest to a given query using some metric of distance (manhattan, euclidean, etc.). Brute force search quickly becomes inefficient as the size of a dataset and number of dimensions in the data grow (see the curse of dimensionality). Searching can be made more efficient by intelligent indexing (k-d trees, ball tree, etc.), but even here large datasets quickly lead to large indexes, memory constraints, and suboptimal search speed.

95 | 96 |

Approximate nearest neighbors algorithms make a trade-off in reduced accuracy for improved query performance. Annoy offers benefits in both speed and memory footprint (indexes are memory mapped) that make it an attractive alternative to brute force search and exact nearest neighbor methods.

97 |

Installation

98 | 99 |

Swift Package Manager (SPM) 100 | If you are using Xcode you can do the following:

101 | 102 |
    103 |
  1. Select File > Swift Packages > Add Package Dependency.
  2. 104 |
  3. copy and paste https://github.com/jbadger3/SwiftAnnoy into the search URL
  4. 105 |
  5. Select SwiftAnnoy and click next.
  6. 106 |
  7. Choose a rule for dependency management. Click next.
  8. 107 |
  9. Click Finish.
  10. 108 |
109 |

Usage

110 | 111 |

Using SwiftAnnoy is fairly straitforward. You can follow the code snippets below to get started.

112 |

Create an index

113 | 114 |

First, create an AnnoyIndex<T> as in:

115 |
let index = AnnoyIndex<Double>(itemLength: 2, metric: .euclidean)
116 | 
117 | 118 |

Currently supported types are Float and Double.

119 |

Adding items

120 | 121 |

Next, add some data to your index. There are two functions that can be used to populate an index: addItem and addItems.

122 |
var item0 = [1.0, 1.0]
123 | var item1 = [3.0, 4.0]
124 | var item2 = [6.0, 8.0]
125 | var items = [[item0, item1, item2]]
126 | 
127 | // add one item
128 | try? index.addItem(index: 0, vector: &item0)
129 | 
130 | // add multple items
131 | try? index.addItems(items: &items)
132 | 
133 | 134 |

Note: Annoy expects indices in chronological order from 0…n-1. If you need/intend to use some other id numbering system create your own mapping as memory will be allocated for max(id) + 1 items.

135 |

Build the index

136 | 137 |

In order to run queries on an AnnoyIndex the index must first be built.

138 |
try? index.build(numTrees: 1)
139 | 
140 | 141 |

The argument numTrees specifies the number of trees you want Annoy to use to construct the index. The more trees you include in the index the more accurate the search results will be, but it will take longer to build, take up more space, and require more time to search. Experiment with this parameter to optimize the tradeoffs.

142 |

Running queries

143 | 144 |

An AnnoyIndex can be queried using either an item index or a vector.

145 |
// by item
146 | let results =  index.getNNsForItem(item: 3, neighbors: 3)
147 | print(results)
148 | "Optional((indices: [3, 2, 0], distances: [0.0, 5.0, 8.602325267042627]))"
149 | 
150 | // by vector
151 | var vector = [3.0, 4.0]
152 | let results2 = index.getNNsForVector(vector: &vector, neighbors: 3)
153 | print(results2)
154 | "Optional((indices: [2, 0, 1], distances: [0.0, 3.605551275463989, 3.605551275463989]))"
155 | 
156 |

Contributing

157 | 158 |

Improvements and suggestions are welcome. Have a look at the TODO list below or open up an issue for other areas of improvement.

159 |

TODO

160 |

Features

161 | 162 |
    163 |
  • [ ] hamming distance
  • 164 |
  • [ ] bounds checking for getItem, getNNsForItem, and getNNsForVector
  • 165 |
  • [ ]
  • 166 |
167 |

Unit testing

168 | 169 |
    170 |
  • [ ] build
  • 171 |
  • [ ] unbuild
  • 172 |
  • [ ] unload
  • 173 |
  • [ ] setVerbose
  • 174 |
  • [ ] getItem
  • 175 |
  • [ ] setSeed
  • 176 |
  • [ ] onDiskBuild
  • 177 |
178 |

Memory management

179 | 180 |
    181 |
  • [ ] test for memory leaks
  • 182 |
  • [ ] verify mmap and prefault function properly
  • 183 |
184 |

OS/device testing

185 | 186 |
    187 |
  • [ ] macOS
  • 188 |
  • [ ] iOS
  • 189 |
  • [ ] Linux
  • 190 |
191 |

License

192 | 193 |

SwiftAnnoy is released with an MIT license. If you use SwiftAnnoy in another project, please add a link to this repository in your acknowledgements.

194 | 195 |
196 |
197 | 201 |
202 |
203 | 204 | 205 | 206 | -------------------------------------------------------------------------------- /docs/docsets/.docset/Contents/Resources/Documents/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |

Docs (83% documented)

17 |
18 |
19 |
20 | 25 |
26 |
27 | 69 |
70 |
71 |
72 | 73 |

SwiftAnnoy

74 | 75 |

SwiftAnnoy provides an interface (bindings) to the Annoy (Approximate Nearest Neighbors Oh Yeah) C++ library.

76 |

Supported data types

77 | 78 |
    79 |
  • [ ] Float16 (half-precision)
  • 80 |
  • [x] Float (32 bit)
  • 81 |
  • [x] Double (64 bit)
  • 82 |
83 |

Supported distance metrics (similarity measures)

84 | 85 |
    86 |
  • [x] angular
  • 87 |
  • [x] dotProcut
  • 88 |
  • [x] euclidean
  • 89 |
  • [ ] hamming
  • 90 |
  • [x] manhattan
  • 91 |
92 |

Background

93 | 94 |

Nearest neighbors search (NNS) algorithms are used in a variety of tasks that include classification, clustering, image retreval, and recommendation. The fundamental task in NNS is to look for points (examples) in a data set that are closest to a given query using some metric of distance (manhattan, euclidean, etc.). Brute force search quickly becomes inefficient as the size of a dataset and number of dimensions in the data grow (see the curse of dimensionality). Searching can be made more efficient by intelligent indexing (k-d trees, ball tree, etc.), but even here large datasets quickly lead to large indexes, memory constraints, and suboptimal search speed.

95 | 96 |

Approximate nearest neighbors algorithms make a trade-off in reduced accuracy for improved query performance. Annoy offers benefits in both speed and memory footprint (indexes are memory mapped) that make it an attractive alternative to brute force search and exact nearest neighbor methods.

97 |

Installation

98 | 99 |

Swift Package Manager (SPM) 100 | If you are using Xcode you can do the following:

101 | 102 |
    103 |
  1. Select File > Swift Packages > Add Package Dependency.
  2. 104 |
  3. copy and paste https://github.com/jbadger3/SwiftAnnoy into the search URL
  4. 105 |
  5. Select SwiftAnnoy and click next.
  6. 106 |
  7. Choose a rule for dependency management. Click next.
  8. 107 |
  9. Click Finish.
  10. 108 |
109 |

Usage

110 | 111 |

Using SwiftAnnoy is fairly straitforward. You can follow the code snippets below to get started.

112 |

Create an index

113 | 114 |

First, create an AnnoyIndex<T> as in:

115 |
let index = AnnoyIndex<Double>(itemLength: 2, metric: .euclidean)
116 | 
117 | 118 |

Currently supported types are Float and Double.

119 |

Adding items

120 | 121 |

Next, add some data to your index. There are two functions that can be used to populate an index: addItem and addItems.

122 |
var item0 = [1.0, 1.0]
123 | var item1 = [3.0, 4.0]
124 | var item2 = [6.0, 8.0]
125 | var items = [[item0, item1, item2]]
126 | 
127 | // add one item
128 | try? index.addItem(index: 0, vector: &item0)
129 | 
130 | // add multple items
131 | try? index.addItems(items: &items)
132 | 
133 | 134 |

Note: Annoy expects indices in chronological order from 0…n-1. If you need/intend to use some other id numbering system create your own mapping as memory will be allocated for max(id) + 1 items.

135 |

Build the index

136 | 137 |

In order to run queries on an AnnoyIndex the index must first be built.

138 |
try? index.build(numTrees: 1)
139 | 
140 | 141 |

The argument numTrees specifies the number of trees you want Annoy to use to construct the index. The more trees you include in the index the more accurate the search results will be, but it will take longer to build, take up more space, and require more time to search. Experiment with this parameter to optimize the tradeoffs.

142 |

Running queries

143 | 144 |

An AnnoyIndex can be queried using either an item index or a vector.

145 |
// by item
146 | let results =  index.getNNsForItem(item: 3, neighbors: 3)
147 | print(results)
148 | "Optional((indices: [3, 2, 0], distances: [0.0, 5.0, 8.602325267042627]))"
149 | 
150 | // by vector
151 | var vector = [3.0, 4.0]
152 | let results2 = index.getNNsForVector(vector: &vector, neighbors: 3)
153 | print(results2)
154 | "Optional((indices: [2, 0, 1], distances: [0.0, 3.605551275463989, 3.605551275463989]))"
155 | 
156 |

Contributing

157 | 158 |

Improvements and suggestions are welcome. Have a look at the TODO list below or open up an issue for other areas of improvement.

159 |

TODO

160 |

Features

161 | 162 |
    163 |
  • [ ] hamming distance
  • 164 |
  • [ ] bounds checking for getItem, getNNsForItem, and getNNsForVector
  • 165 |
  • [ ]
  • 166 |
167 |

Unit testing

168 | 169 |
    170 |
  • [ ] build
  • 171 |
  • [ ] unbuild
  • 172 |
  • [ ] unload
  • 173 |
  • [ ] setVerbose
  • 174 |
  • [ ] getItem
  • 175 |
  • [ ] setSeed
  • 176 |
  • [ ] onDiskBuild
  • 177 |
178 |

Memory management

179 | 180 |
    181 |
  • [ ] test for memory leaks
  • 182 |
  • [ ] verify mmap and prefault function properly
  • 183 |
184 |

OS/device testing

185 | 186 |
    187 |
  • [ ] macOS
  • 188 |
  • [ ] iOS
  • 189 |
  • [ ] Linux
  • 190 |
191 |

License

192 | 193 |

SwiftAnnoy is released with an MIT license. If you use SwiftAnnoy in another project, please add a link to this repository in your acknowledgements.

194 | 195 |
196 |
197 | 201 |
202 |
203 | 204 | 205 | 206 | -------------------------------------------------------------------------------- /docs/Classes/AnnoyIndex/DistanceMetric.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | DistanceMetric Enumeration Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

Docs (83% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 70 |
71 |
72 |
73 |

DistanceMetric

74 |
75 |
76 |
public enum DistanceMetric : String
77 | 78 |
79 |
80 |

Supported distance metrics / similarity measures

81 | 82 |
83 |
84 |
85 |
    86 |
  • 87 |
    88 | 89 | 90 | 91 | angular 92 | 93 |
    94 |
    95 |
    96 |
    97 |
    98 |
    99 |

    The angular distance used is the Euclidean distance of normalized vectors, which is sqrt(2(1-cos(u,v)))

    100 | 101 |
    102 |
    103 |

    Declaration

    104 |
    105 |

    Swift

    106 |
    case angular
    107 | 108 |
    109 |
    110 |
    111 |
    112 |
  • 113 |
  • 114 |
    115 | 116 | 117 | 118 | dotProduct 119 | 120 |
    121 |
    122 |
    123 |
    124 |
    125 |
    126 |

    The dot product also called the inner product.

    127 | 128 |
    129 |
    130 |

    Declaration

    131 |
    132 |

    Swift

    133 |
    case dotProduct
    134 | 135 |
    136 |
    137 |
    138 |
    139 |
  • 140 |
  • 141 |
    142 | 143 | 144 | 145 | euclidean 146 | 147 |
    148 |
    149 |
    150 |
    151 |
    152 |
    153 |

    The L2 or straight line distance as calculated using the Pythagorean formula

    154 | 155 |
    156 |
    157 |

    Declaration

    158 |
    159 |

    Swift

    160 |
    case euclidean
    161 | 162 |
    163 |
    164 |
    165 |
    166 |
  • 167 |
  • 168 |
    169 | 170 | 171 | 172 | manhattan 173 | 174 |
    175 |
    176 |
    177 |
    178 |
    179 |
    180 |

    The L1 or city block distance

    181 | 182 |
    183 |
    184 |

    Declaration

    185 |
    186 |

    Swift

    187 |
    case manhattan
    188 | 189 |
    190 |
    191 |
    192 |
    193 |
  • 194 |
195 |
196 |
197 |
198 | 202 |
203 |
204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /docs/docsets/.docset/Contents/Resources/Documents/Classes/AnnoyIndex/DistanceMetric.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | DistanceMetric Enumeration Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

Docs (83% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 70 |
71 |
72 |
73 |

DistanceMetric

74 |
75 |
76 |
public enum DistanceMetric : String
77 | 78 |
79 |
80 |

Supported distance metrics / similarity measures

81 | 82 |
83 |
84 |
85 |
    86 |
  • 87 |
    88 | 89 | 90 | 91 | angular 92 | 93 |
    94 |
    95 |
    96 |
    97 |
    98 |
    99 |

    The angular distance used is the Euclidean distance of normalized vectors, which is sqrt(2(1-cos(u,v)))

    100 | 101 |
    102 |
    103 |

    Declaration

    104 |
    105 |

    Swift

    106 |
    case angular
    107 | 108 |
    109 |
    110 |
    111 |
    112 |
  • 113 |
  • 114 |
    115 | 116 | 117 | 118 | dotProduct 119 | 120 |
    121 |
    122 |
    123 |
    124 |
    125 |
    126 |

    The dot product also called the inner product.

    127 | 128 |
    129 |
    130 |

    Declaration

    131 |
    132 |

    Swift

    133 |
    case dotProduct
    134 | 135 |
    136 |
    137 |
    138 |
    139 |
  • 140 |
  • 141 |
    142 | 143 | 144 | 145 | euclidean 146 | 147 |
    148 |
    149 |
    150 |
    151 |
    152 |
    153 |

    The L2 or straight line distance as calculated using the Pythagorean formula

    154 | 155 |
    156 |
    157 |

    Declaration

    158 |
    159 |

    Swift

    160 |
    case euclidean
    161 | 162 |
    163 |
    164 |
    165 |
    166 |
  • 167 |
  • 168 |
    169 | 170 | 171 | 172 | manhattan 173 | 174 |
    175 |
    176 |
    177 |
    178 |
    179 |
    180 |

    The L1 or city block distance

    181 | 182 |
    183 |
    184 |

    Declaration

    185 |
    186 |

    Swift

    187 |
    case manhattan
    188 | 189 |
    190 |
    191 |
    192 |
    193 |
  • 194 |
195 |
196 |
197 |
198 | 202 |
203 |
204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /Tests/SwiftAnnoyTests/AnnoyIndexFloatTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import SwiftAnnoy 3 | 4 | final class AnnoyIndexFloatTests: XCTestCase { 5 | typealias DistanceMetric = AnnoyIndex.DistanceMetric 6 | 7 | var sut:AnnoyIndex! 8 | 9 | override func setUp() { 10 | super.setUp() 11 | } 12 | 13 | override func tearDown() { 14 | sut = nil 15 | super.tearDown() 16 | } 17 | 18 | //MARK: - Given 19 | func givenFloatTestData() { 20 | var testData = floatTestData() 21 | for (index, _) in testData.enumerated() { 22 | try! sut.addItem(index: index, vector: &testData[index]) 23 | } 24 | } 25 | 26 | func givenIndex(itemLength: Int = 2, metric: DistanceMetric = DistanceMetric.euclidean) { 27 | sut = AnnoyIndex(itemLength: itemLength, metric: metric) 28 | } 29 | 30 | func test_addItem_updatesNumItems() { 31 | // Given 32 | givenIndex() 33 | var item: [Float] = [1.0, 1.0] 34 | 35 | // When 36 | try! sut.addItem(index: 0, vector: &item) 37 | 38 | // Then 39 | XCTAssertEqual(sut.numberOfItems, 1) 40 | 41 | // When 42 | try! sut.addItem(index: 1, vector: &item) 43 | 44 | // Then 45 | XCTAssertEqual(sut.numberOfItems, 2) 46 | } 47 | 48 | func test_addItem_whenOverwritingOldIndex_UpdatesStoredItem() { 49 | // Given 50 | givenIndex() 51 | var item: [Float] = [1.0, 1.0] 52 | try! sut.addItem(index: 0, vector: &item) 53 | let itemBack = sut.getItem(index: 0) 54 | assertVecsEqual(vec1: item, vec2: itemBack!) 55 | 56 | // When 57 | var item2: [Float] = [2.0, 1.0] 58 | try! sut.addItem(index: 0, vector: &item2) 59 | 60 | // Then 61 | let itemBack2 = sut.getItem(index: 0) 62 | assertVecsEqual(vec1: item2, vec2: itemBack2!) 63 | } 64 | 65 | func test_addItem_whenWrongVectorLength_throwsInvalidVectorSizeError() { 66 | // Given 67 | givenIndex() 68 | // When 69 | var item: [Float] = [1.0, 1.0, 1.0] 70 | 71 | // Then 72 | XCTAssertThrowsError(try sut.addItem(index: 0,vector: &item)) { error in 73 | guard case AnnoyIndexError.invalidVectorLength(_) = error else { 74 | return XCTFail() 75 | } 76 | } 77 | } 78 | 79 | func test_addItems_whenNoItemsInIndex_enumeratesIndicesStartingWithZero() { 80 | // Given 81 | givenIndex() 82 | var testData = floatTestData() 83 | 84 | // When 85 | try! sut.addItems(items: &testData) 86 | 87 | // Then 88 | let item0 = testData[0] 89 | let indexItem0 = sut.getItem(index: 0)! 90 | assertVecsEqual(vec1: indexItem0, vec2: item0) 91 | let item3 = testData[3] 92 | let indexItem3 = sut.getItem(index: 3)! 93 | assertVecsEqual(vec1: indexItem3, vec2: item3) 94 | } 95 | 96 | func test_addItems_givenItemsInIndex_continuesIndexingFromCurrentItemCount() { 97 | // Given 98 | givenIndex() 99 | var testData = floatTestData() 100 | try! sut.addItems(items: &testData) 101 | 102 | // When 103 | var newData: [[Float]] = [[4.0, 4.0],[5.0, 5.0]] 104 | try! sut.addItems(items: &newData) 105 | 106 | // Then 107 | let indexItem = sut.getItem(index: testData.count)! 108 | let expecteditem = newData[0] 109 | assertVecsEqual(vec1: indexItem, vec2: expecteditem) 110 | } 111 | 112 | func test_getDistance_euclidean_whenSameItem_returnsDistanceZero() { 113 | // Given 114 | givenIndex(metric: .euclidean) 115 | givenFloatTestData() 116 | 117 | // When 118 | let distance = sut.getDistance(item1: 0, item2: 0) 119 | 120 | // Then 121 | XCTAssertEqual(distance!, Float(0)) 122 | } 123 | 124 | func test_getDistance_manhattan_whenSameItem_returnsDistanceZero() { 125 | // Given 126 | givenIndex(metric: .manhattan) 127 | givenFloatTestData() 128 | 129 | // When 130 | let distance = sut.getDistance(item1: 0, item2: 0) 131 | 132 | // Then 133 | XCTAssertEqual(distance!, Float(0)) 134 | } 135 | 136 | func test_getDistance_angular_whenSameItem_returnsDistanceZero() { 137 | // Given 138 | givenIndex(metric: .angular) 139 | givenFloatTestData() 140 | 141 | // When 142 | let distance = sut.getDistance(item1: 0, item2: 0) 143 | 144 | // Then 145 | XCTAssertEqual(distance!, Float(0)) 146 | } 147 | 148 | func test_getDistance_dotProduct_whenSameItem_returnsDotOfSelf() { 149 | // Given 150 | givenIndex(metric: .dotProduct) 151 | givenFloatTestData() 152 | 153 | // When 154 | let distance = sut.getDistance(item1: 0, item2: 0) 155 | 156 | // Then 157 | let expectedDistance = Float(1.0) 158 | XCTAssertEqual(distance!, expectedDistance) 159 | } 160 | 161 | func test_getDistance_euclidean_whenItemDifferent_returnsCorrectDistance() { 162 | // Given 163 | givenIndex(metric: .euclidean) 164 | givenFloatTestData() 165 | 166 | // When 167 | let distance = sut.getDistance(item1: 3, item2: 4) 168 | 169 | let expectedDistance = Float(5.0) 170 | // Then 171 | XCTAssertEqual(distance!, expectedDistance, accuracy: Float(0.000001)) 172 | } 173 | 174 | func test_getDistance_manhattan_whenItemDifferent_returnsCorrectDistance() { 175 | // Given 176 | givenIndex(metric: .manhattan) 177 | givenFloatTestData() 178 | 179 | // When 180 | let distance = sut.getDistance(item1: 0, item2: 1) 181 | 182 | let expectedDistance = Float(2.0) 183 | // Then 184 | XCTAssertEqual(distance!, expectedDistance, accuracy: Float(0.000001)) 185 | } 186 | 187 | func test_getDistance_angular_whenItemDifferent_returnsCorrectDistance() { 188 | // Given 189 | givenIndex(metric: .angular) 190 | givenFloatTestData() 191 | 192 | // When 193 | let distance = sut.getDistance(item1: 0, item2: 2) 194 | 195 | let expectedDistance = Float(2.0) 196 | // Then 197 | XCTAssertEqual(distance!, expectedDistance, accuracy: Float(0.000001)) 198 | } 199 | 200 | func test_getDistance_dotProduct_whenItemDifferent_returnsCorrectDistance() { 201 | // Given 202 | givenIndex(metric: .dotProduct) 203 | givenFloatTestData() 204 | 205 | // When 206 | let distance = sut.getDistance(item1: 3, item2: 4) 207 | 208 | let expectedDistance = Float(9.0) 209 | // Then 210 | XCTAssertEqual(distance!, expectedDistance, accuracy: Float(0.000001)) 211 | } 212 | 213 | func test_getDistance_whenItemIndexOutBounds_returnsNil() { 214 | // Given 215 | givenIndex() 216 | givenFloatTestData() 217 | 218 | // When 219 | let distance = sut.getDistance(item1: 0, item2: 100) 220 | 221 | // Then 222 | XCTAssertNil(distance) 223 | } 224 | 225 | func test_getNNsForItem_angular_whenValidIndex_returnsCorrectItemsAndDistances() { 226 | // Given 227 | givenIndex(metric: .angular) 228 | givenFloatTestData() 229 | try! sut.build(numTrees: 1) 230 | 231 | // When 232 | let results = sut.getNNsForItem(item: 0, neighbors: 2)! 233 | 234 | // Then 235 | let expectedIndices = [0, 3] 236 | 237 | let expectedDistances : [Float] = [0, sqrt(2*(1-Float(sqrt(2)/2)))] 238 | assertVecsEqual(vec1: results.indices, vec2: expectedIndices) 239 | assertVecsEqual(vec1: results.distances , vec2: expectedDistances) 240 | } 241 | 242 | func test_getNNsForItem_dotProduct_whenValidIndex_returnsCorrectItemsAndDistances() { 243 | // Given 244 | givenIndex(metric: .dotProduct) 245 | givenFloatTestData() 246 | try! sut.build(numTrees: 1) 247 | 248 | // When 249 | let results = sut.getNNsForItem(item: 0, neighbors: 3)! 250 | 251 | // Then 252 | let expectedIndices = [5, 4, 0] 253 | let expectedDistances : [Float] = [7, 4, 1] 254 | assertVecsEqual(vec1: results.indices, vec2: expectedIndices) 255 | assertVecsEqual(vec1: results.distances , vec2: expectedDistances) 256 | } 257 | 258 | 259 | 260 | func test_getNNsForItem_euclidean_whenValidIndex_returnsCorrectItemsAndDistances() { 261 | // Given 262 | givenIndex(metric: .euclidean) 263 | givenFloatTestData() 264 | try! sut.build(numTrees: 1) 265 | 266 | // When 267 | let results = sut.getNNsForItem(item: 5, neighbors: 3)! 268 | 269 | // Then 270 | let expectedIndices = [5, 4, 3] 271 | let expectedDistances : [Float] = [0, 5, 10] 272 | assertVecsEqual(vec1: results.indices, vec2: expectedIndices) 273 | assertVecsEqual(vec1: results.distances , vec2: expectedDistances) 274 | } 275 | 276 | func test_getNNsForItem_manhattan_whenValidIndex_returnsCorrectItemsAndDistances() { 277 | // Given 278 | givenIndex(metric: .manhattan) 279 | givenFloatTestData() 280 | try! sut.build(numTrees: 1) 281 | 282 | // When 283 | let results = sut.getNNsForItem(item: 0, neighbors: 3)! 284 | 285 | // Then 286 | let expectedIndices = [0, 3, 1] 287 | let expectedDistances : [Float] = [0, 1, 2] 288 | assertVecsEqual(vec1: results.indices, vec2: expectedIndices) 289 | assertVecsEqual(vec1: results.distances , vec2: expectedDistances) 290 | } 291 | 292 | func test_getNNsForVector_angular_returnsCorrectItemsAndDistances() { 293 | // Given 294 | givenIndex(metric: .angular) 295 | givenFloatTestData() 296 | try! sut.build(numTrees: 1) 297 | var vector: [Float] = [1.0, 0.0] 298 | 299 | // When 300 | let results = sut.getNNsForVector(vector: &vector, neighbors: 2)! 301 | 302 | // Then 303 | let expectedIndices = [0, 3] 304 | let expectedDistances: [Float] = [0.0, sqrt(2*(1-Float(sqrt(2)/2)))] 305 | assertVecsEqual(vec1: results.indices , vec2: expectedIndices) 306 | assertVecsEqual(vec1: results.distances, vec2: expectedDistances) 307 | } 308 | 309 | func test_getNNsForVector_dotProduct_returnsCorrectItemsAndDistances() { 310 | // Given 311 | givenIndex(metric: .dotProduct) 312 | givenFloatTestData() 313 | try! sut.build(numTrees: 1) 314 | var vector: [Float] = [1.0, 1.0] 315 | 316 | // When 317 | let results = sut.getNNsForVector(vector: &vector, neighbors: 3)! 318 | 319 | // Then 320 | let expectedIndices = [5, 4, 3] 321 | let expectedDistances: [Float] = [16, 9, 2] 322 | assertVecsEqual(vec1: results.indices , vec2: expectedIndices) 323 | assertVecsEqual(vec1: results.distances, vec2: expectedDistances) 324 | } 325 | 326 | func test_getNNsForVector_euclidean_returnsCorrectItemsAndDistances() { 327 | // Given 328 | givenIndex(metric: .euclidean) 329 | givenFloatTestData() 330 | try! sut.build(numTrees: 1) 331 | var vector: [Float] = [7.0, 9.0] 332 | 333 | // When 334 | let results = sut.getNNsForVector(vector: &vector, neighbors: 3)! 335 | 336 | // Then 337 | let expectedIndices = [5, 4, 3] 338 | let expectedDistances: [Float] = [0, 5, 10] 339 | assertVecsEqual(vec1: results.indices , vec2: expectedIndices) 340 | assertVecsEqual(vec1: results.distances, vec2: expectedDistances) 341 | } 342 | 343 | func test_getNNsForVector_manhattan_returnsCorrectItemsAndDistances() { 344 | // Given 345 | givenIndex(metric: .manhattan) 346 | givenFloatTestData() 347 | try! sut.build(numTrees: 1) 348 | var vector: [Float] = [7.0, 9.0] 349 | 350 | // When 351 | let results = sut.getNNsForVector(vector: &vector, neighbors: 3)! 352 | 353 | // Then 354 | let expectedIndices = [5, 4, 3] 355 | let expectedDistances: [Float] = [0, 7, 14] 356 | assertVecsEqual(vec1: results.indices , vec2: expectedIndices) 357 | assertVecsEqual(vec1: results.distances, vec2: expectedDistances) 358 | } 359 | 360 | } 361 | -------------------------------------------------------------------------------- /Sources/SwiftAnnoy/AnnoyIndex.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnnoyIndex.swift 3 | // SwiftAnnoy 4 | // 5 | // Created by Jonathan Badger on 5/4/20. 6 | // Copyright © 2020 Jonathan Badger. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CAnnoyWrapper 11 | 12 | /// The primary generic class used to create an index. 13 | public class AnnoyIndex { 14 | 15 | /// Supported distance metrics / similarity measures 16 | public enum DistanceMetric: String{ 17 | /// The angular distance used is the Euclidean distance of normalized vectors, which is sqrt(2(1-cos(u,v))) 18 | case angular 19 | /// The dot product also called the inner product. 20 | case dotProduct 21 | /// The L2 or straight line distance as calculated using the Pythagorean formula 22 | case euclidean 23 | /// The L1 or city block distance 24 | case manhattan 25 | } 26 | 27 | //MARK: - Properties 28 | private let indexPointer: UnsafeRawPointer 29 | /// The length of each vector (Array count if you wish) for each item in the index. 30 | public private(set) var itemLength: Int 31 | /// The distance metric used to measure the similarity between vectors. 32 | public private(set) var distanceMetric: [CChar] 33 | /// The type of data being used in each vector. 34 | public private(set) var dataType: [CChar] 35 | 36 | /** 37 | The number of items in the index. 38 | */ 39 | public var numberOfItems: Int { 40 | get { 41 | return Int(C_get_n_items(indexPointer)) 42 | } 43 | } 44 | 45 | /** 46 | The number of trees in the index. (if built) 47 | */ 48 | public var numberOfTrees: Int { 49 | get { 50 | return Int(C_get_n_trees(indexPointer)) 51 | } 52 | } 53 | 54 | /** 55 | - Parameters: 56 | - itemLength: The vector length (Array.count) of each item stored in the index . 57 | - metric: The metric to be used to measure the distance between items. One of .angular, .dotProduct, .euclidean, or .manhattan 58 | */ 59 | public init(itemLength: Int, metric: DistanceMetric = .euclidean) { 60 | self.itemLength = itemLength 61 | distanceMetric = metric.rawValue.cString(using: .utf8)! 62 | dataType = String(describing: T.self).cString(using: .utf8)! 63 | indexPointer = C_initializeAnnoyIndex(Int32(itemLength), &distanceMetric, &dataType)! 64 | } 65 | 66 | 67 | deinit { 68 | C_unload(indexPointer) 69 | C_deleteAnnoyIndex(indexPointer) 70 | } 71 | 72 | /** 73 | Adds an a new item to the index. 74 | 75 | - Parameters: 76 | - index: The index (integer) to assign to the item. 77 | - vector: Array representing the feature vector for the item. 78 | - Throws: `AnnoyIndexError.addItemFailed` 79 | */ 80 | public func addItem(index: Int, vector: inout [T]) throws { 81 | guard self.itemLength == vector.count else { 82 | throw AnnoyIndexError.invalidVectorLength(message: "Expected vector.count = \(self.itemLength), input vector.count was \(vector.count).") 83 | } 84 | let i = Int32(index) 85 | let success = vector.withUnsafeMutableBufferPointer { (buffer) -> Bool in 86 | let p = buffer.baseAddress 87 | return C_add_item(i, p, &distanceMetric, &dataType, indexPointer) 88 | } 89 | if !success { 90 | throw AnnoyIndexError.addItemFailed 91 | } 92 | } 93 | 94 | /** 95 | Adds multiple new items to the index. 96 | 97 | - Parameters: 98 | - items: An Array of vectors to add to the index. 99 | - Throws: AnnoyIndexError.addIemFailed 100 | */ 101 | public func addItems(items: inout [[T]]) throws { 102 | let beginningNumItems = self.numberOfItems 103 | for (index, _) in items.enumerated() { 104 | try self.addItem(index: index + beginningNumItems, vector: &items[index]) 105 | } 106 | } 107 | 108 | /** 109 | Builds the index to allow for fast approximate nearest neighbors lookup. 110 | 111 | Using a larger number of trees to build the index will take longer, but will also lead to better accuracy 112 | during querying. You may need to experiment to find the right balance. 113 | - Parameters: 114 | - numTrees: The number of trees to use to build the index. 115 | */ 116 | public func build(numTrees: Int) throws { 117 | let success = C_build(Int32(numTrees), &distanceMetric, &dataType, indexPointer) 118 | if !success { 119 | throw AnnoyIndexError.buildFailed 120 | } 121 | } 122 | 123 | /** 124 | Unbuilds the current AnnoyIndex which allows additional items to be added or for the index to be rebuilt with a 125 | different number of trees. 126 | - Throws: AnnoyIndexError.UnbuildFailed 127 | */ 128 | public func unbuild() throws { 129 | let success = C_unbuild(indexPointer) 130 | if !success { 131 | throw AnnoyIndexError.unbuildFailed 132 | } 133 | } 134 | 135 | /** 136 | Saves the current index to a file then immediately loads (memory maps) the index from disk. 137 | 138 | - Parameters: 139 | - url: The file destination URL for the index. 140 | - prefault: If set to true the entire file will be preread into memory. 141 | - Throws: AnnoyIndexError.saveFailed 142 | */ 143 | public func save(url: URL, prefault: Bool = false) throws { 144 | guard var filenameCString = url.path.cString(using: .utf8) else { 145 | return 146 | } 147 | let success = C_save(&filenameCString, prefault, indexPointer) 148 | if !success { 149 | throw AnnoyIndexError.saveFailed 150 | } 151 | } 152 | 153 | /** 154 | Unloads the underlying index and all associated items. 155 | */ 156 | public func unload() { 157 | C_unload(indexPointer) 158 | } 159 | 160 | /** 161 | Loads a previously saved index. 162 | - Parameters: 163 | - url: The file URL to load 164 | - prefault: If set to true the entire file will be preread into memory. Default is false which uses memory mapping. 165 | - Throws: AnnoyIndexError.loadFailed 166 | */ 167 | public func load(url: URL, prefault: Bool = false) throws { 168 | guard let filenameCString = url.path.cString(using: .utf8) else { 169 | return 170 | } 171 | let success = C_load(filenameCString, &distanceMetric, &dataType, prefault, indexPointer) 172 | if !success { 173 | throw AnnoyIndexError.loadFailed 174 | } 175 | 176 | } 177 | 178 | /** 179 | Calculates the distance between two items in the index. 180 | - Parameters: 181 | - item1: The index of first item of inerest. 182 | - item2: The index of the second item of interest. 183 | - Returns: The distance between the two items or nil if one of the items is not in the index. 184 | */ 185 | public func getDistance(item1: Int, item2: Int) -> T? { 186 | if (item1 >= self.numberOfItems) || (item2 >= self.numberOfItems) { return nil } 187 | switch T.self { 188 | case is Float.Type: 189 | var result = Float(-1.0) 190 | C_get_distance(Int32(item1), Int32(item2),&result, &distanceMetric, &dataType, indexPointer) 191 | return result as? T 192 | case is Double.Type: 193 | var result = Double(-1.0) 194 | C_get_distance(Int32(item1), Int32(item2),&result, &distanceMetric, &dataType, indexPointer) 195 | return result as? T 196 | default: 197 | return nil 198 | } 199 | } 200 | 201 | /** 202 | Gathers the approximate nearest neighbors for an item. 203 | 204 | - Parameters: 205 | - item: The index of the item of interest. 206 | - neighbors: The number of neighbors to return. 207 | - search_k: The number of nodes to inspect during search. search_k defaults to n * n_trees * D where n is the number of approximate nearest neighbors and D is a constant determined by Annoy based on the distance metric. In general, increasing search_k increases search time, but will give more accurate results. 208 | - Returns: A tuple of arrays containing the item indicies and distances. 209 | */ 210 | public func getNNsForItem(item: Int, neighbors: Int, search_k: Int = -1) -> (indices: [Int], distances: [T])? { 211 | var indices: [Int32] = Array(repeating: -1, count: neighbors) 212 | switch T.self { 213 | case is Float.Type: 214 | var distances = Array(repeating: Float(-1.0), count: neighbors) 215 | C_get_nns_by_item(Int32(item), Int32(neighbors), Int32(search_k), &indices, &distances, &distanceMetric, &dataType, indexPointer) 216 | return (indices.toInt(), distances as! [T]) 217 | case is Double.Type: 218 | var distances = Array(repeating: Double(-1.0), count: neighbors) 219 | C_get_nns_by_item(Int32(item), Int32(neighbors), Int32(search_k), &indices, &distances, &distanceMetric, &dataType, indexPointer) 220 | return (indices.toInt(), distances as! [T]) 221 | default: 222 | return nil 223 | } 224 | } 225 | 226 | /** 227 | Gathers the approximate nearest neighbors for a vector. 228 | 229 | - Parameters: 230 | - vector: The vector of interest. 231 | - neighbors: The number of neighbors to return. 232 | - search_k: The number of nodes to inspect during search. search_k defaults to n * n_trees * D where n is the number of approximate nearest neighbors and D is a constant determined by Annoy based on the distance metric. In general, increasing search_k increases search time, but will give more accurate results. 233 | - Returns: A tuple of arrays containing the item indicies and distances. 234 | */ 235 | public func getNNsForVector(vector: inout [T], neighbors: Int, search_k: Int = -1) -> (indices: [Int], distances: [T])? { 236 | var results: [Int32] = Array(repeating: -1, count: neighbors) 237 | switch T.self { 238 | case is Float.Type: 239 | var distances = Array(repeating: Float(-1.0), count: neighbors) 240 | C_get_nns_by_vector(&vector, Int32(neighbors), Int32(search_k), &results, &distances, &distanceMetric, &dataType, indexPointer) 241 | return (results.toInt(), distances as! [T]) 242 | case is Double.Type: 243 | var distances = Array(repeating: Double(-1.0), count: neighbors) 244 | C_get_nns_by_vector(&vector, Int32(neighbors), Int32(search_k), &results, &distances, &distanceMetric, &dataType, indexPointer) 245 | return (results.toInt(), distances as! [T]) 246 | default: 247 | return nil 248 | } 249 | } 250 | 251 | /** 252 | When set to true provides additional information about operations carried out by Annoy via stdout. 253 | */ 254 | public func setVerbos(boolVal: Bool) { 255 | C_verbose(boolVal, indexPointer) 256 | } 257 | 258 | /** 259 | Retrieves the vector for an item in the index. 260 | - Parameters: 261 | - item: The index of the item to retrieve 262 | - Returns: The vector associated with the item or nil if not found. 263 | */ 264 | public func getItem(index: Int) -> [T]? { 265 | if T.self is Float.Type { 266 | var item: [Float] = Array(repeating: -1.0, count: itemLength) 267 | C_get_item(Int32(index), &item, &distanceMetric, &dataType, indexPointer) 268 | return item as? [T] 269 | } 270 | if T.self is Double.Type { 271 | var item: [Double] = Array(repeating: -1, count: itemLength) 272 | C_get_item(Int32(index), &item, &distanceMetric, &dataType, indexPointer) 273 | return item as? [T] 274 | } 275 | return nil 276 | } 277 | 278 | /** 279 | Sets the random seed used for generating the index. Setting this value is useful when running performance testing to ensure consistent results. 280 | */ 281 | public func setSeed(seedVal: Int) { 282 | C_set_seed(Int32(seedVal), indexPointer) 283 | } 284 | 285 | /** 286 | Prepares the index to be built on disk rather than in RAM. Should be set before adding items to the index. 287 | - Parameters: 288 | - url: A file destination URL for the index. 289 | */ 290 | public func onDiskBuild(url: URL) { 291 | guard var filenameCString = url.path.cString(using: .utf8) else { return } 292 | C_on_disk_build(&filenameCString, indexPointer) 293 | } 294 | 295 | } 296 | 297 | extension Array where Element: BinaryInteger { 298 | func toInt() -> [Int] { 299 | return self.map({Int($0)}) 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /Tests/SwiftAnnoyTests/AnnoyIndexDoubleTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by Jonathan Badger on 5/20/20. 6 | // 7 | 8 | import XCTest 9 | @testable import SwiftAnnoy 10 | 11 | final class AnnoyIndexDoubleTests: XCTestCase { 12 | typealias DistanceMetric = AnnoyIndex.DistanceMetric 13 | 14 | var sut:AnnoyIndex! 15 | 16 | override func setUp() { 17 | super.setUp() 18 | } 19 | 20 | override func tearDown() { 21 | sut = nil 22 | super.tearDown() 23 | } 24 | 25 | //MARK: - Given 26 | func givenTestData() { 27 | var data = testData() 28 | for (index, _) in data.enumerated() { 29 | try! sut.addItem(index: index, vector: &data[index]) 30 | } 31 | } 32 | 33 | func givenIndex(itemLength: Int = 2, metric: DistanceMetric = DistanceMetric.euclidean) { 34 | sut = AnnoyIndex(itemLength: itemLength, metric: metric) 35 | } 36 | 37 | func test_addItem_updatesNumItems() { 38 | // Given 39 | givenIndex() 40 | var item = [1.0, 1.0] 41 | 42 | // When 43 | try! sut.addItem(index: 0, vector: &item) 44 | 45 | // Then 46 | XCTAssertEqual(sut.numberOfItems, 1) 47 | 48 | // When 49 | try! sut.addItem(index: 1, vector: &item) 50 | 51 | // Then 52 | XCTAssertEqual(sut.numberOfItems, 2) 53 | } 54 | 55 | func test_addItem_whenOverwritingOldIndex_UpdatesStoredItem() { 56 | // Given 57 | givenIndex() 58 | var item = [1.0, 1.0] 59 | try! sut.addItem(index: 0, vector: &item) 60 | let itemBack = sut.getItem(index: 0) 61 | assertVecsEqual(vec1: item, vec2: itemBack!) 62 | 63 | // When 64 | var item2 = [2.0, 1.0] 65 | try! sut.addItem(index: 0, vector: &item2) 66 | 67 | // Then 68 | let itemBack2 = sut.getItem(index: 0) 69 | assertVecsEqual(vec1: item2, vec2: itemBack2!) 70 | } 71 | 72 | func test_addItem_whenWrongVectorLength_throwsInvalidVectorSizeError() { 73 | // Given 74 | givenIndex() 75 | // When 76 | var item = [1.0, 1.0, 1.0] 77 | 78 | // Then 79 | XCTAssertThrowsError(try sut.addItem(index: 0,vector: &item)) { error in 80 | guard case AnnoyIndexError.invalidVectorLength(_) = error else { 81 | return XCTFail() 82 | } 83 | } 84 | } 85 | 86 | func test_addItems_whenNoItemsInIndex_enumeratesIndicesStartingWithZero() { 87 | // Given 88 | givenIndex() 89 | var data = testData() 90 | 91 | // When 92 | try! sut.addItems(items: &data) 93 | 94 | // Then 95 | let item0 = data[0] 96 | let indexItem0 = sut.getItem(index: 0)! 97 | assertVecsEqual(vec1: indexItem0, vec2: item0) 98 | let item3 = data[3] 99 | let indexItem3 = sut.getItem(index: 3)! 100 | assertVecsEqual(vec1: indexItem3, vec2: item3) 101 | } 102 | 103 | func test_addItems_givenItemsInIndex_continuesIndexingFromCurrentItemCount() { 104 | // Given 105 | givenIndex() 106 | var data = testData() 107 | try! sut.addItems(items: &data) 108 | 109 | // When 110 | var newData = [[4.0, 4.0],[5.0, 5.0]] 111 | try! sut.addItems(items: &newData) 112 | 113 | // Then 114 | let indexItem = sut.getItem(index: data.count)! 115 | let expecteditem = newData[0] 116 | assertVecsEqual(vec1: indexItem, vec2: expecteditem) 117 | } 118 | 119 | func test_addItems_givenItemsInIndex_whenNewDataInvalid_throwsError() { 120 | //Given 121 | givenIndex() 122 | 123 | // When 124 | var newData = [[3.5, 4.4, 2.5], [2.3, 4.6, 2.0]] 125 | 126 | XCTAssertThrowsError(try sut.addItems(items: &newData)) 127 | } 128 | 129 | func test_getDistance_euclidean_whenSameItem_returnsDistanceZero() { 130 | // Given 131 | givenIndex(metric: .euclidean) 132 | givenTestData() 133 | 134 | // When 135 | let distance = sut.getDistance(item1: 0, item2: 0) 136 | 137 | // Then 138 | XCTAssertEqual(distance!, 0.0) 139 | } 140 | 141 | func test_getDistance_manhattan_whenSameItem_returnsDistanceZero() { 142 | // Given 143 | givenIndex(metric: .manhattan) 144 | givenTestData() 145 | 146 | // When 147 | let distance = sut.getDistance(item1: 0, item2: 0) 148 | 149 | // Then 150 | XCTAssertEqual(distance!, 0.0) 151 | } 152 | 153 | func test_getDistance_angular_whenSameItem_returnsDistanceZero() { 154 | // Given 155 | givenIndex(metric: .angular) 156 | givenTestData() 157 | 158 | // When 159 | let distance = sut.getDistance(item1: 0, item2: 0) 160 | 161 | // Then 162 | XCTAssertEqual(distance!, 0.0) 163 | } 164 | 165 | func test_getDistance_dotProduct_whenSameItem_returnsDotOfSelf() { 166 | // Given 167 | givenIndex(metric: .dotProduct) 168 | givenTestData() 169 | 170 | // When 171 | let distance = sut.getDistance(item1: 0, item2: 0) 172 | 173 | // Then 174 | let expectedDistance = 1.0 175 | XCTAssertEqual(distance!, expectedDistance) 176 | } 177 | 178 | func test_getDistance_euclidean_whenItemDifferent_returnsCorrectDistance() { 179 | // Given 180 | givenIndex(metric: .euclidean) 181 | givenTestData() 182 | 183 | // When 184 | let distance = sut.getDistance(item1: 3, item2: 4) 185 | 186 | let expectedDistance = 5.0 187 | // Then 188 | XCTAssertEqual(distance!, expectedDistance, accuracy: 0.000001) 189 | } 190 | 191 | func test_getDistance_manhattan_whenItemDifferent_returnsCorrectDistance() { 192 | // Given 193 | givenIndex(metric: .manhattan) 194 | givenTestData() 195 | 196 | // When 197 | let distance = sut.getDistance(item1: 0, item2: 1) 198 | 199 | let expectedDistance = 2.0 200 | // Then 201 | XCTAssertEqual(distance!, expectedDistance, accuracy: 0.000001) 202 | } 203 | 204 | func test_getDistance_angular_whenItemDifferent_returnsCorrectDistance() { 205 | // Given 206 | givenIndex(metric: .angular) 207 | givenTestData() 208 | 209 | // When 210 | let distance = sut.getDistance(item1: 0, item2: 2) 211 | 212 | let expectedDistance = 2.0 213 | // Then 214 | XCTAssertEqual(distance!, expectedDistance, accuracy: 0.000001) 215 | } 216 | 217 | func test_getDistance_dotProduct_whenItemDifferent_returnsCorrectDistance() { 218 | // Given 219 | givenIndex(metric: .dotProduct) 220 | givenTestData() 221 | 222 | // When 223 | let distance = sut.getDistance(item1: 3, item2: 4) 224 | 225 | let expectedDistance = 9.0 226 | // Then 227 | XCTAssertEqual(distance!, expectedDistance, accuracy: 0.000001) 228 | } 229 | 230 | func test_getDistance_whenItemIndexOutBounds_returnsNil() { 231 | // Given 232 | givenIndex() 233 | givenTestData() 234 | 235 | // When 236 | let distance = sut.getDistance(item1: 0, item2: 100) 237 | 238 | // Then 239 | XCTAssertNil(distance) 240 | } 241 | 242 | func test_getNNsForItem_angular_whenValidIndex_returnsCorrectItemsAndDistances() { 243 | // Given 244 | givenIndex(metric: .angular) 245 | givenTestData() 246 | try! sut.build(numTrees: 1) 247 | 248 | // When 249 | let results = sut.getNNsForItem(item: 0, neighbors: 2)! 250 | 251 | // Then 252 | let expectedIndices = [0, 3] 253 | 254 | let expectedDistances = [0, sqrt(2*(1-sqrt(2)/2))] 255 | assertVecsEqual(vec1: results.indices, vec2: expectedIndices) 256 | assertVecsEqual(vec1: results.distances , vec2: expectedDistances) 257 | } 258 | 259 | func test_getNNsForItem_dotProduct_whenValidIndex_returnsCorrectItemsAndDistances() { 260 | // Given 261 | givenIndex(metric: .dotProduct) 262 | givenTestData() 263 | try! sut.build(numTrees: 1) 264 | 265 | // When 266 | let results = sut.getNNsForItem(item: 0, neighbors: 3)! 267 | 268 | // Then 269 | let expectedIndices = [5, 4, 0] 270 | let expectedDistances = [7.0, 4.0, 1.0] 271 | assertVecsEqual(vec1: results.indices, vec2: expectedIndices) 272 | assertVecsEqual(vec1: results.distances , vec2: expectedDistances) 273 | } 274 | 275 | 276 | 277 | func test_getNNsForItem_euclidean_whenValidIndex_returnsCorrectItemsAndDistances() { 278 | // Given 279 | givenIndex(metric: .euclidean) 280 | givenTestData() 281 | try! sut.build(numTrees: 1) 282 | 283 | // When 284 | let results = sut.getNNsForItem(item: 5, neighbors: 3)! 285 | 286 | // Then 287 | let expectedIndices = [5, 4, 3] 288 | let expectedDistances = [0.0, 5.0, 10.0] 289 | assertVecsEqual(vec1: results.indices, vec2: expectedIndices) 290 | assertVecsEqual(vec1: results.distances , vec2: expectedDistances) 291 | } 292 | 293 | func test_getNNsForItem_manhattan_whenValidIndex_returnsCorrectItemsAndDistances() { 294 | // Given 295 | givenIndex(metric: .manhattan) 296 | givenTestData() 297 | try! sut.build(numTrees: 1) 298 | 299 | // When 300 | let results = sut.getNNsForItem(item: 0, neighbors: 3)! 301 | 302 | // Then 303 | let expectedIndices = [0, 3, 1] 304 | let expectedDistances = [0.0, 1.0, 2.0] 305 | assertVecsEqual(vec1: results.indices, vec2: expectedIndices) 306 | assertVecsEqual(vec1: results.distances , vec2: expectedDistances) 307 | } 308 | 309 | func test_getNNsForVector_angular_returnsCorrectItemsAndDistances() { 310 | // Given 311 | givenIndex(metric: .angular) 312 | givenTestData() 313 | try! sut.build(numTrees: 1) 314 | var vector = [1.0, 0.0] 315 | 316 | // When 317 | let results = sut.getNNsForVector(vector: &vector, neighbors: 2)! 318 | 319 | // Then 320 | let expectedIndices = [0, 3] 321 | let expectedDistances = [0.0, sqrt(2*(1-sqrt(2)/2))] 322 | assertVecsEqual(vec1: results.indices , vec2: expectedIndices) 323 | assertVecsEqual(vec1: results.distances, vec2: expectedDistances) 324 | } 325 | 326 | func test_getNNsForVector_dotProduct_returnsCorrectItemsAndDistances() { 327 | // Given 328 | givenIndex(metric: .dotProduct) 329 | givenTestData() 330 | try! sut.build(numTrees: 1) 331 | var vector = [1.0, 1.0] 332 | 333 | // When 334 | let results = sut.getNNsForVector(vector: &vector, neighbors: 3)! 335 | 336 | // Then 337 | let expectedIndices = [5, 4, 3] 338 | let expectedDistances = [16.0, 9.0, 2.0] 339 | assertVecsEqual(vec1: results.indices , vec2: expectedIndices) 340 | assertVecsEqual(vec1: results.distances, vec2: expectedDistances) 341 | } 342 | 343 | func test_getNNsForVector_euclidean_returnsCorrectItemsAndDistances() { 344 | // Given 345 | givenIndex(metric: .euclidean) 346 | givenTestData() 347 | try! sut.build(numTrees: 1) 348 | var vector = [7.0, 9.0] 349 | 350 | // When 351 | let results = sut.getNNsForVector(vector: &vector, neighbors: 3)! 352 | 353 | // Then 354 | let expectedIndices = [5, 4, 3] 355 | let expectedDistances = [0.0, 5.0, 10.0] 356 | assertVecsEqual(vec1: results.indices , vec2: expectedIndices) 357 | assertVecsEqual(vec1: results.distances, vec2: expectedDistances) 358 | } 359 | 360 | func test_getNNsForVector_manhattan_returnsCorrectItemsAndDistances() { 361 | // Given 362 | givenIndex(metric: .manhattan) 363 | givenTestData() 364 | try! sut.build(numTrees: 1) 365 | var vector = [7.0, 9.0] 366 | 367 | // When 368 | let results = sut.getNNsForVector(vector: &vector, neighbors: 3)! 369 | 370 | // Then 371 | let expectedIndices = [5, 4, 3] 372 | let expectedDistances = [0.0, 7.0, 14.0] 373 | assertVecsEqual(vec1: results.indices , vec2: expectedIndices) 374 | assertVecsEqual(vec1: results.distances, vec2: expectedDistances) 375 | } 376 | 377 | } 378 | 379 | -------------------------------------------------------------------------------- /docs/Enums/AnnoyIndexError.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | AnnoyIndexError Enumeration Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

Docs (83% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 70 |
71 |
72 |
73 |

AnnoyIndexError

74 |
75 |
76 |
public enum AnnoyIndexError : Error, LocalizedError
77 | 78 |
79 |
80 |

Errors thrown by AnnoyIndex.

81 | 82 |

values

83 | 84 |

invalidVectorLenght 85 | addItemFailed 86 | buildFailed 87 | unbuildFailed 88 | saveFailed 89 | loadFailed

90 | 91 |
92 |
93 |
94 |
    95 |
  • 96 |
    97 | 98 | 99 | 100 | invalidVectorLength(message:) 101 | 102 |
    103 |
    104 |
    105 |
    106 |
    107 |
    108 |

    Undocumented

    109 | 110 |
    111 |
    112 |

    Declaration

    113 |
    114 |

    Swift

    115 |
    case invalidVectorLength(message: String)
    116 | 117 |
    118 |
    119 |
    120 |
    121 |
  • 122 |
  • 123 |
    124 | 125 | 126 | 127 | addItemFailed 128 | 129 |
    130 |
    131 |
    132 |
    133 |
    134 |
    135 |

    Undocumented

    136 | 137 |
    138 |
    139 |

    Declaration

    140 |
    141 |

    Swift

    142 |
    case addItemFailed
    143 | 144 |
    145 |
    146 |
    147 |
    148 |
  • 149 |
  • 150 |
    151 | 152 | 153 | 154 | buildFailed 155 | 156 |
    157 |
    158 |
    159 |
    160 |
    161 |
    162 |

    Undocumented

    163 | 164 |
    165 |
    166 |

    Declaration

    167 |
    168 |

    Swift

    169 |
    case buildFailed
    170 | 171 |
    172 |
    173 |
    174 |
    175 |
  • 176 |
  • 177 |
    178 | 179 | 180 | 181 | unbuildFailed 182 | 183 |
    184 |
    185 |
    186 |
    187 |
    188 |
    189 |

    Undocumented

    190 | 191 |
    192 |
    193 |

    Declaration

    194 |
    195 |

    Swift

    196 |
    case unbuildFailed
    197 | 198 |
    199 |
    200 |
    201 |
    202 |
  • 203 |
  • 204 |
    205 | 206 | 207 | 208 | saveFailed 209 | 210 |
    211 |
    212 |
    213 |
    214 |
    215 |
    216 |

    Undocumented

    217 | 218 |
    219 |
    220 |

    Declaration

    221 |
    222 |

    Swift

    223 |
    case saveFailed
    224 | 225 |
    226 |
    227 |
    228 |
    229 |
  • 230 |
  • 231 |
    232 | 233 | 234 | 235 | loadFailed 236 | 237 |
    238 |
    239 |
    240 |
    241 |
    242 |
    243 |

    Undocumented

    244 | 245 |
    246 |
    247 |

    Declaration

    248 |
    249 |

    Swift

    250 |
    case loadFailed
    251 | 252 |
    253 |
    254 |
    255 |
    256 |
  • 257 |
  • 258 |
    259 | 260 | 261 | 262 | errorDescription 263 | 264 |
    265 |
    266 |
    267 |
    268 |
    269 |
    270 | 271 |
    272 |
    273 |

    Declaration

    274 |
    275 |

    Swift

    276 |
    public var errorDescription: String? { get }
    277 | 278 |
    279 |
    280 |
    281 |
    282 |
  • 283 |
284 |
285 |
286 |
287 | 291 |
292 |
293 | 294 | 295 | 296 | -------------------------------------------------------------------------------- /docs/docsets/.docset/Contents/Resources/Documents/Enums/AnnoyIndexError.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | AnnoyIndexError Enumeration Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

Docs (83% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 70 |
71 |
72 |
73 |

AnnoyIndexError

74 |
75 |
76 |
public enum AnnoyIndexError : Error, LocalizedError
77 | 78 |
79 |
80 |

Errors thrown by AnnoyIndex.

81 | 82 |

values

83 | 84 |

invalidVectorLenght 85 | addItemFailed 86 | buildFailed 87 | unbuildFailed 88 | saveFailed 89 | loadFailed

90 | 91 |
92 |
93 |
94 |
    95 |
  • 96 |
    97 | 98 | 99 | 100 | invalidVectorLength(message:) 101 | 102 |
    103 |
    104 |
    105 |
    106 |
    107 |
    108 |

    Undocumented

    109 | 110 |
    111 |
    112 |

    Declaration

    113 |
    114 |

    Swift

    115 |
    case invalidVectorLength(message: String)
    116 | 117 |
    118 |
    119 |
    120 |
    121 |
  • 122 |
  • 123 |
    124 | 125 | 126 | 127 | addItemFailed 128 | 129 |
    130 |
    131 |
    132 |
    133 |
    134 |
    135 |

    Undocumented

    136 | 137 |
    138 |
    139 |

    Declaration

    140 |
    141 |

    Swift

    142 |
    case addItemFailed
    143 | 144 |
    145 |
    146 |
    147 |
    148 |
  • 149 |
  • 150 |
    151 | 152 | 153 | 154 | buildFailed 155 | 156 |
    157 |
    158 |
    159 |
    160 |
    161 |
    162 |

    Undocumented

    163 | 164 |
    165 |
    166 |

    Declaration

    167 |
    168 |

    Swift

    169 |
    case buildFailed
    170 | 171 |
    172 |
    173 |
    174 |
    175 |
  • 176 |
  • 177 |
    178 | 179 | 180 | 181 | unbuildFailed 182 | 183 |
    184 |
    185 |
    186 |
    187 |
    188 |
    189 |

    Undocumented

    190 | 191 |
    192 |
    193 |

    Declaration

    194 |
    195 |

    Swift

    196 |
    case unbuildFailed
    197 | 198 |
    199 |
    200 |
    201 |
    202 |
  • 203 |
  • 204 |
    205 | 206 | 207 | 208 | saveFailed 209 | 210 |
    211 |
    212 |
    213 |
    214 |
    215 |
    216 |

    Undocumented

    217 | 218 |
    219 |
    220 |

    Declaration

    221 |
    222 |

    Swift

    223 |
    case saveFailed
    224 | 225 |
    226 |
    227 |
    228 |
    229 |
  • 230 |
  • 231 |
    232 | 233 | 234 | 235 | loadFailed 236 | 237 |
    238 |
    239 |
    240 |
    241 |
    242 |
    243 |

    Undocumented

    244 | 245 |
    246 |
    247 |

    Declaration

    248 |
    249 |

    Swift

    250 |
    case loadFailed
    251 | 252 |
    253 |
    254 |
    255 |
    256 |
  • 257 |
  • 258 |
    259 | 260 | 261 | 262 | errorDescription 263 | 264 |
    265 |
    266 |
    267 |
    268 |
    269 |
    270 | 271 |
    272 |
    273 |

    Declaration

    274 |
    275 |

    Swift

    276 |
    public var errorDescription: String? { get }
    277 | 278 |
    279 |
    280 |
    281 |
    282 |
  • 283 |
284 |
285 |
286 |
287 | 291 |
292 |
293 | 294 | 295 | 296 | --------------------------------------------------------------------------------