{ "cells": [ { "cell_type": "markdown", "id": "1d53198b-0f55-46c1-ba42-65708af26bc1", "metadata": {}, "source": [ "Operator overloading is illustrated." ] }, { "cell_type": "markdown", "id": "105ae6bc-4509-4d8f-93b6-b92e6c09d77f", "metadata": {}, "source": [ "# 1. Counting Floating-Point Operations" ] }, { "cell_type": "markdown", "id": "c3fe3e6c-26fb-458b-a639-6e8919612768", "metadata": {}, "source": [ "With every floating-point number we compute, we want to record the number of operations executed. The result of any floating-point computation thus consists of two parts:\n", "\n", "1. The floating-point number, of type `float`.\n", "\n", "2. The number of operations that let to this result, of type `int`.\n", "\n", "These are thus the two data attributes of any object that is an instance of `FlopFloat`. " ] }, { "cell_type": "markdown", "id": "bc2f0f97-cfbe-4a08-82ab-1d3e55755e9d", "metadata": {}, "source": [ "A *flop* is short for floating point operation." ] }, { "cell_type": "code", "execution_count": 1, "id": "31ba6cd0-5e19-45c6-a573-b2a5bea3baaa", "metadata": {}, "outputs": [], "source": [ "import flopfloats" ] }, { "cell_type": "code", "execution_count": 2, "id": "0741c1dc-107e-4f61-9530-2807a5012f15", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on module flopfloats:\n", "\n", "NAME\n", " flopfloats\n", "\n", "DESCRIPTION\n", " To analyze the cost of an algorithm,\n", " we count the number of operations.\n", "\n", "CLASSES\n", " builtins.object\n", " FlopFloat\n", " \n", " class FlopFloat(builtins.object)\n", " | FlopFloat(f=0.0, n=0)\n", " | \n", " | An object of the class FlopFloat records\n", " | the number of floating-point operations\n", " | executed to compute the float.\n", " | \n", " | Methods defined here:\n", " | \n", " | __add__(self, other)\n", " | Returns the result of the addition,\n", " | the other may be an ordinary number.\n", " | \n", " | __div__(self, other)\n", " | Returns the result of the division.\n", " | \n", " | __eq__(self, other)\n", " | Two flopfloats are equal if both\n", " | their float and flops match.\n", " | \n", " | __iadd__(self, other)\n", " | Defines the inplace addition, x += y.\n", " | \n", " | __init__(self, f=0.0, n=0)\n", " | Construct a FlopFloat from a number f\n", " | and an operational cost n, by default 0.\"\n", " | \n", " | __isub__(self, other)\n", " | Defines the inplace subtraction, x -= y.\n", " | \n", " | __mul__(self, other)\n", " | Returns the result of the multiplication,\n", " | the other may b an ordinary number.\n", " | \n", " | __neg__(self)\n", " | Returns -x.\n", " | \n", " | __pow__(self, pwr)\n", " | Returns the result of a FlopFloat\n", " | taken to the power pwr, asuming no\n", " | binary expansion of pwr takes place...\n", " | \n", " | __radd__(self, other)\n", " | Addition when operand is not a FlopFloat,\n", " | as in 2.7 + x or 3 + x (reflected operand).\n", " | \n", " | __repr__(self)\n", " | Returns the default string representation.\n", " | \n", " | __str__(self, form='%.4e')\n", " | Uses the formatting string in the %\n", " | to return astring representation.\n", " | \n", " | __sub__(self, other)\n", " | Returns the result of the subtraction,\n", " | where the other is optional, to allow for -x.\n", " | \n", " | ----------------------------------------------------------------------\n", " | Data descriptors defined here:\n", " | \n", " | __dict__\n", " | dictionary for instance variables (if defined)\n", " | \n", " | __weakref__\n", " | list of weak references to the object (if defined)\n", " | \n", " | ----------------------------------------------------------------------\n", " | Data and other attributes defined here:\n", " | \n", " | __hash__ = None\n", "\n", "FILE\n", " c:\\users\\jan\\courses\\mcs260\\summer23\\lec27\\flopfloats.py\n", "\n", "\n" ] } ], "source": [ "help(flopfloats)" ] }, { "cell_type": "markdown", "id": "f98940a5-3e24-4180-8390-dcaebc62c6b7", "metadata": {}, "source": [ "Observe the `other` next to the `self` in the operations. Like `self`, `other` is a reserved word and is used to refer to the second operand in the binary operations." ] }, { "cell_type": "markdown", "id": "ad6fea3b-954b-4a53-ace1-8833eae48653", "metadata": {}, "source": [ "From the module `flopfloats` we import the class `FlopFloat`, in order to work with flop floats." ] }, { "cell_type": "code", "execution_count": 3, "id": "1b6e5d41-46ee-4f77-8ce2-6f083336150f", "metadata": {}, "outputs": [], "source": [ "from flopfloats import FlopFloat" ] }, { "cell_type": "code", "execution_count": 4, "id": "53607920-3283-4cf3-9d65-04d19fa706a6", "metadata": {}, "outputs": [], "source": [ "x = FlopFloat(3.14159625)" ] }, { "cell_type": "markdown", "id": "b63672d7-a3b0-463c-8787-9a9676c7a918", "metadata": {}, "source": [ "Using the name of the class `FlopFloat` as a function as in the example above calls the constructor, defined by the method `__init__()`." ] }, { "cell_type": "code", "execution_count": 5, "id": "d5770f7b-3c5b-4065-8317-2d6b1ea7333b", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3.1416e+00" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x" ] }, { "cell_type": "code", "execution_count": 6, "id": "7df02ab6-b84f-4636-9493-581c2393f656", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'3.1416e+00'" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "str(x)" ] }, { "cell_type": "code", "execution_count": 7, "id": "bcd6b0fc-6718-427a-b4ff-270ab41958fc", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3.1416e+00\n" ] } ], "source": [ "print(x)" ] }, { "cell_type": "markdown", "id": "b1b1e1cd-955c-458d-8034-50d6a18073c1", "metadata": {}, "source": [ "What shows when we type `x` if `x` is an instance of the class `FlopFloat` is defined by the method `__repr__()` which defines the representation of the object, which corresponds in this case to the string representation with default format string `.4e` (scientific notation with four decimals after the dot)." ] }, { "cell_type": "code", "execution_count": 8, "id": "4f5f45c2-c74e-41d7-b75b-b0f481035603", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'3.14159625'" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x.__str__('%.8f')" ] }, { "cell_type": "markdown", "id": "e46e41f4-dc40-44a5-975d-4c16f9fd7c4b", "metadata": {}, "source": [ "The data attributes are `float` and `flops`." ] }, { "cell_type": "code", "execution_count": 9, "id": "40b94c26-6492-4e34-a4cb-7bb321fa9c6d", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3.14159625" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x.float" ] }, { "cell_type": "code", "execution_count": 10, "id": "9ebd9372-f946-498e-aede-94b6153f3748", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x.flops" ] }, { "cell_type": "code", "execution_count": 11, "id": "b090aebc-280a-4e68-9807-60673232eea2", "metadata": {}, "outputs": [], "source": [ "z = x*x" ] }, { "cell_type": "code", "execution_count": 12, "id": "af5d259a-6bfc-4164-a6e2-5af4cabed287", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "z.flops" ] }, { "cell_type": "markdown", "id": "41ff6ff3-4ae5-4f07-974e-173d15c3285a", "metadata": {}, "source": [ "The value of `z.flops` is 1 by the definition of the `__mul__()` for the multiplication of flop floats." ] }, { "cell_type": "code", "execution_count": 13, "id": "fe6bd94e-e03d-4b62-903c-a6400209528f", "metadata": {}, "outputs": [], "source": [ "y = z + 2" ] }, { "cell_type": "code", "execution_count": 14, "id": "c218f53b-2161-4f64-8b26-d42ad27d3484", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1.1870e+01" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y" ] }, { "cell_type": "code", "execution_count": 15, "id": "7b759449-a367-48aa-9713-3a371ec0ca35", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y.flops" ] }, { "cell_type": "markdown", "id": "29c5b80a-4f20-4d57-9fa7-86cd2b92482b", "metadata": {}, "source": [ "The `y = z + 2` was executed as `y = z.__add__(2)`." ] }, { "cell_type": "markdown", "id": "213d5dfb-0a1a-4d86-b8df-0119474990fe", "metadata": {}, "source": [ "Observe that the computation of `y` involved 2 floating-point operations, reflected in the value of `y.flops`." ] }, { "cell_type": "code", "execution_count": 16, "id": "f9526f10-200d-4419-8826-6404bc9eb700", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y == y.float" ] }, { "cell_type": "markdown", "id": "9b89dfd5-10a7-43d5-8e2e-37c1c51ce3c5", "metadata": {}, "source": [ "The `y` is equal to its floating-point value is because of the definition of the `__eq__()` in the class. Even though the `y.float` is not an instance of the class `FlopFloat`, as illustrated below, because its value is equal of the of `y`, the equality returns `True`." ] }, { "cell_type": "code", "execution_count": 17, "id": "bddca60f-323d-4a59-980d-c0bfdbfd58fe", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isinstance(y, FlopFloat)" ] }, { "cell_type": "code", "execution_count": 18, "id": "26698ab8-6c8d-47f7-986b-b8f96f69ffce", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "isinstance(y.float, FlopFloat)" ] }, { "cell_type": "code", "execution_count": 19, "id": "e24bfcd7-6388-453e-a191-d5805ff785cf", "metadata": {}, "outputs": [], "source": [ "y = 3 + y" ] }, { "cell_type": "code", "execution_count": 20, "id": "f7686610-e1ee-4c6e-acdb-45f826d166e6", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y.flops" ] }, { "cell_type": "markdown", "id": "e20fde18-d97b-4ffc-b167-f3abbd3499e6", "metadata": {}, "source": [ "The `y = 3 + y` is defined by the reflective addition, defined by `__radd__()`." ] }, { "cell_type": "markdown", "id": "ee8c6194-beca-43a9-b69a-47fb06607729", "metadata": {}, "source": [ "As an application, let us commpute the sum of any number of random floats, normally distributed." ] }, { "cell_type": "code", "execution_count": 21, "id": "1f52c15d-8f62-4268-9cae-368de089cdf6", "metadata": {}, "outputs": [], "source": [ "from random import gauss" ] }, { "cell_type": "code", "execution_count": 22, "id": "abe2b92f-bcfd-40a2-a1f5-cad11234fefa", "metadata": {}, "outputs": [], "source": [ "n = 10" ] }, { "cell_type": "code", "execution_count": 23, "id": "ed7f4b2e-3574-49e9-8ffe-70c90c8177c7", "metadata": {}, "outputs": [], "source": [ "randsum = FlopFloat()" ] }, { "cell_type": "code", "execution_count": 24, "id": "7dff00a8-55b4-4c3d-b84a-5d182369252b", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.0000e+00" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "randsum" ] }, { "cell_type": "code", "execution_count": 25, "id": "74548ba5-f28f-4af3-886a-90c73a41f929", "metadata": {}, "outputs": [], "source": [ "for i in range(n):\n", " randsum = randsum + gauss(0, 1)" ] }, { "cell_type": "code", "execution_count": 26, "id": "c438529c-09e7-4a80-b449-e439b6d715f5", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "randsum.flops" ] }, { "cell_type": "markdown", "id": "06c6776f-7663-4029-975d-d861f1b9f2a0", "metadata": {}, "source": [ "Although the sum in itself is not complicated, accumulating with an object of `FlopFloat` the number of floating-point operations are automatically computed." ] }, { "cell_type": "markdown", "id": "6b261ac8-3f77-4b5a-87cf-6ad19e1da454", "metadata": {}, "source": [ "# 2. Quaternions" ] }, { "cell_type": "markdown", "id": "5d24f729-c741-49fb-a64d-a7e91d0bfead", "metadata": {}, "source": [ "Python has the type `complex` to work with complex numbers." ] }, { "cell_type": "code", "execution_count": 27, "id": "5dc5e3e0-74f6-485c-abeb-be5b9a006202", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on class complex in module builtins:\n", "\n", "class complex(object)\n", " | complex(real=0, imag=0)\n", " | \n", " | Create a complex number from a real part and an optional imaginary part.\n", " | \n", " | This is equivalent to (real + imag*1j) where imag defaults to 0.\n", " | \n", " | Methods defined here:\n", " | \n", " | __abs__(self, /)\n", " | abs(self)\n", " | \n", " | __add__(self, value, /)\n", " | Return self+value.\n", " | \n", " | __bool__(self, /)\n", " | True if self else False\n", " | \n", " | __eq__(self, value, /)\n", " | Return self==value.\n", " | \n", " | __format__(self, format_spec, /)\n", " | Convert to a string according to format_spec.\n", " | \n", " | __ge__(self, value, /)\n", " | Return self>=value.\n", " | \n", " | __getattribute__(self, name, /)\n", " | Return getattr(self, name).\n", " | \n", " | __getnewargs__(self, /)\n", " | \n", " | __gt__(self, value, /)\n", " | Return self>value.\n", " | \n", " | __hash__(self, /)\n", " | Return hash(self).\n", " | \n", " | __le__(self, value, /)\n", " | Return self<=value.\n", " | \n", " | __lt__(self, value, /)\n", " | Return self