Source code for HMB.VectorsHelper

import numpy as np


[docs] class VectorsHelper(object): r''' Common vector operations built on NumPy. Methods: - Length(vector): Euclidean (L2) norm of a vector. - DotProduct(vector1, vector2): Standard dot product. - CrossProduct(vector1, vector2): Cross product for 3D vectors. - Distance(vector1, vector2): Euclidean distance between two vectors. - Angle(vector1, vector2, mode="rad"): Angle between vectors (radians or degrees). - ChangeBasis(v, *args): Project vector v onto provided basis vectors. '''
[docs] def Length(self, vector): r''' Compute the Euclidean (L2) length/norm of a vector. .. math:: \|v\|_2 = \sqrt{\sum_i v_i^2} Parameters: vector (array-like): Input vector (list, tuple or NumPy array). Returns: float or numpy.ndarray: L2 norm. Works for 1D vectors or arrays. ''' result = np.sqrt(np.sum(np.power(vector, 2))) return result
[docs] def DotProduct(self, vector1, vector2): r''' Compute the dot product between two vectors. .. math:: \langle a, b \rangle = \sum_i a_i b_i Parameters: vector1, vector2 (array-like): Input vectors (same shape required for dot). Returns: scalar or numpy.ndarray: Dot product result. ''' # <vector1, vector2> = vector1.T * vector2 result = np.dot(vector1, vector2) return result
[docs] def CrossProduct(self, vector1, vector2): r''' Compute the cross product (only meaningful for 3-dimensional vectors). .. math:: a \times b = [a_2 b_3 - a_3 b_2,\; a_3 b_1 - a_1 b_3,\; a_1 b_2 - a_2 b_1] Parameters: vector1, vector2 (array-like): 3-element vectors. Returns: numpy.ndarray: Cross product vector. ''' result = np.cross(vector1, vector2) return result
[docs] def Distance(self, vector1, vector2): r''' Compute Euclidean distance between two vectors. .. math:: d(a, b) = \|a - b\|_2 = \sqrt{\sum_i (a_i - b_i)^2} Parameters: vector1, vector2 (array-like): Input vectors. Returns: float or ndarray: Euclidean distance. ''' diff = np.subtract(vector1, vector2) result = self.Length(diff) return result
[docs] def Angle(self, vector1, vector2, mode="rad"): r''' Compute the angle between two vectors using the arccosine of the normalized dot product. .. math:: \theta(a,b) = \arccos\left(\frac{\langle a, b \rangle}{\|a\|_2 \; \|b\|_2}\right) Parameters: vector1, vector2 (array-like): Input vectors. mode (str): "rad" for radians (default) or "deg" for degrees. Returns: float or numpy.ndarray: Angle in radians or degrees depending on mode. Notes: - No clipping is performed on the cosine input; numerical round-off may cause values slightly outside [-1, 1] which can produce NaNs. Consumers may want to clip the argument. ''' num = self.DotProduct(vector1, vector2) den = self.Length(vector1) * self.Length(vector2) # If denominator is zero, angle is undefined -> return NaN to match tests. if (den == 0): return float(np.nan) # Compute cosine value and clip to valid range to avoid NaNs. cosVal = num / den cosVal = np.clip(cosVal, -1.0, 1.0) result = np.arccos(cosVal) if (mode == "deg"): result = (result / np.pi) * 180.0 return result
[docs] def ChangeBasis(self, v, *args): r''' Project vector `v` onto each of the provided basis vectors. Parameters: v (array-like): Vector to project. *args (array-like): Basis vectors to project onto. Returns: list: List of projection coefficients (one per provided basis vector). ''' L = [] for arg in args: x = np.dot(v, arg) denom = np.sum(np.power(arg, 2)) # If denom is zero, return NaN (non-finite) to match tests without triggering a RuntimeWarning. if (denom == 0): coeff = float(np.nan) else: coeff = x / denom L.append(coeff) return L
[docs] def ProjectVector(self, v, basis): r''' Project vector `v` onto the provided basis vectors. .. math:: c_i = \frac{\langle v, b_i \rangle}{\langle b_i, b_i \rangle} \quad\text{for each basis vector } b_i Parameters: v (array-like): Vector to project. basis (list of array-like): Basis vectors to project onto. Returns: list: List of projection coefficients (one per provided basis vector). ''' v = np.asarray(v, dtype=float) coeffs = [] for b in basis: b = np.asarray(b, dtype=float) denom = np.sum(np.power(b, 2)) num = np.dot(v, b) coeffs.append( 0.0 if (denom == 0) else (num / denom).item() if (np.isscalar(num)) else float(num / denom) ) return coeffs
[docs] def CosineSimilarity(self, vector1, vector2): r''' Compute the cosine similarity between two vectors. .. math:: \text{cosine}(a, b) = \frac{\langle a, b\rangle}{\|a\|_2 \; \|b\|_2} Parameters: vector1 (array-like): First input vector. vector2 (array-like): Second input vector. Returns: float: Cosine similarity value in the range [-1, 1]. ''' v1 = np.asarray(vector1, dtype=float) v2 = np.asarray(vector2, dtype=float) n1 = self.Length(v1) n2 = self.Length(v2) if (n1 == 0 or n2 == 0): return 0.0 return float(np.dot(v1, v2) / (n1 * n2))
[docs] def NormalizeVector(self, vector): r''' Normalize the input vector to have unit length. Parameters: vector (array-like): Input vector to normalize. Returns: numpy.ndarray: Normalized vector with unit length. If input is zero vector, returns zero vector of the same shape. ''' v = np.asarray(vector, dtype=float) n = self.Length(v) if (n == 0): # Return the same zero vector instead of raising to satisfy tests return np.zeros_like(v) return v / n
if __name__ == "__main__": # SafeCall helper used to call methods and gracefully report failures. def SafeCall(name, fn, *args, **kwargs): try: res = fn(*args, **kwargs) print(f"{name} ->", res) print("-" * 40) return res except Exception as e: print(f"{name} raised {type(e).__name__}:", e) print("-" * 40) return None vh = VectorsHelper() # Simple vectors a = np.array([3.0, 4.0]) b = np.array([1.0, 1.0]) SafeCall("Length(a)", vh.Length, a) SafeCall("DotProduct(a,b)", vh.DotProduct, a, b) SafeCall( "CrossProduct (2D expects 3D) -> will promote or error depending on numpy", vh.CrossProduct, np.array([1, 0, 0]), np.array([0, 1, 0]) ) SafeCall("Distance(a,b)", vh.Distance, a, b) SafeCall("Angle (rad)", vh.Angle, a, b, "rad") SafeCall("Angle (deg)", vh.Angle, a, b, "deg") SafeCall("ChangeBasis", vh.ChangeBasis, a, np.array([2.0, 1.0]), np.array([-2.0, 4.0])) print("VectorsHelper demo completed.")