#!/usr/bin/env python
# coding: utf-8
# # Computing Function Inner Products, Norms & Metrics
#
# **Randall Romero Aguilar, PhD**
#
# This demo is based on the original Matlab demo accompanying the Computational Economics and Finance 2001 textbook by Mario Miranda and Paul Fackler.
#
# Original (Matlab) CompEcon file: **demmath02.m**
#
# Running this file requires the Python version of CompEcon. This can be installed with pip by running
#
# !pip install compecon --upgrade
#
# Last updated: 2022-Oct-23
#
# In[1]:
import numpy as np
import matplotlib.pyplot as plt
plt.style.use('seaborn-dark')
import scipy.integrate as integrate
import scipy as sp
# ## Class function
# We now define the class **function**. An object of class **function** operates just as a lambda function, but it supports several function operations: sum, substraction, multiplication, division, power, absolute value, integral, norm, and angle.
#
# This example illustrates how it is possible to overwrite the methods of the function class.
# In[2]:
class function:
def __init__(self, func):
self.f = func
def __call__(self, *args):
return self.f(*args)
def __add__(self, other):
return function(lambda *args: self.f(*args) + other.f(*args))
def __sub__(self, other):
return function(lambda *args: self.f(*args) - other.f(*args))
def __mul__(self, other):
return function(lambda *args: self.f(*args) * other.f(*args))
def __pow__(self, n):
return function(lambda *args: self.f(*args) ** n)
def __truediv__(self, other):
return function(lambda *args: self.f(*args) / other.f(*args))
def integral(self, l, h):
return integrate.quad(self.f, l, h)[0]
def abs(self):
return function(lambda *args: np.abs(self.f(*args)))
def norm(self, l, h, p=2):
return ((self.abs()) ** p).integral(l, h) ** (1/p)
def angle(self, other, l, h):
fg = (self * other).integral(l, u)
ff = (self**2).integral(l, u)
gg = (other**2).integral(l, u)
return np.arccos(fg*np.sqrt(ff*gg))*180/np.pi
# ## Compute inner product and angle
#
# Define the functions $f(x) = 2x^2-1$ and $g(x)= 4x^3-3x$, both over the domain $[-1,1]$. Compute their inner product and angle.
# In[3]:
l, u = -1, 1
f = function(lambda x: 2 * x**2 - 1)
g = function(lambda x: 4 * x**3 - 3*x)
fg = (f*g).integral(l, u)
ff = (f**2).integral(l, u)
gg = (g**2).integral(l, u)
angle = f.angle(g, l, u)
print(f'∫(f*g)(x)dx = {fg:.2f}')
print(f'∫(f^2)(x)dx = {ff:.2f}')
print(f'∫(g^2)(x)dx = {gg:.2f}')
print(f'Angle in degrees = {angle:.0f}°')
# ## Compute Function Norm
#
# Now compute the norm of function $f(x) = x^2 - 1$ over the domain $[0, 2]$.
# In[4]:
l, u = 0, 2
f = function(lambda x: x ** 2 - 1)
print(f'∥f∥₁ = {f.norm(l, u, 1):.3f}')
print(f'∥f∥₂ = {f.norm(l, u, 2):.3f}')
# ## Compute function metrics
# In[5]:
l, u = 0, 1
f = function(lambda x: 5 + 5*x**2)
g = function(lambda x: 4 + 10*x - 5*x**2)
print(f'∥f-g∥₁ = {(f-g).norm(l, u, 1):.3f}')
print(f'∥f-g∥₂ = {(f-g).norm(l, u, 2):.3f}')
# ### Illustrate Function metrics
# In[6]:
x = np.linspace(l,u,200)
fig, ax = plt.subplots()
ax.set(xlabel='x',
ylabel='|f(x)-g(x)|',
xlim=[0, 1],
ylim=[0, 1.6])
plt.plot(x, (f-g).abs()(x))
# ### Demonstrate Pythagorean Theorem
#
# Again, define the functions $f(x) = 2x^2-1$ and $g(x)= 4x^3-3x$, both over the domain $[-1,1]$.
# In[7]:
l,u = -1, 1
f = function(lambda x: 2 * x**2 - 1)
g = function(lambda x: 4 * x**3 - 3*x)
ifsq = (f**2).integral(l,u)
igsq = (g**2).integral(l,u)
ifplusgsq = ((f+g)**2).integral(l,u)
# In[8]:
print(f'∫f²(x)dx = {ifsq:.4f}')
print(f'∫g²(x)dx = {igsq:.4f}')
print(f'∫(f+g)²(x)dx = {ifplusgsq:.4f}')