fromscratch-pyvecdb 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fromscratch_pyvecdb-0.3.0.dist-info/METADATA +147 -0
- fromscratch_pyvecdb-0.3.0.dist-info/RECORD +16 -0
- fromscratch_pyvecdb-0.3.0.dist-info/WHEEL +4 -0
- fromscratch_pyvecdb-0.3.0.dist-info/licenses/LICENSE +21 -0
- pyvecdb/__init__.py +68 -0
- pyvecdb/_version.py +24 -0
- pyvecdb/atomic.py +66 -0
- pyvecdb/distance.py +138 -0
- pyvecdb/errors.py +61 -0
- pyvecdb/filtering.py +128 -0
- pyvecdb/persistence.py +149 -0
- pyvecdb/py.typed +0 -0
- pyvecdb/record.py +94 -0
- pyvecdb/search.py +165 -0
- pyvecdb/store.py +250 -0
- pyvecdb/vector.py +180 -0
pyvecdb/vector.py
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"""Vector operations for pyvecdb.
|
|
2
|
+
|
|
3
|
+
A vector is just a list of numbers -- like coordinates on a treasure map!
|
|
4
|
+
If you have a 2D map, each spot is described by two numbers (x, y).
|
|
5
|
+
In a vector database, we might use hundreds of numbers to describe
|
|
6
|
+
something complex like a song, a picture, or a sentence.
|
|
7
|
+
|
|
8
|
+
This module provides basic math operations on vectors:
|
|
9
|
+
|
|
10
|
+
- **Add** two vectors together (combine directions)
|
|
11
|
+
- **Subtract** one vector from another (find the difference)
|
|
12
|
+
- **Scale** a vector by a number (make it bigger or smaller)
|
|
13
|
+
- **Magnitude** -- how long the vector is (distance from the origin)
|
|
14
|
+
- **Normalize** -- shrink or stretch to length 1 (keep direction, standard size)
|
|
15
|
+
- **Dot product** -- a special multiplication that tells how similar two vectors are
|
|
16
|
+
|
|
17
|
+
Example:
|
|
18
|
+
>>> from pyvecdb.vector import add, magnitude, normalize
|
|
19
|
+
>>> v = [3.0, 4.0]
|
|
20
|
+
>>> magnitude(v)
|
|
21
|
+
5.0
|
|
22
|
+
>>> normalize(v)
|
|
23
|
+
[0.6, 0.8]
|
|
24
|
+
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
import math
|
|
28
|
+
|
|
29
|
+
from pyvecdb.errors import DimensionMismatchError, EmptyVectorError
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def check_not_empty(v: list[float]) -> None:
|
|
33
|
+
"""Raise EmptyVectorError if the vector has no components."""
|
|
34
|
+
if not v:
|
|
35
|
+
msg = "Vector must have at least one component"
|
|
36
|
+
raise EmptyVectorError(msg)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def check_same_dimensions(a: list[float], b: list[float]) -> None:
|
|
40
|
+
"""Raise DimensionMismatchError if vectors have different lengths."""
|
|
41
|
+
if len(a) != len(b):
|
|
42
|
+
msg = f"Vector dimensions don't match: {len(a)} vs {len(b)}"
|
|
43
|
+
raise DimensionMismatchError(msg)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def add(a: list[float], b: list[float]) -> list[float]:
|
|
47
|
+
"""Add two vectors component by component.
|
|
48
|
+
|
|
49
|
+
Like walking 3 steps east and then 2 more steps east -- you end up 5 east!
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
a: The first vector.
|
|
53
|
+
b: The second vector.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
A new vector where each component is the sum of the inputs.
|
|
57
|
+
|
|
58
|
+
Raises:
|
|
59
|
+
DimensionMismatchError: If the vectors have different lengths.
|
|
60
|
+
EmptyVectorError: If either vector is empty.
|
|
61
|
+
|
|
62
|
+
"""
|
|
63
|
+
check_not_empty(a)
|
|
64
|
+
check_not_empty(b)
|
|
65
|
+
check_same_dimensions(a, b)
|
|
66
|
+
return [x + y for x, y in zip(a, b, strict=False)]
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def subtract(a: list[float], b: list[float]) -> list[float]:
|
|
70
|
+
"""Subtract vector b from vector a component by component.
|
|
71
|
+
|
|
72
|
+
Find the difference between two positions -- like figuring out
|
|
73
|
+
which direction to walk to get from one spot to another.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
a: The vector to subtract from.
|
|
77
|
+
b: The vector to subtract.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
A new vector where each component is a[i] - b[i].
|
|
81
|
+
|
|
82
|
+
Raises:
|
|
83
|
+
DimensionMismatchError: If the vectors have different lengths.
|
|
84
|
+
EmptyVectorError: If either vector is empty.
|
|
85
|
+
|
|
86
|
+
"""
|
|
87
|
+
check_not_empty(a)
|
|
88
|
+
check_not_empty(b)
|
|
89
|
+
check_same_dimensions(a, b)
|
|
90
|
+
return [x - y for x, y in zip(a, b, strict=False)]
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def scale(v: list[float], factor: float) -> list[float]:
|
|
94
|
+
"""Multiply every component of a vector by a number.
|
|
95
|
+
|
|
96
|
+
Like zooming in on a map -- everything gets bigger by the same amount.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
v: The vector to scale.
|
|
100
|
+
factor: The number to multiply by.
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
A new vector with each component multiplied by the factor.
|
|
104
|
+
|
|
105
|
+
Raises:
|
|
106
|
+
EmptyVectorError: If the vector is empty.
|
|
107
|
+
|
|
108
|
+
"""
|
|
109
|
+
check_not_empty(v)
|
|
110
|
+
return [x * factor for x in v]
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def dot(a: list[float], b: list[float]) -> float:
|
|
114
|
+
"""Compute the dot product of two vectors.
|
|
115
|
+
|
|
116
|
+
Multiply matching components and add them up. The dot product tells you
|
|
117
|
+
how much two vectors "agree" -- if they point the same way, it's big
|
|
118
|
+
and positive; if opposite, it's big and negative; if perpendicular, it's zero.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
a: The first vector.
|
|
122
|
+
b: The second vector.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
The sum of component-wise products.
|
|
126
|
+
|
|
127
|
+
Raises:
|
|
128
|
+
DimensionMismatchError: If the vectors have different lengths.
|
|
129
|
+
EmptyVectorError: If either vector is empty.
|
|
130
|
+
|
|
131
|
+
"""
|
|
132
|
+
check_not_empty(a)
|
|
133
|
+
check_not_empty(b)
|
|
134
|
+
check_same_dimensions(a, b)
|
|
135
|
+
return sum(x * y for x, y in zip(a, b, strict=False))
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def magnitude(v: list[float]) -> float:
|
|
139
|
+
"""Compute the length (magnitude) of a vector.
|
|
140
|
+
|
|
141
|
+
Think of the Pythagorean theorem in any number of dimensions.
|
|
142
|
+
For [3, 4] the magnitude is 5, just like a 3-4-5 right triangle!
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
v: The vector to measure.
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
The Euclidean length of the vector.
|
|
149
|
+
|
|
150
|
+
Raises:
|
|
151
|
+
EmptyVectorError: If the vector is empty.
|
|
152
|
+
|
|
153
|
+
"""
|
|
154
|
+
check_not_empty(v)
|
|
155
|
+
return math.sqrt(sum(x * x for x in v))
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def normalize(v: list[float]) -> list[float]:
|
|
159
|
+
"""Scale a vector to have length 1 (unit vector).
|
|
160
|
+
|
|
161
|
+
Like keeping the direction on a compass but ignoring how far you walked.
|
|
162
|
+
This is called L2 normalization and is essential for cosine similarity.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
v: The vector to normalize.
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
A new unit vector pointing in the same direction.
|
|
169
|
+
|
|
170
|
+
Raises:
|
|
171
|
+
EmptyVectorError: If the vector is empty.
|
|
172
|
+
ValueError: If the vector has zero magnitude (all zeros).
|
|
173
|
+
|
|
174
|
+
"""
|
|
175
|
+
check_not_empty(v)
|
|
176
|
+
mag = magnitude(v)
|
|
177
|
+
if mag == 0.0:
|
|
178
|
+
msg = "Cannot normalize a zero vector"
|
|
179
|
+
raise ValueError(msg)
|
|
180
|
+
return [x / mag for x in v]
|