Search code examples
listfloating-pointlist-comprehensionmultiplicationpython-3.9

Multiple TypeErrors trying to multiply elements of a list inside a list comprehension


I am following a book on deep learning and the book encourages understanding basic math operations behind the more convenient numpy alternatives in order to better understand the underlying principles. I am trying to reconstruct numpy's multiplication (*) operator in native Python 3.9 but I am getting some TypeErrors that I find very confusing. Hopefully somebody can help.

def multiply(a, b):

    """ 1.) float * float
        2.) float * vector
        3.) float * matrix
        4.) vector * vector
        5.) vector * matrix
        6.) matrix * matrix """

    def float_float(float_a, float_b):
        return float_a * float_b
    def float_vector(float_a, vector_b):
        return [float_float(float_a, component_b) for component_b in vector_b]
    def float_matrix(float_a, matrix_b):
        return [float_vector(float_a, vector_b) for vector_b in b]
    def vector_vector(vector_a, vector_b):
        return [a * b for a, b in dict(zip(vec_a, vec_b)).items()]
    def vector_matrix(vector_a, matrix_b):
        return [vector_vector(vector_a, vector_b) for vector_b in matrix_b]
    def matrix_matrix(matrix_a, matrix_b):
        return [vector_vector(a, b) for a, b in dict(zip(matrix_a, matrix_b)).items()]

    def get_type(operand):
        if type(operand) == float:
            return "float"
        elif type(operand) == list:
            if any(isinstance(item, list) for item in operand):
                return "matrix"
            else:
                return "vector"

    types = (get_type(a), get_type(b))
    print(types)

    operations_table = {
        ("float", "float"): float_float(a, b),
        ("float", "vector"): float_vector(a, b),
        ("vector", "float"): float_vector(b, a),
        ("float", "matrix"): float_matrix(a, b),
        ("matrix", "float"): float_matrix(b, a),
        ("vector", "vector"): vector_vector(a, b),
        ("vector", "matrix"): vector_matrix(a, b),
        ("matrix", "vector"): vector_matrix(b, a),
        ("matrix", "matrix"): matrix_matrix(a, b)
    }

    return operations_table[types]

# float
f = 2.0
# vector
v = [0.5, 1.0, 2.0]
# matrix
m = [
    [1.0, 2.0, 3.0],
    [4.0, 5.0, 6.0],
    [7.0, 8.0, 9.0]
]

# TEST
print(multiply(f, f))
print(multiply(f, v))
print(multiply(v, m))

Here is the first TypeError I get when trying to multiple 2 floats:

('float', 'float')
Traceback (most recent call last):
  File "e:\OneDrive\Projects\Deep_Learning\CH05\CH05-000_NativeOps.py", line 62, in <module>
    print(multiply(f, f))
  File "e:\OneDrive\Projects\Deep_Learning\CH05\CH05-000_NativeOps.py", line 38, in multiply
    ("float", "vector"): float_vector(a, b),
  File "e:\OneDrive\Projects\Deep_Learning\CH05\CH05-000_NativeOps.py", line 14, in float_vector
    return [float_float(float_a, component_b) for component_b in vector_b]
TypeError: 'float' object is not iterable

Here is the second TypeError I get when trying to multiply float * vector

('float', 'vector')
Traceback (most recent call last):
  File "e:\OneDrive\Projects\Deep_Learning\CH05\CH05-000_NativeOps.py", line 63, in <module>
    print(multiply(f, v))
  File "e:\OneDrive\Projects\Deep_Learning\CH05\CH05-000_NativeOps.py", line 37, in multiply
    ("float", "float"): float_float(a, b),
  File "e:\OneDrive\Projects\Deep_Learning\CH05\CH05-000_NativeOps.py", line 12, in float_float
    return float_a * float_b
TypeError: can't multiply sequence by non-int of type 'float'

Any help is greatly appreciated because in the example all of the values in the variables f, v, and m are made exclusively floats so I am very surprised at the types of errors that I get. Thank you!!!


Solution

  • The problem was with that operations_table dictionary. It caused all versions of multiplication to run always, regardless of operand type. I changed that for a much shorter solution using eval() and now the code works perfectly.

    def multiply(a, b):
    
        """ 1.) float * float
            2.) float * vector
            3.) float * matrix
            4.) vector * vector
            5.) vector * matrix
            6.) matrix * matrix """
    
        def float_float(float_a, float_b):
            return float_a * float_b
        def float_vector(float_a, vector_b):
            return [float_float(float_a, component_b) for component_b in vector_b]
        def float_matrix(float_a, matrix_b):
            return [float_vector(float_a, vector_b) for vector_b in b]
        def vector_vector(vector_a, vector_b):
            return [a * b for a, b in list(zip(vector_a, vector_b))]
        def vector_matrix(vector_a, matrix_b):
            return [vector_vector(vector_a, vector_b) for vector_b in matrix_b]
        def matrix_matrix(matrix_a, matrix_b):
            return [vector_vector(a, b) for a, b in list(zip(matrix_a, matrix_b))]
    
        def get_type(operand):
            if type(operand) == float:
                return "float"
            elif type(operand) == list:
                if any(isinstance(item, list) for item in operand):
                    return "matrix"
                else:
                    return "vector"
    
        types = (get_type(a), get_type(b))
        print(types)
        result = eval(f"{types[0]}_{types[1]}(a, b)")
        return result