diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index a874e1d8..2e2bad39 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -109,3 +109,4 @@ jobs: xcodebuild -scheme embedder-tool -skipMacroValidation xcodebuild -scheme image-tool -skipMacroValidation xcodebuild -scheme mnist-tool -skipMacroValidation + xcodebuild -scheme vector-search-tool -skipMacroValidation diff --git a/ACKNOWLEDGMENTS.md b/ACKNOWLEDGMENTS.md index 2580e925..02b3e6b8 100644 --- a/ACKNOWLEDGMENTS.md +++ b/ACKNOWLEDGMENTS.md @@ -7,6 +7,7 @@ MLX Swift was developed with contributions from the following individuals: - [John Mai](https://github.com/johnmai-dev): Added support for multiple models (Qwen2, Starcoder2, InternLM2, Qwen3, Qwen3 MoE, GLM-4, MiMo, BitNet, SmolLM3, LFM2, Baichuan-M1). +- [Joel Nishanth](https://offlyn.ai): Added `vector-search-tool` demonstrating MLX-accelerated TurboQuant vector search via [mlx-turbovec-swift](https://github.com/offlyn-ai/mlx-turbovec-swift). diff --git a/Package.resolved b/Package.resolved index 30b3e81a..8e43c0ba 100644 --- a/Package.resolved +++ b/Package.resolved @@ -23,8 +23,26 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/ml-explore/mlx-swift", "state" : { - "revision" : "61b9e011e09a62b489f6bd647958f1555bdf2896", - "version" : "0.31.3" + "revision" : "dc43e62d7055353c7f99fa071a4e71d29dfddc44", + "version" : "0.31.4" + } + }, + { + "identity" : "mlx-turbovec-swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/offlyn-ai/mlx-turbovec-swift.git", + "state" : { + "revision" : "2e9acad9bdd02b70685ae9a8ebfdf467b4c6ff08", + "version" : "0.1.0" + } + }, + { + "identity" : "swift-argument-parser", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-argument-parser", + "state" : { + "revision" : "6a52f3251125d74daf04fcbd5e6f08a75d074382", + "version" : "1.8.2" } }, { diff --git a/Package.swift b/Package.swift index fa7a7efc..769fc45c 100644 --- a/Package.swift +++ b/Package.swift @@ -16,6 +16,8 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/ml-explore/mlx-swift", .upToNextMinor(from: "0.31.4")), + .package(url: "https://github.com/offlyn-ai/mlx-turbovec-swift.git", from: "0.1.0"), + .package(url: "https://github.com/apple/swift-argument-parser", from: "1.4.0"), // Note: used by StableDiffusion library to download weights .package( @@ -59,6 +61,17 @@ let package = Package( .enableExperimentalFeature("StrictConcurrency") ] ), + .executableTarget( + name: "vector-search-tool", + dependencies: [ + .product(name: "TurboVec", package: "mlx-turbovec-swift"), + .product(name: "ArgumentParser", package: "swift-argument-parser"), + ], + path: "Tools/vector-search-tool", + exclude: [ + "README.md" + ] + ), ] ) diff --git a/README.md b/README.md index 8e27e8ff..fe84f06f 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,10 @@ examples use models implemented in [MLX Swift LM](https://github.com/ml-explore/ - [mnist-tool](Tools/mnist-tool/README.md): A command line tool for training a a LeNet on MNIST. +- [vector-search-tool](Tools/vector-search-tool/README.md): A command line tool + demonstrating MLX-accelerated TurboQuant vector search with compression and + recall benchmarks. Uses [mlx-turbovec-swift](https://github.com/offlyn-ai/mlx-turbovec-swift). + ## Numerical Computing Examples that use MLX for general numerical computing (no ML model involved), diff --git a/Tools/vector-search-tool/README.md b/Tools/vector-search-tool/README.md new file mode 100644 index 00000000..3adbf11a --- /dev/null +++ b/Tools/vector-search-tool/README.md @@ -0,0 +1,80 @@ +# vector-search-tool + +A command-line tool demonstrating [mlx-turbovec-swift](https://github.com/offlyn-ai/mlx-turbovec-swift) — TurboQuant vector quantization with optional MLX GPU acceleration. + +Unlike `embedder-tool` (which embeds text with MLX Embedders and stores raw vectors as JSON), this tool shows **compressed vector search**: 768-dim embeddings shrink from 3,072 bytes to 384 bytes (4-bit) while maintaining >90% recall@10. + +### Building + +Build the `vector-search-tool` scheme in Xcode, or use Swift Package Manager: + +```bash +swift build --target vector-search-tool +``` + +### Running: Command Line + +Use the `mlx-run` helper after building in Xcode: + +```bash +./mlx-run vector-search-tool --gpu --dim 768 --count 10000 --queries 100 --k 10 +``` + +Pass `--debug` after `mlx-run` to run the Debug configuration. + +Enable MLX GPU acceleration with `--gpu`. Without it, TurboVec falls back to Accelerate CPU paths (useful for CI and environments without Metal). + +Write a structured JSON report for LLM analysis: + +```bash +./mlx-run vector-search-tool --gpu --json /tmp/turbovec-report.json +``` + +### Running: Xcode + +Configure scheme arguments (Product > Scheme > Edit Scheme > Run > Arguments): + +``` +--gpu --dim 768 --count 10000 --queries 50 --k 10 +``` + +Then press +R to run. + +### Options + +| Flag | Default | Description | +|------|---------|-------------| +| `--dim` | 768 | Vector dimension (multiple of 8) | +| `--count` | 10000 | Vectors to index | +| `--queries` | 100 | Search queries to run | +| `--k` | 10 | Top-k neighbors | +| `--bits` | 4 | Quantization bit width (2, 3, or 4) | +| `--gpu` | off | Enable MLX GPU acceleration | +| `--json` | — | Save JSON benchmark report to path | + +### Expected Output + +``` +▸ MLX GPU acceleration enabled +▸ Generating 10000 random unit vectors (d=768)... +▸ Indexing with 4-bit TurboQuant... + ✓ Indexed in 842.3ms + ✓ Compression: 8.0× (30720000 → 3840000 bytes) +▸ Running 100 searches (k=10)... + ✓ Mean latency: 0.412ms + ✓ P99 latency: 0.891ms + ✓ QPS: 2427 +▸ Measuring recall vs brute-force baseline... + ✓ Recall@1: 0.9100 + ✓ Recall@10: 0.9450 + ✓ Brute-force mean: 12.340ms + ✓ Speedup: 29.9× + +Summary: 8.0× compression, R@1=0.9100, 29.9× faster than brute-force +``` + +See also: + +- [mlx-turbovec-swift](https://github.com/offlyn-ai/mlx-turbovec-swift) — the TurboQuant library +- [embedder-tool](../embedder-tool/README.md) — MLX Embedders for text embedding + JSON index +- [MLX troubleshooting](https://swiftpackageindex.com/ml-explore/mlx-swift/main/documentation/mlx/troubleshooting) diff --git a/Tools/vector-search-tool/VectorSearchTool.swift b/Tools/vector-search-tool/VectorSearchTool.swift new file mode 100644 index 00000000..ffeca4f2 --- /dev/null +++ b/Tools/vector-search-tool/VectorSearchTool.swift @@ -0,0 +1,225 @@ +import ArgumentParser +import Foundation +import TurboVec + +/// Command-line demo of MLX-accelerated TurboQuant vector search. +/// +/// Generates synthetic unit vectors, indexes them with TurboVec, runs top-k +/// search, and reports compression, latency, and recall vs brute-force cosine. +@main +struct VectorSearchTool: ParsableCommand { + static let configuration = CommandConfiguration( + abstract: "Demonstrate MLX-accelerated TurboQuant vector search", + discussion: """ + Builds a TurboQuant index over random vectors and compares approximate \ + search against an exact brute-force baseline. Enable MLX GPU acceleration \ + before indexing for faster batch rotation. + """ + ) + + @Option(name: .long, help: "Vector dimension (multiple of 8)") + var dim: Int = 768 + + @Option(name: .long, help: "Number of vectors to index") + var count: Int = 10_000 + + @Option(name: .long, help: "Number of search queries") + var queries: Int = 100 + + @Option(name: .long, help: "Top-k neighbors to retrieve") + var k: Int = 10 + + @Option(name: .long, help: "Quantization bit width (2, 3, or 4)") + var bits: Int = 4 + + @Flag(name: .long, help: "Enable MLX GPU acceleration via TurboVec") + var gpu = false + + @Option(name: .long, help: "Write structured JSON benchmark report to path") + var json: String? + + mutating func run() throws { + guard dim > 0, dim % 8 == 0 else { + throw ValidationError("--dim must be a positive multiple of 8") + } + guard let bitWidth = BitWidth(rawValue: UInt8(bits)) else { + throw ValidationError("--bits must be 2, 3, or 4") + } + guard count > 0, queries > 0, k > 0 else { + throw ValidationError("--count, --queries, and --k must be positive") + } + + if gpu { + MLXBackend.enableGPU() + print("▸ MLX GPU acceleration enabled") + } else { + print("▸ CPU-only mode (pass --gpu to enable MLX acceleration)") + } + + print("▸ Generating \(count) random unit vectors (d=\(dim))...") + var rng = SplitMix64(seed: 42) + let vectors = Self.generateVectors(n: count, dim: dim, rng: &rng) + let queryVectors = Self.generateVectors(n: queries, dim: dim, rng: &rng) + + print("▸ Indexing with \(bits)-bit TurboQuant...") + let index = TurboQuantIndex(dim: dim, bitWidth: bitWidth) + let indexStart = CFAbsoluteTimeGetCurrent() + try index.add(vectors) + let indexMs = (CFAbsoluteTimeGetCurrent() - indexStart) * 1000 + + let rawBytes = count * dim * MemoryLayout.size + let indexBytes = index.indexSizeBytes + let compression = Double(rawBytes) / Double(indexBytes) + + print(" ✓ Indexed in \(String(format: "%.1f", indexMs))ms") + print(" ✓ Compression: \(String(format: "%.1f", compression))× (\(rawBytes) → \(indexBytes) bytes)") + + print("▸ Running \(queries) searches (k=\(k))...") + var latencies = [Double]() + var approximateResults = [[SearchHit]]() + + for query in queryVectors { + let start = CFAbsoluteTimeGetCurrent() + let hits = try index.search(query: query, k: k) + latencies.append((CFAbsoluteTimeGetCurrent() - start) * 1000) + approximateResults.append(hits) + } + + let searchStats = Self.latencyStats(latencies) + print(" ✓ Mean latency: \(String(format: "%.3f", searchStats.meanMs))ms") + print(" ✓ P99 latency: \(String(format: "%.3f", searchStats.p99Ms))ms") + print(" ✓ QPS: \(String(format: "%.0f", 1000.0 / searchStats.meanMs))") + + print("▸ Measuring recall vs brute-force baseline...") + var recall1Sum = 0.0 + var recallKSum = 0.0 + var bfLatencies = [Double]() + + for (qi, query) in queryVectors.enumerated() { + let start = CFAbsoluteTimeGetCurrent() + let exact = BruteForceSearch.search(query: query, vectors: vectors, k: k) + bfLatencies.append((CFAbsoluteTimeGetCurrent() - start) * 1000) + + let approx = approximateResults[qi] + let exactTopK = Set(exact.prefix(k).map(\.index)) + let approxIndices = approx.prefix(k).map(\.index) + + if !exact.isEmpty, !approxIndices.isEmpty, approxIndices[0] == exact[0].index { + recall1Sum += 1 + } + let overlap = approxIndices.filter { exactTopK.contains($0) }.count + recallKSum += Double(overlap) / Double(min(k, exact.count)) + } + + let recall1 = recall1Sum / Double(queries) + let recallK = recallKSum / Double(queries) + let bfStats = Self.latencyStats(bfLatencies) + let speedup = bfStats.meanMs / searchStats.meanMs + + print(" ✓ Recall@1: \(String(format: "%.4f", recall1))") + print(" ✓ Recall@\(k): \(String(format: "%.4f", recallK))") + print(" ✓ Brute-force mean: \(String(format: "%.3f", bfStats.meanMs))ms") + print(" ✓ Speedup: \(String(format: "%.1f", speedup))×") + + let report = BenchmarkReport( + hardware: Self.detectHardware(), + config: BenchmarkReport.TestConfig( + dimensions: dim, + bitWidth: bits, + numVectors: count, + numQueries: queries, + k: k + ), + search: BenchmarkReport.SearchResult( + latency: searchStats, + queriesPerSecond: 1000.0 / searchStats.meanMs, + recallAt1: recall1, + recallAtK: recallK + ), + memory: BenchmarkReport.MemoryUsage( + rawBytes: rawBytes, + indexBytes: indexBytes, + compressionRatio: compression, + bytesPerVector: Double(indexBytes) / Double(count) + ), + indexing: BenchmarkReport.IndexingResult( + totalMs: indexMs, + perVectorUs: indexMs * 1000.0 / Double(count), + vectorsPerSecond: Double(count) / (indexMs / 1000.0) + ), + bruteForceBaseline: BenchmarkReport.SearchResult( + latency: bfStats, + queriesPerSecond: 1000.0 / bfStats.meanMs, + recallAt1: 1.0, + recallAtK: 1.0 + ), + timestamp: ISO8601DateFormatter().string(from: Date()), + version: "0.1.0" + ) + + print() + print("Summary: \(String(format: "%.1f", compression))× compression, " + + "R@1=\(String(format: "%.4f", recall1)), " + + "\(String(format: "%.1f", speedup))× faster than brute-force") + + let jsonText = try report.jsonString() + if let json { + try jsonText.write(toFile: json, atomically: true, encoding: .utf8) + print("Report saved to \(json)") + } + + print() + print("--- JSON Report ---") + print(jsonText) + } + + private static func generateVectors(n: Int, dim: Int, rng: inout SplitMix64) -> [[Float]] { + (0.. Float in + let u1 = max(Float(rng.nextUniform()), Float.leastNormalMagnitude) + let u2 = Float(rng.nextUniform()) + return sqrtf(-2.0 * logf(u1)) * cosf(2.0 * .pi * u2) + } + VectorMath.normalize(&vector) + return vector + } + } + + private static func latencyStats(_ values: [Double]) -> BenchmarkReport.LatencyStats { + let sorted = values.sorted() + let n = sorted.count + return BenchmarkReport.LatencyStats( + p50Ms: sorted[n / 2], + p95Ms: sorted[Int(Double(n) * 0.95)], + p99Ms: sorted[Int(Double(n) * 0.99)], + meanMs: sorted.reduce(0, +) / Double(n), + minMs: sorted.first ?? 0, + maxMs: sorted.last ?? 0 + ) + } + + private static func detectHardware() -> BenchmarkReport.HardwareInfo { + var chip = "Unknown" + #if arch(arm64) + chip = sysctlString("machdep.cpu.brand_string") ?? "Apple Silicon" + #else + chip = sysctlString("machdep.cpu.brand_string") ?? "x86_64" + #endif + + return BenchmarkReport.HardwareInfo( + chip: chip, + cores: ProcessInfo.processInfo.activeProcessorCount, + memoryGB: Int(ProcessInfo.processInfo.physicalMemory / (1024 * 1024 * 1024)), + os: ProcessInfo.processInfo.operatingSystemVersionString + ) + } + + private static func sysctlString(_ name: String) -> String? { + var size = 0 + sysctlbyname(name, nil, &size, nil, 0) + guard size > 0 else { return nil } + var buffer = [CChar](repeating: 0, count: size) + sysctlbyname(name, &buffer, &size, nil, 0) + return String(cString: buffer) + } +} diff --git a/mlx-swift-examples.xcodeproj/project.pbxproj b/mlx-swift-examples.xcodeproj/project.pbxproj index 87ab36c6..5f609261 100644 --- a/mlx-swift-examples.xcodeproj/project.pbxproj +++ b/mlx-swift-examples.xcodeproj/project.pbxproj @@ -58,6 +58,8 @@ C3EA7F6F2F8444A00054AEA3 /* Tokenizers in Frameworks */ = {isa = PBXBuildFile; productRef = C3EA7F6E2F8444A00054AEA3 /* Tokenizers */; }; C3EA7F732F8445EE0054AEA3 /* HuggingFace in Frameworks */ = {isa = PBXBuildFile; productRef = C3EA7F722F8445EE0054AEA3 /* HuggingFace */; }; C3EA7F752F8445EE0054AEA3 /* Tokenizers in Frameworks */ = {isa = PBXBuildFile; productRef = C3EA7F742F8445EE0054AEA3 /* Tokenizers */; }; + E3A1B00D2F06000100000000 /* TurboVec in Frameworks */ = {isa = PBXBuildFile; productRef = E3A1B00B2F06000100000000 /* TurboVec */; }; + E3A1B00E2F06000100000000 /* ArgumentParser in Frameworks */ = {isa = PBXBuildFile; productRef = E3A1B00C2F06000100000000 /* ArgumentParser */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -70,6 +72,15 @@ ); runOnlyForDeploymentPostprocessing = 1; }; + E3A1B0082F06000100000000 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; C3056BC12BCD984F00A31D04 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -198,6 +209,7 @@ /* Begin PBXFileReference section */ 0A979CD12E97175800635D67 /* embedder-tool */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "embedder-tool"; sourceTree = BUILT_PRODUCTS_DIR; }; + E3A1B0022F06000100000000 /* vector-search-tool */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "vector-search-tool"; sourceTree = BUILT_PRODUCTS_DIR; }; 0F5AD7412DB70C0300745C06 /* MLXChatExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MLXChatExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; C3056BAB2BCD97B700A31D04 /* LoRATrainingExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LoRATrainingExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; C3132C232DFB2FFB00270E8E /* ExampleLLM */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ExampleLLM; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -294,6 +306,13 @@ ); target = C36BEFDF2BC32988002D4AFE /* image-tool */; }; + E3A1B0092F06000100000000 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + "vector-search-tool/VectorSearchTool.swift", + ); + target = E3A1B0012F06000100000000 /* vector-search-tool */; + }; C314FDD82EF3315A00E5B73A /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( @@ -428,7 +447,7 @@ /* Begin PBXFileSystemSynchronizedRootGroup section */ C314FCF02EF3313B00E5B73A /* Configuration */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = Configuration; sourceTree = ""; }; C314FCF62EF3314200E5B73A /* Data */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (C314FCFB2EF3314200E5B73A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = Data; sourceTree = ""; }; - C314FD242EF3315100E5B73A /* Tools */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (C314FD422EF3315100E5B73A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, C314FD442EF3315100E5B73A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, C314FD462EF3315100E5B73A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, C314FD432EF3315100E5B73A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, C314FD412EF3315100E5B73A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, C314FD452EF3315100E5B73A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = Tools; sourceTree = ""; }; + C314FD242EF3315100E5B73A /* Tools */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (C314FD422EF3315100E5B73A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, C314FD442EF3315100E5B73A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, C314FD462EF3315100E5B73A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, C314FD432EF3315100E5B73A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, C314FD412EF3315100E5B73A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, C314FD452EF3315100E5B73A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, E3A1B0092F06000100000000 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = Tools; sourceTree = ""; }; C314FDA02EF3315A00E5B73A /* Applications */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (C314FDD82EF3315A00E5B73A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, C314FDDA2EF3315A00E5B73A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, C314FEED2EF33B3900E5B73A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, C314FDDD2EF3315A00E5B73A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, C314FDDC2EF3315A00E5B73A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, C314FDDB2EF3315A00E5B73A /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = Applications; sourceTree = ""; }; C397D92E2CD440EF00B87EE2 /* Libraries */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = Libraries; sourceTree = ""; }; C3D9F9312FD36A810038C3B3 /* Numerical */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (C3D9FB882FD36CDA0038C3B3 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, C3D9FB742FD36CA40038C3B3 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, C3D9FB612FD36C500038C3B3 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = Numerical; sourceTree = ""; }; @@ -447,6 +466,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + E3A1B0072F06000100000000 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + E3A1B00D2F06000100000000 /* TurboVec in Frameworks */, + E3A1B00E2F06000100000000 /* ArgumentParser in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 0F5AD73E2DB70C0300745C06 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -640,6 +668,7 @@ 0F5AD7412DB70C0300745C06 /* MLXChatExample.app */, C3132C232DFB2FFB00270E8E /* ExampleLLM */, 0A979CD12E97175800635D67 /* embedder-tool */, + E3A1B0022F06000100000000 /* vector-search-tool */, C314FEDB2EF33B2F00E5B73A /* LLMBasic.app */, C3D9F9362FD36A9B0038C3B3 /* Mandelbrot.app */, C3D9F9452FD36AB50038C3B3 /* HeatTransfer.app */, @@ -686,6 +715,27 @@ productReference = 0A979CD12E97175800635D67 /* embedder-tool */; productType = "com.apple.product-type.tool"; }; + E3A1B0012F06000100000000 /* vector-search-tool */ = { + isa = PBXNativeTarget; + buildConfigurationList = E3A1B0032F06000100000000 /* Build configuration list for PBXNativeTarget "vector-search-tool" */; + buildPhases = ( + E3A1B0062F06000100000000 /* Sources */, + E3A1B0072F06000100000000 /* Frameworks */, + E3A1B0082F06000100000000 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "vector-search-tool"; + packageProductDependencies = ( + E3A1B00B2F06000100000000 /* TurboVec */, + E3A1B00C2F06000100000000 /* ArgumentParser */, + ); + productName = "vector-search-tool"; + productReference = E3A1B0022F06000100000000 /* vector-search-tool */; + productType = "com.apple.product-type.tool"; + }; 0F5AD7402DB70C0300745C06 /* MLXChatExample */ = { isa = PBXNativeTarget; buildConfigurationList = 0F5AD74C2DB70C0400745C06 /* Build configuration list for PBXNativeTarget "MLXChatExample" */; @@ -1054,6 +1104,9 @@ 0A979CD02E97175800635D67 = { CreatedOnToolsVersion = 26.0.1; }; + E3A1B0012F06000100000000 = { + CreatedOnToolsVersion = 26.0.1; + }; 0F5AD7402DB70C0300745C06 = { CreatedOnToolsVersion = 16.2; }; @@ -1120,6 +1173,7 @@ C3EA7F522F8431BE0054AEA3 /* XCRemoteSwiftPackageReference "swift-huggingface" */, C3EA7F532F8432080054AEA3 /* XCRemoteSwiftPackageReference "swift-transformers" */, C36941322F8F489800810A49 /* XCRemoteSwiftPackageReference "mlx-swift-lm" */, + E3A1B00A2F06000100000000 /* XCRemoteSwiftPackageReference "mlx-turbovec-swift" */, ); productRefGroup = C39273752B606A0A00368D5D /* Products */; projectDirPath = ""; @@ -1131,6 +1185,7 @@ C397C58A2B62C6A9004B084D /* llm-tool */, C3132C222DFB2FFB00270E8E /* ExampleLLM */, 0A979CD02E97175800635D67 /* embedder-tool */, + E3A1B0012F06000100000000 /* vector-search-tool */, C36BF0002BC5CE55002D4AFE /* StableDiffusionExample */, C36BEFDF2BC32988002D4AFE /* image-tool */, C3056BAA2BCD97B700A31D04 /* LoRATrainingExample */, @@ -1227,6 +1282,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + E3A1B0062F06000100000000 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 0F5AD73D2DB70C0300745C06 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1422,6 +1484,86 @@ }; name = Release; }; + E3A1B0042F06000100000000 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = ""; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 14.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + E3A1B0052F06000100000000 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = ""; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 14.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; 0F5AD74D2DB70C0400745C06 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -3723,6 +3865,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + E3A1B0032F06000100000000 /* Build configuration list for PBXNativeTarget "vector-search-tool" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E3A1B0042F06000100000000 /* Debug */, + E3A1B0052F06000100000000 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 0F5AD74C2DB70C0400745C06 /* Build configuration list for PBXNativeTarget "MLXChatExample" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -3954,6 +4105,14 @@ traits = ( ); }; + E3A1B00A2F06000100000000 /* XCRemoteSwiftPackageReference "mlx-turbovec-swift" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/offlyn-ai/mlx-turbovec-swift.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 0.1.0; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -3962,6 +4121,16 @@ package = C392736E2B60699100368D5D /* XCRemoteSwiftPackageReference "swift-argument-parser" */; productName = ArgumentParser; }; + E3A1B00B2F06000100000000 /* TurboVec */ = { + isa = XCSwiftPackageProductDependency; + package = E3A1B00A2F06000100000000 /* XCRemoteSwiftPackageReference "mlx-turbovec-swift" */; + productName = TurboVec; + }; + E3A1B00C2F06000100000000 /* ArgumentParser */ = { + isa = XCSwiftPackageProductDependency; + package = C392736E2B60699100368D5D /* XCRemoteSwiftPackageReference "swift-argument-parser" */; + productName = ArgumentParser; + }; 0A979D122E9743D800635D67 /* MLXEmbedders */ = { isa = XCSwiftPackageProductDependency; package = C397D8F22CD2F60B00B87EE2 /* XCLocalSwiftPackageReference "Libraries/.." */; diff --git a/mlx-swift-examples.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/mlx-swift-examples.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 0ea5f645..9ed00640 100644 --- a/mlx-swift-examples.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/mlx-swift-examples.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "e538f345418093ec944f6af6ff98e1b0c1e736fb7c1f52379fbcf33a8ac26084", + "originHash" : "eb0800a07c6a73288a79eda943494b570c447748bd3cbf8911e39559d9b1eb59", "pins" : [ { "identity" : "eventsource", @@ -37,6 +37,15 @@ "version" : "3.31.3" } }, + { + "identity" : "mlx-turbovec-swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/offlyn-ai/mlx-turbovec-swift.git", + "state" : { + "revision" : "2e9acad9bdd02b70685ae9a8ebfdf467b4c6ff08", + "version" : "0.1.0" + } + }, { "identity" : "networkimage", "kind" : "remoteSourceControl", diff --git a/mlx-swift-examples.xcodeproj/xcshareddata/xcschemes/vector-search-tool.xcscheme b/mlx-swift-examples.xcodeproj/xcshareddata/xcschemes/vector-search-tool.xcscheme new file mode 100644 index 00000000..603657ec --- /dev/null +++ b/mlx-swift-examples.xcodeproj/xcshareddata/xcschemes/vector-search-tool.xcscheme @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +