Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
1983fc9
Added svd.rs file, where I will code a pure-Rust SVD implementation t…
SarahNasser576 Jun 16, 2026
bb307d0
Formed the A^T A matrix, and used Jacobi iteration to eigendecompose …
SarahNasser576 Jun 20, 2026
b54450b
Set σᵢ = √λᵢ
Jun 22, 2026
0cea39f
Sorted all n eigenvalues in descending order before computing singula…
Jun 22, 2026
e26009a
Computed left singular vectors uᵢ = (1/σᵢ) * A * vᵢ. Used the sorted …
Jun 22, 2026
71fc8cb
Padded the U matrix with zero columns if m > k. Used Gram-Schmidt to…
Jun 23, 2026
812d848
Packed the columns of the U matrix into a row-major mxm matrix
Jun 23, 2026
52972c8
Built the V^T matrix and returned the U, sigma, and V^T matrices
Jun 23, 2026
2b347fe
Implemented the create_identity_matrix() function, which creates an n…
Jun 23, 2026
7202b70
Created the jacobi_rotate() function, which applies a Givens rotation…
Jun 23, 2026
49c6009
Renamed a few functions for clarity purposes
Jun 23, 2026
3d64e1f
Fixed a typo in a variable name
Jun 23, 2026
5602eed
Removed a semicolon from a return value
Jun 23, 2026
4dd41f8
Checked whether the input matrix has at least one row and at least on…
Jun 23, 2026
74fed31
Checked whether the data length of the input matrix matches the produ…
Jun 23, 2026
80fd348
Checked whether the input matrix contains null or infinite entries
Jun 23, 2026
6187fde
Edited my input validation statements for clarity purposes
Jun 23, 2026
ff2e272
Converted my input validation statement for whether the matrix has at…
Jun 23, 2026
7bb18c1
Converted my input validation statement for whether the data length o…
Jun 23, 2026
93584c8
Converted my input validation statement for whether the matrix contai…
Jun 23, 2026
fe78e53
Edited all my input validation logic so that multiple different error…
Jun 23, 2026
bcfca5c
Included unit, integration, and manual tests and made a minor edit to…
Jun 24, 2026
355603b
Edited my input validation logic so that the error message on empty i…
Jun 30, 2026
70b006c
Edited my input validation logic so that all the output matrices are …
Jun 30, 2026
b67fd6f
Included comments explaining my code
Jun 30, 2026
40e00ac
Removed the tests from this file to tidy this file up
Jun 30, 2026
f791cd4
Merge branch 'main' into fix-issue-SVDImplementation
SarahNasser576 Jun 30, 2026
d3e671d
Merge branch 'main' into fix-issue-SVDImplementation
SarahNasser576 Jul 1, 2026
5dbd8d2
Merge branch 'main' into fix-issue-SVDImplementation
SarahNasser576 Jul 2, 2026
ac7dd21
Merge branch 'main' into fix-issue-SVDImplementation
SarahNasser576 Jul 3, 2026
253ed81
Merge branch 'main' into fix-issue-SVDImplementation
SarahNasser576 Jul 6, 2026
9d50ed4
Merge branch 'main' into fix-issue-SVDImplementation
SarahNasser576 Jul 8, 2026
49f8d5f
Normalized the YAML list indentation in pre-commit-config.yaml
SarahNasser576 Jul 17, 2026
2dafa91
Merge branch 'main' into fix-issue-SVDImplementation
SarahNasser576 Jul 17, 2026
2877427
Added real rustdoc comments with an example for the public API svd
Jul 17, 2026
0f431bf
Returned a Result with typed errors for invalid input to svd() function
Jul 17, 2026
d6d60e4
Checked that m*n, m*m, and n*n don't overflow usize
Jul 17, 2026
4085b92
Made Jacobi convergence aware of small-scale matrices A and actually …
Jul 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 82 additions & 44 deletions benchmarks/full_report/report.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@
"outputs": [],
"source": [
"import sys\n",
"\n",
"sys.dont_write_bytecode = True\n",
"\n",
"import os\n",
"\n",
"module_path = os.path.abspath(os.path.join('.'))\n",
"module_path = os.path.abspath(os.path.join(\".\"))\n",
"if module_path not in sys.path:\n",
" sys.path.append(module_path)"
]
Expand Down Expand Up @@ -61,58 +62,91 @@
" gt_in_sample = knn(in_sample, data, metric, 10)\n",
"\n",
" print(\"generated gt\")\n",
" \n",
"\n",
" with tempfile.TemporaryDirectory() as d:\n",
" write_lance(d, data)\n",
" ds = lance.dataset(d)\n",
"\n",
" for q, target in zip(tqdm(in_sample, desc=\"checking brute force\"), gt_in_sample):\n",
" res = ds.to_table(nearest={\n",
" \"column\": \"vec\",\n",
" \"q\": q,\n",
" \"k\": 10,\n",
" \"metric\": metric,\n",
" }, columns=[\"id\"])\n",
" for q, target in zip(\n",
" tqdm(in_sample, desc=\"checking brute force\"), gt_in_sample\n",
" ):\n",
" res = ds.to_table(\n",
" nearest={\n",
" \"column\": \"vec\",\n",
" \"q\": q,\n",
" \"k\": 10,\n",
" \"metric\": metric,\n",
" },\n",
" columns=[\"id\"],\n",
" )\n",
" assert len(np.intersect1d(res[\"id\"].to_numpy(), target)) == 10\n",
" \n",
" ds = ds.create_index(\"vec\", \"IVF_PQ\", metric=metric, num_partitions=num_partitions, num_sub_vectors=num_sub_vectors)\n",
" \n",
"\n",
" ds = ds.create_index(\n",
" \"vec\",\n",
" \"IVF_PQ\",\n",
" metric=metric,\n",
" num_partitions=num_partitions,\n",
" num_sub_vectors=num_sub_vectors,\n",
" )\n",
"\n",
" recall_data = []\n",
" for nprobes in nprobes_list:\n",
" for refine_factor in refine_factor_list:\n",
" hits = 0\n",
" # check that brute force impl is correct\n",
" for q, target in zip(tqdm(query, desc=f\"out of sample, nprobes={nprobes}, refine={refine_factor}\"), gt):\n",
" res = ds.to_table(nearest={\n",
" \"column\": \"vec\",\n",
" \"q\": q,\n",
" \"k\": 10,\n",
" \"nprobes\": nprobes,\n",
" \"refine_factor\": refine_factor,\n",
" }, columns=[\"id\"])[\"id\"].to_numpy()\n",
" for q, target in zip(\n",
" tqdm(\n",
" query,\n",
" desc=f\"out of sample, nprobes={nprobes}, refine={refine_factor}\",\n",
" ),\n",
" gt,\n",
" ):\n",
" res = ds.to_table(\n",
" nearest={\n",
" \"column\": \"vec\",\n",
" \"q\": q,\n",
" \"k\": 10,\n",
" \"nprobes\": nprobes,\n",
" \"refine_factor\": refine_factor,\n",
" },\n",
" columns=[\"id\"],\n",
" )[\"id\"].to_numpy()\n",
" hits += len(np.intersect1d(res, target))\n",
" recall_data.append([\n",
" \"out_of_sample\",\n",
" nprobes,\n",
" refine_factor,\n",
" hits / 10 / len(gt),\n",
" ])\n",
" recall_data.append(\n",
" [\n",
" \"out_of_sample\",\n",
" nprobes,\n",
" refine_factor,\n",
" hits / 10 / len(gt),\n",
" ]\n",
" )\n",
" # check that brute force impl is correct\n",
" for q, target in zip(tqdm(in_sample, desc=f\"in sample nprobes={nprobes}, refine={refine_factor}\"), gt_in_sample):\n",
" res = ds.to_table(nearest={\n",
" \"column\": \"vec\",\n",
" \"q\": q,\n",
" \"k\": 10,\n",
" \"nprobes\": nprobes,\n",
" \"refine_factor\": refine_factor,\n",
" }, columns=[\"id\"])[\"id\"].to_numpy()\n",
" for q, target in zip(\n",
" tqdm(\n",
" in_sample,\n",
" desc=f\"in sample nprobes={nprobes}, refine={refine_factor}\",\n",
" ),\n",
" gt_in_sample,\n",
" ):\n",
" res = ds.to_table(\n",
" nearest={\n",
" \"column\": \"vec\",\n",
" \"q\": q,\n",
" \"k\": 10,\n",
" \"nprobes\": nprobes,\n",
" \"refine_factor\": refine_factor,\n",
" },\n",
" columns=[\"id\"],\n",
" )[\"id\"].to_numpy()\n",
" hits += len(np.intersect1d(res, target))\n",
" recall_data.append([\n",
" \"in_sample\",\n",
" nprobes,\n",
" refine_factor,\n",
" hits / 10 / len(gt_in_sample),\n",
" ])\n",
" recall_data.append(\n",
" [\n",
" \"in_sample\",\n",
" nprobes,\n",
" refine_factor,\n",
" hits / 10 / len(gt_in_sample),\n",
" ]\n",
" )\n",
" return recall_data"
]
},
Expand All @@ -124,15 +158,19 @@
"outputs": [],
"source": [
"def make_plot(recall_data):\n",
" df = pd.DataFrame(recall_data, columns=[\"case\", \"nprobes\", \"refine_factor\", \"recall\"])\n",
" \n",
" df = pd.DataFrame(\n",
" recall_data, columns=[\"case\", \"nprobes\", \"refine_factor\", \"recall\"]\n",
" )\n",
"\n",
" num_cases = len(df[\"case\"].unique())\n",
" (fig, axs) = plt.subplots(1, 2, figsize=(16, 8))\n",
" \n",
"\n",
" for case, ax in zip(df[\"case\"].unique(), axs):\n",
" current_case = df[df[\"case\"] == case]\n",
" sns.heatmap(\n",
" current_case.drop(columns=[\"case\"]).set_index([\"nprobes\", \"refine_factor\"])[\"recall\"].unstack(),\n",
" current_case.drop(columns=[\"case\"])\n",
" .set_index([\"nprobes\", \"refine_factor\"])[\"recall\"]\n",
" .unstack(),\n",
" annot=True,\n",
" ax=ax,\n",
" ).set(title=f\"Recall -- {case}\")"
Expand Down
2 changes: 2 additions & 0 deletions benchmarks/sift/Results.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"outputs": [],
"source": [
"import pandas as pd\n",
"\n",
"df = pd.read_csv(\"query.csv\")"
]
},
Expand All @@ -46,6 +47,7 @@
"outputs": [],
"source": [
"import seaborn as sns\n",
"\n",
"sns.set_style(\"darkgrid\")"
]
},
Expand Down
16 changes: 12 additions & 4 deletions ci/check_breaking_changes.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

Can also be used as a library to detect breaking changes without version validation.
"""

import argparse
import os
import sys
Expand Down Expand Up @@ -40,10 +41,17 @@ def detect_breaking_changes(repo, base, head):
parser = argparse.ArgumentParser()
parser.add_argument("base", help="Base commit/tag for comparison")
parser.add_argument("head", help="Head commit/tag for comparison")
parser.add_argument("last_stable_version", nargs="?", help="Last stable version (for validation)")
parser.add_argument("current_version", nargs="?", help="Current version (for validation)")
parser.add_argument("--detect-only", action="store_true",
help="Only detect breaking changes, don't validate version")
parser.add_argument(
"last_stable_version", nargs="?", help="Last stable version (for validation)"
)
parser.add_argument(
"current_version", nargs="?", help="Current version (for validation)"
)
parser.add_argument(
"--detect-only",
action="store_true",
help="Only detect breaking changes, don't validate version",
)
args = parser.parse_args()

repo = Github(os.environ["GITHUB_TOKEN"]).get_repo(os.environ["GITHUB_REPOSITORY"])
Expand Down
8 changes: 4 additions & 4 deletions ci/setup_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
This script is used to set the pre-release version for beta releases
(e.g. 0.10.17-beta.1) when the tag indicates a beta release.

With the new automated release process, stable versions are already
With the new automated release process, stable versions are already
updated by bump-my-version during the release workflow.
"""

Expand Down Expand Up @@ -38,9 +38,9 @@ def main():
print(f"Setting beta version: {current_version} -> {args.version[1:]}")
else:
# For stable releases, version should already match
assert (
parsed_version.release == current_version_parsed.release
), f"Version mismatch for stable release: {parsed_version.release} != {current_version_parsed.release}"
assert parsed_version.release == current_version_parsed.release, (
f"Version mismatch for stable release: {parsed_version.release} != {current_version_parsed.release}"
)

with open("python/Cargo.toml", "w") as f:
f.writelines(lines)
Expand Down
2 changes: 1 addition & 1 deletion memtest/tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use libc::{c_void, size_t};
use std::ptr;

// Import from the library we're testing
use memtest::{memtest_get_stats, memtest_reset_stats, MemtestStats};
use memtest::{MemtestStats, memtest_get_stats, memtest_reset_stats};

extern "C" {
fn malloc(size: size_t) -> *mut c_void;
Expand Down
31 changes: 19 additions & 12 deletions notebooks/quickstart.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@
"shutil.rmtree(\"/tmp/test.lance\", ignore_errors=True)\n",
"\n",
"tbl = pa.Table.from_pandas(df)\n",
"pa.dataset.write_dataset(tbl, \"/tmp/test.parquet\", format='parquet')\n",
"pa.dataset.write_dataset(tbl, \"/tmp/test.parquet\", format=\"parquet\")\n",
"\n",
"parquet = pa.dataset.dataset(\"/tmp/test.parquet\")\n",
"parquet.to_table().to_pandas()"
Expand Down Expand Up @@ -542,7 +542,7 @@
}
],
"source": [
"lance.dataset('/tmp/test.lance', version=1).to_table().to_pandas()"
"lance.dataset(\"/tmp/test.lance\", version=1).to_table().to_pandas()"
]
},
{
Expand Down Expand Up @@ -600,7 +600,7 @@
}
],
"source": [
"lance.dataset('/tmp/test.lance', version=2).to_table().to_pandas()"
"lance.dataset(\"/tmp/test.lance\", version=2).to_table().to_pandas()"
]
},
{
Expand Down Expand Up @@ -698,7 +698,7 @@
}
],
"source": [
"lance.dataset('/tmp/test.lance', version=\"stable\").to_table().to_pandas()"
"lance.dataset(\"/tmp/test.lance\", version=\"stable\").to_table().to_pandas()"
]
},
{
Expand Down Expand Up @@ -796,11 +796,13 @@
"\n",
"with open(\"sift/sift_base.fvecs\", mode=\"rb\") as fobj:\n",
" buf = fobj.read()\n",
" data = np.array(struct.unpack(\"<128000000f\", buf[4 : 4 + 4 * 1000000 * 128])).reshape((1000000, 128))\n",
" data = np.array(\n",
" struct.unpack(\"<128000000f\", buf[4 : 4 + 4 * 1000000 * 128])\n",
" ).reshape((1000000, 128))\n",
" dd = dict(zip(range(1000000), data))\n",
"\n",
"table = vec_to_table(dd)\n",
"lance.write_dataset(table, uri, max_rows_per_group=8192, max_rows_per_file=1024*1024)"
"lance.write_dataset(table, uri, max_rows_per_group=8192, max_rows_per_file=1024 * 1024)"
]
},
{
Expand Down Expand Up @@ -864,6 +866,7 @@
],
"source": [
"import duckdb\n",
"\n",
"# if this segfaults make sure duckdb v0.7+ is installed\n",
"samples = duckdb.query(\"SELECT vector FROM sift1m USING SAMPLE 100\").to_df().vector\n",
"samples"
Expand Down Expand Up @@ -910,10 +913,12 @@
"import time\n",
"\n",
"start = time.time()\n",
"tbl = sift1m.to_table(columns=[\"id\"], nearest={\"column\": \"vector\", \"q\": samples[0], \"k\": 10})\n",
"tbl = sift1m.to_table(\n",
" columns=[\"id\"], nearest={\"column\": \"vector\", \"q\": samples[0], \"k\": 10}\n",
")\n",
"end = time.time()\n",
"\n",
"print(f\"Time(sec): {end-start}\")\n",
"print(f\"Time(sec): {end - start}\")\n",
"print(tbl.to_pandas())"
]
},
Expand Down Expand Up @@ -993,7 +998,7 @@
"\n",
"sift1m.create_index(\n",
" \"vector\",\n",
" index_type=\"IVF_PQ\", # IVF_PQ, IVF_HNSW_PQ and IVF_HNSW_SQ are supported\n",
" index_type=\"IVF_PQ\", # IVF_PQ, IVF_HNSW_PQ and IVF_HNSW_SQ are supported\n",
" num_partitions=256, # IVF\n",
" num_sub_vectors=16, # PQ\n",
")"
Expand Down Expand Up @@ -1068,7 +1073,7 @@
" start = time.time()\n",
" tbl = sift1m.to_table(nearest={\"column\": \"vector\", \"q\": q, \"k\": 10})\n",
" end = time.time()\n",
" tot += (end - start)\n",
" tot += end - start\n",
"\n",
"print(f\"Avg(sec): {tot / len(samples)}\")\n",
"print(tbl.to_pandas())"
Expand Down Expand Up @@ -1425,7 +1430,7 @@
"source": [
"tbl = sift1m.to_table()\n",
"tbl = tbl.append_column(\"item_id\", pa.array(range(len(tbl))))\n",
"tbl = tbl.append_column(\"revenue\", pa.array((np.random.randn(len(tbl))+5)*1000))\n",
"tbl = tbl.append_column(\"revenue\", pa.array((np.random.randn(len(tbl)) + 5) * 1000))\n",
"tbl.to_pandas()"
]
},
Expand Down Expand Up @@ -1564,7 +1569,9 @@
}
],
"source": [
"sift1m.to_table(columns=[\"revenue\"], nearest={\"column\": \"vector\", \"q\": samples[0], \"k\": 10}).to_pandas()"
"sift1m.to_table(\n",
" columns=[\"revenue\"], nearest={\"column\": \"vector\", \"q\": samples[0], \"k\": 10}\n",
").to_pandas()"
]
}
],
Expand Down
Loading
Loading