Untitled

mail@pastecode.io avatar
unknown
python
a month ago
6.3 kB
3
Indexable
Never
from common.utils import randomize_int
from typing import List


class Vector:
    def __init__(self, content):
        """
        self.dim is an integer indicating the dimension of the vector.  e.g.
            a 2-tuple has self.dim=2, a 3-tuple has self.dim=3
        self.content is a tuple containing dim-many numbers indicating
            the components of the vector
        """
        self.dim = len(content)
        self.content = tuple(content)

    def __repr__(self):
        """creates and returns a string giving a human-readable representation
        of the vector.  e.g., 'Vector[4]((3, 5, 2, -1))'
        """
        return f"Vector[{self.dim}]({self.content})"

    def __eq__(self, other):
        """Test whether the two objects are type compatible, (other is type Vector),
        and whether the content is equal using ==.
        """
        if not isinstance(other, Vector):
            return False
        return other.content == self.content

    def __getitem__(self, items):
        """Allow a Vector instance to be accessed like an array."""
        return self.content[items]

    @staticmethod
    def tabulate(dim, f):
        """Create and return a new Vector of dimension dim.
        Each of the components of the Vector is determined by a call
        to the given function with the index as argument.
        For example the k'th element is initialized with a call to f(k).
        E.g., Vector.tabulate(3, f) is equivalent to
              Vector(tuple(f(0), f(1), f(2)))
        """
        return Vector([f(i) for i in range(dim)])

    @staticmethod
    def random(dim, randomizer=randomize_int(-100, 100)):
        """Create and return a Vector having randomly selected
        elements for its components. The randomizer argument
        specifies a zero-ary argument which will return a
        random number.  If the call-site would like to fill
        the 5d Vector with random numbers between 0.5 and 1.5,
        use Vector.random(5,randomize_float(0.5, 1.5))
        """
        assert dim >= 1
        return Vector([randomizer() for i in range(dim)])

    @staticmethod
    def zero(dim):
        """Create a return a Vector of the given dimension consisting entirely
        of 0 (the integer 0)"""
        return Vector([0 for i in range(dim)])

    def __add__(self, other):
        """create and return a new Vector which is the sum (addition) of the
        two given vectors, self and other. raise an error if the dimensions
        are not compatible to add."""
        assert isinstance(other, Vector)
        assert self.dim == other.dim, f"cannot add Vectors of different dimension: {self}, {other}"
        return Vector([self[i] + other[i] for i in range(self.dim)])

    def scale(self, scalar):
        """create and return a new Vector, the same dimension as self, but with
        each component having been multiplied by the given scalar.
        If scalar fails to be a number, then a RuntimeError is raised."""
        import numbers
        if not isinstance(scalar, numbers.Number):
            raise RuntimeError(f"expecting number, got {type(scalar)}: {scalar}")
        return Vector([self.content[i] * scalar for i in range(self.dim)])

    def __mul__(self, other):
        """Override the * operator to perform scalar multiplication.
        Note that we support Vector * scalar, but not scalar * Vector,
        as we do not attempt to override the __mul__ method on numbers."""
        return Vector.scale(self, other)

    def __sub__(self, other):
        """Return the subtraction (difference) of the given Vectors: self - other.
        This method also overrides the - operator to perform the subtraction."""
        return Vector([self[i] - other[i] for i in range(self.dim)])

    def norm(self):
        """Return the Euclidian length of the Vector"""
        import math
        sum_squared = sum(i ** 2 for i in self.content)
        return math.sqrt(sum_squared)

    def normalize(self):
        """If the norm of the vector is 0, then return None,
        otherwise return a unit vector parallel to the vector.
        I.e., scale the vector by 1.0/the-norm-of-the-vector"""
        if self.norm() == 0:
            return None
        return Vector([self[i] / self.norm() for i in range(self.dim)])

    def distance(self, other):
        """Return the Euclidian distance between the two vectors."""
        assert other.dim == self.dim
        difference = self - other
        return difference.norm()

    def inner_product(self, other):
        """Return a number indicating the dot product of the two vectors,
        i.e., the sum of the products of the corresponding components.
        Raise an error of the Vector dimensions are not compatible"""
        assert other.dim == self.dim
        return sum([self[i] * other[i] for i in range(self.dim)])

    def angle(self, other):
        """Return the angle (in radians) between the two Vectors.
        This angle is the same if measured from self to other or from
        other to self.  I.e., the angle is positive,
        between 0 and pi, inclusive."""
        assert other.dim == self.dim
        import math
        # Warning!, this function will compute math.acos(a/b)
        #  you must figure out the values of a and b.
        #  Normally the value of a/b will be between -1 and 1 (inclusive).
        #  However, it is possible with round-off error that the value
        #  is slightly outside this range.  If you attempt to compute acos(1.00000000001)
        #  then ValueError: math domain error
        #  will be raised.   A correct implementation of angle() must protect
        #  against this.
        cosine = self.inner_product(other) / (self.norm() * other.norm())
        if cosine > 1:
            return math.acos(1)
        elif cosine < -1:
            return math.acos(-1)
        return math.acos(cosine)


def is_orthogonal(vectors: List[Vector]) -> bool:
    """Is the given list of Vectors pairwise orthogonal according to its
    inner product"""
    epsilon = 0.0001
    raise NotImplementedError('to be done')
Leave a Comment