# Learning Objectives
- concepts of function, domain and range
- use of notations such as $f(x)=x^2+5$,
    $$f:x\mapsto x^2+5, f^{-1}(x), fg(x) \text{ and } f^2(x)$$
- finding inverse functions and composite functions
- conditions for the existence of inverse functions and composite functions
- domain restriction to obtain an inverse function
- relationship between a function and its inverse

Exclude the use of the relation $(fg)^{-1}=g^{-1}f^{-1}$, and restriction of domain to obtain a composite function.

# 0. Motivation
As we go on and about in our daily lives, our human brain naturally make connections between many occurences that we encounter daily. For example, when we fill a water bottle from a water tap. We know that the time taken to fill the water tap depends on the rate of flow of water flowing into the bottle. In another example, when we buy an item, the change we received depends on the amount of money that you pay. In both these examples, we see that some quantities is related to another quantity by some certain rule, albeit the rule need not be known to us. 

In mathematics, we formalize this idea of connection between objects (one such common object are numerical values) via the notion of **function**. The idea is pretty simple and yet very powerful in capturing these connections. With functions, one can investigate the effect of changing one object on the other object that the former object is related to. One such application of the the investigation is  that students would have encounter in their previous study of mathematics is on the notion of gradients at specific points on the graphs. Surprisingly, functions can also be used to investigate our understanding of numbers. 

In A-Levels, the functions considered are only between one real-valued quantity and another real-valued quantity.

# 1. Functions – Rule, Domain and Range
Let $X$ and $Y$ be sets. A **function** $f$ from $X$ to $Y$, denoted as $f:X\rightarrow Y$, is a mapping (think of this as a way to associate or relate or pair) of *each element* of the set $X$ to exactly one of the elements of the set $Y$. To denote elements $x$ and $y$ are paired, we write $x\mapsto y$. Furthermore, if the element $x$ in $X$ is mapped to the element $y$ in $Y$ by the function $f$, we write $f(x)=y$, and we say that $y$ is the **image of $x$ under $f$**. Besides a way of association, with this definition, we can think of a function $f$ of some kind of machine that accepts an input $x\in X$ and gives an output $y\in Y$. 

>Note:
>- *each element* means that every element in $X$ has to be associated to some element in $Y$. However, not every element in $Y$ has to be associated with.
>- *exactly one* means that a function is single-valued. It will not give more than one output for the same input.

### Example 1
Let $X=\{ 1, 2, 3, 4\}$ and $Y=\{ 3,5,7,9\}$. 
- **(a)** Let $f$ be a way of relating the elements $1,2,3,4$ in $X$ with the elements in $5,3,7,9$ in $Y$ respectively. We write:
    - $1 \mapsto 5$, $2 \mapsto 3$, $3 \mapsto 7$, $4 \mapsto 9$
    - $f$ is a function because it fulfills the definition above. 
- **(b)** Let $g$ be a way of relating the elements $1,2,3,4$ in $X$ with the elements in $3,7,9,7$ in $Y$ respectively. We write:
    - $1 \mapsto 3$, $2 \mapsto 7$, $3 \mapsto 9$, $4 \mapsto 7$
    - $g$ is also a function because it fulfills the definition above. 
- **(c)** Let $h$ be a way of relating the elements $1,2,3,3$ in $X$ with the elements in $3,7,3,9$ in $Y$ respectively. We write:
    - $1 \mapsto 3$, $2 \mapsto 7$, 3 is mapped twice to different elements in $Y$, $3 \mapsto 3$, $3 \mapsto 9$, and 4 isn't mapped to anything.
    - $h$ is **NOT** a function because it violates 2 conditions in the definition above (See Note). 

For a function $f:X\rightarrow Y$,:
- the set $X$ is called the **domain of $f$**, denoted by $D_{f}$. 
- the set $Y$ is called the **codomain of $f$**, and
- the *set of elements in the set $Y$ with at least one element $x$ in the set $X$ associated to it* is called the **range of $f$**, usually denoted by $R_{f}$. In other words, The range of $f$ is defined as the set of all the possible images of the elements of $X$ under the function $f$ i.e., $R_f =\{f(x) : x\in X\}$.
- It is quite obvious from the definition that the range $R_f$ is always a subset of the codomain of $f$.

### Example 2
Referring back to Example 1
- **(a)** $D_f=\{1,2,3,4\}$, $R_f=\{3,5,7,9\}$ and $R_f$ is the same as the codomain of $f$.
- **(b)** $D_g=\{1,2,3,4\}$, $R_g=\{3,7,9\}$ and $R_g$ is a proper subset of the codomain of $f$.
- **(c)** The terms wouldn't make sense as the relation $h$ is not even a function. In our definition, the concepts of domain, codomain and range only makes sense if the relation is a function.

In a function $f:X\rightarrow Y$, the precise description of the way to associate element $x\in X$ with some element $y\in Y$ is termed the **rule of $f$**. We have seen earlier that if $y$ and $x$ is associated as such, we write $f(x)=y$. Therefore, we can also take that $f(x)$ as the rule of the function $f$. And in A-Level, $f(x)$ is often an expression in the variable $x$. Also, if use the machine anology, we can think of domain of $f$ as the collection of all possible inputs of the function.

> To define a function $f$, it is imperative to supply:
> 1. the *rule* of $f$, and 
> 2. the *domain* of $f$. 

### Example 3
Let $f$ be a function that maps the element $x\in X=\{1,2,3,4\}$ with the square of itself $x^2$. Then, we have $x\mapsto x^2$, or $f(x)$ which is $x^2$, to be the rule of $f$.

With Python, given the domain and rule of $f$, we can use *list comprehension* to get the range of $f$! This is very nice, because this way of doing matches with how we mathematically define $R_f$.


```python
# We import the following so that we can display the results prettily
from IPython.display import Math,display,Latex

# Here's the "domain" (set of input values). Technically, sets doesn't have an order.
# In this case we're actually using a list for the iteration.
X = [1,2,3,4]

# Defining the function f
def f(x):
    # We return the elements that is associated with x under the
    # desired rule
    return x**2

# Collecting the elements that elements in the domain gets mapped to
# and put them in a list. 
range_of_f = [f(x) for x in X]

display(Math(f'R_f=\{{ {set(range_of_f)} \}}'))
```


$\displaystyle R_f=\{ {16, 1, 4, 9} \}$


> Note, in A-Level, the domain and codomain are subsets of $\mathbb{R}$.

## 1.1 Interval Notation
When dealing with domains and ranges of functions, in A-Level, we don't usually deal with finite number of values, e.g. $x\geq 3$ are ALL $x$-values greater than 3. As such, representation by listing out in terms of elements in sets are often impossible. Consequently, the following shorthand **interval notation** together with their meaning is listed below. We will use them often and thus, familiarity with these notations is going to be assumed for subsequent parts.

Let $a$ and $b$ be real numbers such that $a\leq b$, we have:
- $[a,b] = \{ x\in \mathbb{R}: a\leq x\leq b\}$
- $[a,b) = \{ x\in \mathbb{R}: a\leq x< b\}$
- $(a,b] = \{ x\in \mathbb{R}: a< x\leq b\}$
- $(a,b) = \{ x\in \mathbb{R}: a< x< b\}$ 
- $(a,\infty) = \{ x\in \mathbb{R}: a< x\}$. Note that we don't write $a<x<\infty$.
- $(-\infty,a) = \{ x\in \mathbb{R}: x<a\}$ 
- $[a,\infty) = \{ x\in \mathbb{R}: a\leq  x\}$. 
- $(-\infty,a] = \{ x\in \mathbb{R}: x\leq a\}$ 
- $(-\infty,\infty) = \mathbb{R}$ is special. 

Square brackets `[]` are used to denote that in the interval the end points are *included* while parentheses `()` are used when the end points of the interval are *excluded*.

## Example 4
Students at this level often use the set notation haphazardly. To illustrate this, consider $\{1,8\}$ and $[1,8]$. They look similar but has completely different meaning.
- $\{1,8\}$ is a set containing **two** elements, namely $1$ and $8$, while,
- $[1,8]= \{ x\in \mathbb{R}: 1\leq x\leq 8\}$ has infinitely many elements. 

# 2. Representations of Relations
Let $X$ and $Y$ be sets. Besides the representation shown in Example 1, the relation between the elements $x\in X$ and $y\in Y$ can be illustrated by any of the following.

## 2.1 Tabular Representation
In the **tabular representation**, we have two columns in the table. One column contains the values $x$ in the domain and the other, the value $y$ in the codomain such that $f(x)=y$.

Using the relations in Example 1, we have the following tables

<center>

| $x$ | $y$ |
|-|-|
|1|5
|2|3
|3|7
|4|9

<br>

| $x$ | $y$ |
|-|-|
|1|3
|2|7
|3|9
|4|7

<br>

| $x$ | $y$ |
|-|-|
|1|3
|2|7
|3|3,9

</center>

In this tabular form, we can detect whether a relation is not a function by looking at the values in the column.
- if **column containing values from the domain has missing values from the domain**, the relation cannot be a function
- if **a cell in the column containing values from the codomain has more than 1 values in it**, the relation cannot be a function

## 2.2 Venn Diagram Representation
When the number of elements in the sets are small, besides tabular form, we can also use the **venn diagram reprentation**. In this representation, the sets are drawn as two different bubbles with the elements inside each set drawn inside the bubbles as well. The association between the elements from the two sets are shown by drawing an arrow between those elements, i.e. if $x\mapsto y$ or $y=f(x)$, we draw an arrow starting from $x\in X$ and point to $y\in Y$.

Using the relations in Example 1 again, we have 

<center>



</center>

<br>

Note:
- The relation as shown in Figure A satisfies the definition of a function as every element in $X$ is mapped to exactly one element in $Y$. 
- For Figure B, we see that two elements of $X$ (namely 2 and 4) are mapped to the element 7 in $Y$, this relation shown  still satisfies the definition of a function. If there are more than one element in the input space (domain) gets mapped to the same element in the output space (codomain), we call this relation **many-to-one**. From the example, we see that a many-to-one relation is a function.
- The relation as shown in Figure C is not a function for two reasons.
    - the element 3 in $X$ is mapped to two elements in $Y$, and
    - the element 4 in $X$ is not mapped to any element in $Y$.
    In this example, if any of the reason above holds, the relation is already not a function as it fails the definition.
    Such relation such that there are elements in the domain that gets mapped to the different elements in the codomain is called  **one-to-many**.
    From the example, we see that a one-to-many relation is *NOT a function*.


## 2.3 Graphical Representation on 2D Cartesian plane.
This representation of relations is by far the most useful, which contributes to its popularity in mathematics. 

Let $X$ and $Y$ be sets. In this **graphical representation**, if $x\mapsto y$, we can create a point $(x,y)$ and plot this point in the Cartesian plane. 

Again, we use the relations in Example 1. Refer to the diagrams in Example 4 and Example 5.

> Note: This last representation is the reason why the relation $y=mx+c$ which you probably would have encountered earlier in your previous mathematics classes be called *linear*, as when you plot all the pairs under the relation, it really looks like a line. 

### Example 5
Recall that in the example 1, for the relation $f$, $1 \mapsto 5$, $2 \mapsto 3$, $3 \mapsto 7$, $4 \mapsto 9$. As such, we:
1. create all the points, which are $(1,5)$, $(2,3)$, $(3,7)$, $(4,9)$,
2. plot the points on the Cartesian plane


```python
# We import the following so
from matplotlib import pyplot as plt

# Rule of f
def f(x):
    if x == 1:
        return 5
    if x == 2:
        return 3 
    if x == 3:
        return 7 
    if x == 4:
        return 9 

# domain of f
domain_of_f = [1,2,3,4]

# create the collection of the points (x,f(x)) before plotting
set_of_points = [(x,f(x)) for x in domain_of_f]

# The following are just commands for the plotting procedures.
# There's no need to be overly fixated on it
plt.clf()
range_of_f = [f(x) for x in domain_of_f]
plt.plot(domain_of_f,range_of_f,'ro')
ax = plt.gca()
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
for x in domain_of_f:
    point = (x,f(x))
    plt.annotate(f'{point}',point)

# show the plot at the end
plt.show()
```

Similarly, for the relation $g$, $1 \mapsto 5$, $2 \mapsto 7$, $3 \mapsto 9$, $4 \mapsto 7$.


```python
# We import the following so
from matplotlib import pyplot as plt

# Rule of g
def g(x):
    if x == 1:
        return 5
    if x == 2:
        return 7 
    if x == 3:
        return 9 
    if x == 4:
        return 7 

# domain of g
domain_of_g = [1,2,3,4]

# create the collection of the points (x,f(x)) before plotting
set_of_points = [(x,g(x)) for x in domain_of_g]

# The following are just commands for the plotting procedures.
# There's no need to be overly fixated on it
plt.clf()
range_of_g = [g(x) for x in domain_of_g]
plt.plot(domain_of_g,range_of_g,'ro')
ax = plt.gca()
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
for x in domain_of_g:
    point = (x,g(x))
    plt.annotate(f'{point}',point)

# show the plot at the end
plt.show()
```

The graphical representation of a relation can also show when the relation is not a function. In example 1, we see the relation $h$, $1 \mapsto 3$, $2 \mapsto 7$, 3 is mapped twice to different elements in $Y$, $3 \mapsto 3$, $3 \mapsto 9$, and 4 isn't mapped to anything. Note in the following graph that there are 2 points ( $(3,3)$ and $(3,9)$) that has the same input value $x$. By definition of a function, such thing cannot happen (see earlier section). As such, $h$ cannot be a function.


```python
# We import the following 
from matplotlib import pyplot as plt

# Note that we would even have problem in defining the relation h as
# when x = 3, there are 2 return values

# create the collection of the points (x,f(x)) before plotting
set_of_points = [(1,3),(2,7),(3,3),(3,9)]

# The following are just commands for the plotting procedures.
# There's no need to be overly fixated on it
plt.clf()
x=0
y=1
plt.plot([pt[x] for pt in set_of_points],[pt[y] for pt in set_of_points],'ro')
ax = plt.gca()
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
for point in set_of_points:
    plt.annotate(f'{point}',point)

# show the plot at the end
plt.show()
```

The power of this approach is more apparent when there're are many points defined by the relation. As we could show further connections between the points in the relation, like shapes they trace, clustering etc. 

### Example 6
In the following example, we will show how the function $$f:x\mapsto x^2, \quad \text{for } x\geq 2$$ can be represented by the graphical approach.

> Note that the interval $x\geq 2$ actually contains **infinite number of points**. In Python, we can't actually have infinitely many such points and also the lower $x_l$ and upper bounds $x_u$ of the $x$ values has to be determined before plotting. As such, the trick is to 
> 1. limit the number $n$ of the points that we want to display. To mimic an infinite number of points behaviour, we pick a large enough value of $n$ that is appropriate
>2. to distribute $n$ points across the $x$-values uniformly, we 
>    - set $\Delta x=\frac{x_{l}-x_{u}}{n}$, and 
>    - compute $f(x+i\Delta x)$ for $0 \leq i \leq n$,
>    - collect these computed values into a list of $(x,f(x))$ values. These are our points.
>3. plot the points on the cartesian diagram. Adjusting value of $n$ if necessary to achieve the smoothness and connectedness of the curve


```python
from matplotlib import pyplot as plt

# Rule of f
def f(x):
    return x**2 

# Mimicking the domain of f 
upper_x_bound = 10
lower_x_bound = 0
no_of_points = 100
domain_of_f = [2+((upper_x_bound-lower_x_bound)/no_of_points)*i for i in range(no_of_points)]

# create the collection of the points (x,f(x)) before plotting
set_of_points = [(x,f(x)) for x in domain_of_f]

# The following are just commands for the plotting procedures.
# There's no need to be overly fixated on it
plt.clf()
range_of_f = [f(x) for x in domain_of_f]
plt.plot(domain_of_f,range_of_f,'ro')
ax = plt.gca()
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.set_xbound(lower=0, upper=None)

# show the plot at the end
plt.show()
```

Recall that the range of a function $f:X\rightarrow Y$ is defined as the set of all the possible images of the elements of $X$ under the function $f$.

### Example 7
- **(a)** Given that $f$ is the function with rule $f(x)=x^3$ defined on $D_f=\{1,2,3\}$. Find $R_f$.
- **(b)** Given that $g$ is the function with rule $g(x)=x^3$ defined on $D_g=[1,3]$. Find $R_g$.  
- **(c)** Given that $h$ is the function with rule $h(x)=x^3$ defined on $D_h=[1,3]$. Find $R_h$.    


```python
# We import the following so
from matplotlib import pyplot as plt

# Rules of f, g and h
def f(x):
    return x**3

def g(x):
    return x**3

def h(x):
    return (x-2)**2

# domain of f, g, and h
domain_of_f = [1,2,3]

# Mimicking the domain of g and domain of h
# the variables are repeated for clarity purpose
upper_x_bound = 3
lower_x_bound = 1
no_of_points = 100
domain_of_g = [1+((upper_x_bound-lower_x_bound)/no_of_points)*i for i in range(no_of_points)]
domain_of_h = [1+((upper_x_bound-lower_x_bound)/no_of_points)*i for i in range(no_of_points)]

# Plotting f
plt.clf()
range_of_f = [f(x) for x in domain_of_f]
plt.plot(domain_of_f,range_of_f,'ro')
ax = plt.gca()
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
for x in domain_of_f:
    point = (x,f(x))
    plt.annotate(f'{point}',point)

plt.show()

# Plotting g
plt.clf()
range_of_g = [g(x) for x in domain_of_g]
plt.plot(domain_of_g,range_of_g,'bo')
ax = plt.gca()
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)


# annotating start and end point
# note that in plt.annotate, we are just adjusting the 2nd argument for the position of the text for visual clarity
domain_g_lower = 1
domain_g_upper = 3
start_point = (domain_g_lower,g(domain_g_lower))
plt.annotate(f'{start_point}',(start_point[0],start_point[1]+2))
end_point = (domain_g_upper,g(domain_g_upper))
plt.annotate(f'{end_point}',(end_point[0],end_point[1]-2))

plt.show()

# Plotting h
plt.clf()
range_of_h = [h(x) for x in domain_of_h]
plt.plot(domain_of_h,range_of_h,'bx')
ax = plt.gca()
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)

# annotating start and end point
# note that in plt.annotate, we are just adjusting the 2nd argument for the position of the text for visual clarity
domain_h_lower = 1
domain_h_upper = 3
start_point = (domain_h_lower,h(domain_h_lower))
plt.annotate(f'{start_point}',(start_point[0]+0.1,start_point[1]))
end_point = (domain_h_upper,h(domain_h_upper))
plt.annotate(f'{end_point}',(end_point[0],end_point[1]))
min_point = (2,h(2))
plt.annotate(f'{min_point}',(min_point[0],min_point[1]+0.05))

plt.show()
```

*Solution*:
- **(a)** We have $f(1)=1$, $f(2)=8$ and $f(3)=27$. Hence, $R_f=\{1,8,27\}$.
- **(b)** From the graph, we see that the values of $g(x)$ for any $x \in D_g$ is between $1=g(1)$ and $27=g(3)$ inclusive. Hence, $R_g=[1,27]$.
- **(c)** From the graph, we can see the values of $h(x)$ for any $x \in D_h$ is between $0$, which happens to be the minimum value that $h(x)$ can take in the domain and $1=h(1)=h(3)$, which happens to be values at the end values of the interval $[1,3]$.

Note:
1.	To find the range of a function that is defined over an interval, it is highly recommended (for A-Level purpose, it is **necessary**) to sketch the graph of the function **within the domain** for visual clarity. 
2.	In general, we cannot use the endpoints of the domain to find the range (see Example 7). Parts **(b)** and **(c)** illustrate this point. We can however do so if we are sure that the graph of the function is increasing or decreasing over the entire domain.

### Example 8
State the rule and domain, and find the range of each of the following functions.
- **(a)** $f:x\mapsto x^2, x\in \mathbb{R}$
- **(b)** $g:x\mapsto x^2, x\in [-1,1]$ 
- **(c)** $h:x\mapsto x^2, x\in [-1,2)$


```python
# We import the following so
from matplotlib import pyplot as plt

# Rules of f, g and h
def f(x):
    return x**2

def g(x):
    return x**2

def h(x):
    return x**2

# Mimicking domain of f, g, and h
upper_x_bound = 3
lower_x_bound = 1
no_of_points = 100
domain_of_f = [-3+((3-(-3))/no_of_points)*i for i in range(no_of_points)]
domain_of_g = [-1+((1-(-1))/no_of_points)*i for i in range(no_of_points)]
domain_of_h = [-1+((2-(-1))/no_of_points)*i for i in range(no_of_points)]

# Plotting f
plt.clf()
range_of_f = [f(x) for x in domain_of_f]
plt.plot(domain_of_f,range_of_f,'ro')
ax = plt.gca()
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.set_xbound(lower=-3, upper=3)
ax.set_ybound(lower=-1, upper=5)

# Since the domain is R, we don't annotate start and end points
# We annotate the minimum point
min_point = (0,0)
plt.annotate(f'{min_point}',(min_point[0],min_point[1]+0.3))

plt.show()

# Plotting g
plt.clf()
range_of_g = [g(x) for x in domain_of_g]
plt.plot(domain_of_g,range_of_g,'bo')
ax = plt.gca()
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.set_xbound(lower=-3, upper=3)
ax.set_ybound(lower=-1, upper=5)

# annotating start and end point
# note that in plt.annotate, we are just adjusting the 2nd argument for the position of the text for visual clarity
domain_g_lower = -1
domain_g_upper = 1
start_point = (domain_g_lower,g(domain_g_lower))
plt.annotate(f'{start_point}',(start_point[0],start_point[1]+0.1))
end_point = (domain_g_upper,g(domain_g_upper))
plt.annotate(f'{end_point}',(end_point[0],end_point[1]+0.1))
min_point = (0,0)
plt.annotate(f'{min_point}',(min_point[0],min_point[1]+0.3))

plt.show()

# Plotting h
plt.clf()
range_of_h = [h(x) for x in domain_of_h]
plt.plot(domain_of_h,range_of_h,'bx')
ax = plt.gca()
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.set_xbound(lower=-3, upper=3)
ax.set_ybound(lower=-1, upper=5)

# annotating start and end point
# note that in plt.annotate, we are just adjusting the 2nd argument for the position of the text for visual clarity
domain_h_lower = -1
domain_h_upper = 2
start_point = (domain_h_lower,h(domain_h_lower))
plt.annotate(f'{start_point}',(start_point[0]+0.1,start_point[1]))
end_point = (domain_h_upper,h(domain_h_upper))
plt.annotate(f'{end_point}',(end_point[0],end_point[1]))
min_point = (0,0)
plt.annotate(f'{min_point}',(min_point[0],min_point[1]+0.3))


plt.show()
```

*Solution*

From the graph plotted above, we have that 

<center>

||Rule|Domain|Range|
|-|-|-|-|
|$f$|$f(x)=x^2$|$D_f=(-\infty,\infty)$|$R_f=[0,\infty)$|
|$g$|$g(x)=x^2$|$D_g=[-1,1]$|$R_g=[0,1]$|
|$h$|$h(x)=x^2$|$D_h=[-1,2)$|$R_h=[0,4)$|

</center>

<br>

> Note:	Though functions $f$, $g$ and $h$ have the same rule, they have different domains. The implication is that the graphs of functions $f$, $g$ and $h$ look different. As a result, functions $f$, $g$ and $h$ are not the same functions.

### Example 9 Logistic Function
The **standard logistic function** is defined to be the function $S$ such that 
$$ S:x\mapsto \frac{1}{1+e^{-x}}, \quad x\in \mathbb{R}.$$

Graphically, it can be represented as 

<center>

 

Logistic Function (Left) and Heaviside Function (Right)


</center>

Note that 
- $D_S=\mathbb{R}$
- $R_S=(0,1)$
- the logistic function mimics the graph to the right that represents a signal that switches on at a specified time and stays switched on indefinitely (`ON` if $y=1$ and `OFF` when $y=0$)
- *differentiable at every point in the domain. (Reader unfaimiliar with derivatives might want to read up on it first.)

Due to its properties, it has many applications in a range of fields including biology, chemistry, economics and even artificial neural networks. 

In artificial neural network, the logistic function can be used as an activation function for the nodes to define the output of the nodes given an input. Below is a diagram that illustrates the working of a single node in the artificial neural network. Because of its differentiability, it can be used for training of a neural network via gradient descent with backpropagation.

<center>



Node diagram from [https://tex.stackexchange.com/questions/104334/tikz-diagram-of-a-perceptron]

</center>

### 2.3.1 Vertical Line Test
Furthermore, to determine whether a relation is indeed a function with the graphical relation, we can use the **vertical line test** (VLT). With this test, we:
1. try to find a vertical line $x = k$ that can be drawn such that it cuts through the graph at least twice. 
2. if such vertical line exists, the relation cannot be a function. (why?)
3. on the other hand, if such vertical line cannot be drawn, the relation is indeed a function.  

Observe that the graph of $f$ is only defined for $x \geq 2$ from its definition. Thus, if we apply VLT, we only try to find a vertical line $x = k$, where $k \in D_f$, i.e $x\geq 2$. And it can be observed that no such vertical line exists. 

### Example 10
In the following example, the relation that is represented by the graph **IS NOT A FUNCTION** as it fails the vertical line test.


```python
from matplotlib import pyplot as plt

# Rule of the relation
def relation(x):
    return x**2 - 5 

# Mimicking the interval of x
upper_x_bound = 10
lower_x_bound = -10
no_of_points = 1000
interval_of_relation = [lower_x_bound+((upper_x_bound-lower_x_bound)/no_of_points)*i for i in range(no_of_points)]

# create the collection of the points before plotting
set_of_points = [(relation(x),x) for x in interval_of_relation]

# The following are just commands for the plotting procedures.
# There's no need to be overly fixated on it
plt.clf()
values = [relation(x) for x in interval_of_relation]
plt.plot(values,interval_of_relation,'ro')
ax = plt.gca()
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.set_xbound(lower=lower_x_bound, upper=upper_x_bound)
ax.set_ybound(lower=-10, upper=10)

# show the plot at the end
plt.show()
```

It fails the vertical line test because the vertical line $x=0$ will cut the graph *more than once* (twice in this case).

The rule of the relation in the above graph is actually $y^2 - 5 = x$ for $x \geq 0$. If we see the algebraic form of the relation, we see that for any $x$, we have $x \mapsto \sqrt{x+5}$ and $x \mapsto -\sqrt{x+5}$. In other words, a single value of $x$ (except 0) is mapped to two different values, violating the definition of a function discussed in Section 1.

# 3. Equality of Functions
Let $f$ and $g$ be functions, we say $f = g$ if 
1. the domains of $f$ and $g$ is the same, i.e. $D_f = D_g$, and
2. $f(x)=g(x)$ for all the values in their domain. Roughly speaking, the 'rules are the same' (in inverted comma, because the two expressions could look different but evaluated to the same value)

### Example 11
1. The function $f:x\mapsto 2x, x>=0$ is not the same as the function $g:x \mapsto 2x, x>=-1$ even though they have the same rule.
2. The functions $h_1:x \mapsto \sin{x}, x\in \{k\pi : k\in \mathbb{Z}\}$ is the same as the function $h_2:x\mapsto 0, x\in \{k\pi : k\in \mathbb{Z}\}$.

# 4. Piecewise Function
A **piecewise function** is one that has a different rule for different intervals (think parts) of its domain component. 

> In Python, this basically means that we can just use `if` conditional statement to handle the different rules for the different intervals. 

## Example 12
The **Heaviside step function** $H$, seen in the earlier section, is defined as $$H\left(x\right)=\begin{cases}
1, & x> 0,\\
0, & x\leq0.
\end{cases}$$

We will use the following Python code to produce the graph.


```python
from sympy import *
from IPython.display import Math,display,Latex
from matplotlib import pyplot as plt

x = symbols('x')

# We define the function H by first defining the individual parts
class H_more(Function):
    @classmethod
    def eval(cls, x):
        return 1

class H_less(Function):
    @classmethod
    def eval(cls, x):
        return 0

# Combining the parts and using conditional 
def H(x):
    if x>0:
        return H_more(x)
    elif x<=0:
        return H_less(x)

upper_x_bound = 3
lower_x_bound = -3
no_of_points = 100
domain_of_H = [-3+((3-(-3))/no_of_points)*i for i in range(no_of_points)]

# Plotting g
plt.clf()
range_of_H = [H(x) for x in domain_of_H]
plt.plot(domain_of_H,range_of_H,'ro')
ax = plt.gca()
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.set_xbound(lower=-3, upper=3)
ax.set_ybound(lower=-1, upper=5)
```

## Example 13
A piecewise function $g$ is defined as $$g\left(x\right)=\begin{cases}
-x+1, & x<0,\\
x, & x\geq0.
\end{cases}$$
 
Find the range of $g$.

*Solution*

The graphical representation of $g$ is as follows.


```python
# We import the following so
from matplotlib import pyplot as plt

# Rules of g
def g(x):
    # using if for the interval x<0
    if x<0:
        return -x+1

    # the other rule for the interval x>=0    
    elif x>=0:
        return x

# Mimicking domain of g
upper_x_bound = 3
lower_x_bound = -3
no_of_points = 100
domain_of_g = [-3+((3-(-3))/no_of_points)*i for i in range(no_of_points)]

# Plotting g
plt.clf()
range_of_g = [g(x) for x in domain_of_g]
plt.plot(domain_of_g,range_of_g,'ro')
ax = plt.gca()
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.set_xbound(lower=-3, upper=3)
ax.set_ybound(lower=-1, upper=5)

# Since the domain is R, we don't annotate start and end points
# We annotate the points where the pieces have different rule
right_piece = (0,g(0))
plt.annotate(f'{right_piece}',(right_piece[0],right_piece[1]-0.3))
left_piece = (0,g(0)+1)
plt.annotate(f'{left_piece}',(left_piece[0],left_piece[1]+0.3))

plt.show()
```

From the graph of $g$, we can see that $R_g=[0,\infty)$.

# 5. One-to-One Functions
We have defined functions to be relations that have exactly one output for every possible input. This does not restrict that the output cannot be repeated. It is natural to consider whether we can have functions that have a unique output for every possible input. If this is possible, then if we know the output of the function, we can easily recover the input. 

An example that describes the desirability of this situation would be a mapping from a person to his/her IC number. Given we have a database of the details of the people, by knowing the IC number of a person, we can easily recover various details of the person like name, contact etc. 

Given a function $f$, we say that $f$ is **one-to-one** (or 1-1) if for any $x_1,x_2\in X$ such that $x_1 \neq x_2$, $f(x_1) \neq f(x_2$. Or, equivalently, if $f(x_1)=f(x_2)$, then it must be the case that $x_1 = x_2$. 

In plain English, $f$ is 1-1 if no two elements of $X$ have the same image.

<center>



</center>

<br>

> In the above example:
> - $f$ is not an example of a 1-1 function but we can *restrict its domain* to make it 1-1. Say, its minimum point occurs at $(a,f(a))$, then its restricted domain can be $x\geq a$ for $f$ to be 1-1.
> - Any value that is greater than $a$ can also be used. For example, if $b>a$ then $f(x)$ where $x\geq b$ is also a 1-1 function.
> - $x\geq a$ or $x\in [a,\infty)$ is known as the **maximal domain** for $f$ to be 1-1. We will define it below.

## 5.1 Testing if a Function is 1-1
Based on the definition of a one-one function given above, to show that a function $f$ is 1-1 using a graphical argument, we should 
- EITHER, show that **every** horizontal line $y=b$, $b\in R_f$ cuts the graph of $y=f(x), x\in D_f$ **exactly once**,
- OR, show that **every** horizontal line $y=b$, $b\in \mathbb{R}$  cuts the graph of $y=f(x), x\in D_f$ **at most once**.

On the flipside, to show that a function f is NOT 1-1, we should provide a counter-example. This can be achieved by employing one of the following methods.
- Method 1 Using a graphical argument: Find **one** horizontal line such that it cuts the graph of $y=f(x), x\in D_f$ **at least twice**. 
- Method 2 Using the defintion: Find $x_1,x_2 \in D_f$ such that $x_1\neq x_2$ but $f(x_1)=f(x_2)$.

> Note:	
> 1. It is necessary to provide a relevant graph and the statement above to show that the function is indeed 1-1. 
> 2. However to show that the function is not 1-1, we only need to provide a counterexample by stating explicitly an equation of a horizontal line that intersects the graph at least twice.

<br>

## Example 14
We say a function $g$ is a **restriction of a function $f$** if:
- the rule of $g$ is the same as $f$, i.e. $g(x)=f(x)$, but
- domain of $g$ is strictly smaller than the domain of $f$, i.e. $D_g \subset D_f$.

Determine if the following are one-to-one functions, giving your reasons. If it is not one-to-one, find a restriction of the function with a maximal domain such that it is one-to-one and has the same range as the original function.
- **(i)** $f:x\mapsto x^2,x\in \mathbb{R}$, 	  
- **(ii)** $g:x\mapsto x^2-4x+1, x\in \mathbb{R}, x\geq 1$,	  
- **(iii)** $h:x\mapsto\frac{1-x}{x},x>0$.	  

*Solution*
- **(i)** The horizontal line $y=1$ cuts the graph of $y=x^2$ twice. Hence, $f$ is not 1-1.
    Alternatively, since $f(-1)=1=f(1)$, $f$ is not 1-1. 
    Now $R_f=[0,\infty)$. From the graph, if we restrict the domain to $[0,\infty)$, then every horizontal line will cut the graph exactly once. Furthermore, the range is the same as $R_f$. Therefore, a required restriction of $f$ is $f_1:x\mapsto x^2, x\geq 0$.

    <center>

    

    </center>
- **(ii)** From the graph, the horizontal line $y=-2$ cuts the curve at 2 points. Hence $g$ is not 1-1. Alternatively, since $f(1)=-2=f(4)$, $f$ is not 1-1. Now $R_g=[-3,\infty)$. From the graph, a restriction of $g$ is $g_1:x\mapsto x^2-4x+1,x\geq 2$.
    <center>

    

    </center>
- **(iii)** From the graph, any horizontal line $y=k$, $k>-1$ cuts the graph exactly once. Therefore $h$ is a 1-1 function.
    <center>

    

    </center>

### Example 15
The standard logistic function defined in the earlier example has the following graph.

<center>



</center>

From the graph, any horizontal line $y=k$, $k\in (0,1)$ cuts the graph exactly once. Therefore $S$ is a 1-1 function. 

# 6. Composite Functions
So far, we have examined various examples of functions individually. A natural question that arises from this is as follow: Given two functions $f,g$, can we create a new function with them as constituents?

The answer is YES, and one way to achieve it is by chaining the output of a function, say $g$, and using it as an input for the next function, say $f$. However, this approach will not always work and in this section, we will see the conditions when we can actually do this.

Such expected behaviour is totally natural and frequently encountered. For a more concrete example, consider the following situation. We have a square with sides of length $x$ units. If we are to increase the side of the square by 2 units and then, scale the sides of the square by a factor of 3, what is the area of the resultant square?

Intuitive, we would have done the following in sequence 

$$\begin{align*}
x&\mapsto x+2 \\ 
x+2&\mapsto 3(x+2) \\
3(x+2)&\mapsto (3(x+2))^2.
\end{align*}$$ 

So, if we think about it, we have $x\mapsto (3(x+2))^2$.

The main point this example try to illustrate is that we are taking outputs of a mapping to be used as an input of *another* mapping repeatedly (see line 2 and 3) and in the end, we can actually just condense the multiple mapping steps into only ONE map.

Such approach of creating a new function (by nesting a function inside another) is what we call **function composition**, and the resulting function is called a **composite function**. We will make the definition formal a little later

## 6.1 Condition for Existence for Composite Functions
In the example given in the previous section, it is indeed reasonable that such composite function would exist in the end. However, such procedure doesn't always necessarily produce a function in the end. 

For example, consider $f:{1,2}\mapsto 0$ and $g:{3,4}\mapsto 1$. We see that
1. if we start with $f$ first, then $1\mapsto 0$ and $2\mapsto 0$, but in both cases $0$ cannot be mapped by $g$ as $0$ doesn't exists in the domain of $g$.
2. on the other hand, if we are to start with $g$ first, then $3\mapsto 1$ and $4\mapsto 1$, and since $1$ is inside the domain of $f$, we can continue further to have $$3\mapsto 1\mapsto 0, 4\mapsto 1 \mapsto 0.$$

> Note: the input elements in this process to generate composite functions is always the elements in the domain of the first function being considered. In the above cases, it is $D_f$ and $D_g$ respectively.

As such, we can conclude that in order for the chaining of the values to be carried out successfully, the output of the first function must be able to be used as the input of the function that comes after. If $g,f$ is the first and second function respectively, symbolically, this means that $R_g$ must fall within $D_f$, i.e. $R_g\subseteq D_f$.

We are now ready to define a composite function. 

Let $f:X_1\rightarrow Y_1$ and $g:X_2 \rightarrow Y_2$. The **composite function** $f\circ g$ (note that this seemingly has 2 functions, but really just symbol used to represent a single SINGLE function) is a function such that 
1. $R_g \subseteq D_f = X_1$. Note that $R_g$ is not necessarily $X_1$.
2. $f\circ g:X_2 \rightarrow Y_1$. In particular, note that $D_{f\circ g}=D_g$.
3. for any $x\in X$, $x\mapsto  f(g(x))$. In other words, $f\circ g (x) = f(g(x))$. Note that the RHS has TWO functions $f$ and $g$, while the LHS only has ONE function, $f\circ g$.

Graphically, 

<center>



</center>

On the other hand, if $R_g \nsubseteq D_f$, graphically we have

<center>



</center>

In particular, there are some elements in $R_g$ that cannot be mapped under the rule of $f$. Consequently, in such case, the composite function $f\circ g$ cannot exist.

> The shorthand for the composite function $f\circ g$ is just $fg$. So, in the subsequent parts, we will drop the $\circ$ symbols to refer to the composite functions.

An analogy of doing composite functions is like putting on multiple socks on the same foot one after another.  

### Example 16
The functions $f$ and $g$ are defined as 
$$\begin{align*}
    f:x &\mapsto \ln x, \quad & x\in (0,\infty), \\
    g:x &\mapsto -x, \quad &x\in \mathbb{R}.
\end{align*}$$

We note that $R_g = \mathbb{R}$ and $D_f=(0,\infty)$. So, we have that $R_g\nsubseteq D_f$. As a consequence, the composite function $f\circ g$ cannot exist. 
For a concrete example of an element that cannot be mapped, we take $1 \in D_g$, we see that under $g$,
$$ 1\mapsto g(1)=-1.$$
However, $-1\notin D_f$ and as such $f(-1)=f(g(1))$ is undefined. 

### Example 17 (Restriction of Domain for Composite to Exist)
The functions $f$ and $g$ are defined as
$$\begin{align*}
    f	:x&\mapsto e^{1-x} &,x\in\mathbb{R},x>0 \\ 
    g	:x&\mapsto\ln\left(x-1\right) &,x\in\mathbb{R},x>1
\end{align*}$$
 
By restricting the domain of $g$ to $(a,\infty)$, where $a$ is a real number, find the least value of $a$ such that the composite function $fg$ exists. Define $fg$ in similar form.

*Solution*

For the composite function to exist, we must restrict the domain of $g$ such that the range of the restriction function is a subset of $D_f=(0,\infty)$. Since we want the least value of $a$, we will attempt to find an answer based on the whole of $D_f$ first.

From the graph of $y=g(x)$, we see that if we restrict the domain to $(2,\infty)$ , then the range of the restriction of $g$ is $(0,\infty)$   which is $D_f$.


```python
from sympy.calculus.util import *

x = symbols('x')

# We define the function g 
class g(Function):
    @classmethod
    def eval(cls, x):
        return log(x-1)

plot(g(x),xlim=(-5,5),ylim=(-5,5))
```

Therefore least value of $a$ is 2.


```python
from sympy import *
from IPython.display import Math,display,Latex

# Defining the symbol for the parameters/arguments/input of the functions f,g
x = symbols('x')

# We define the function f 
class f(Function):
    @classmethod
    def eval(cls, x):
        # the rule of f 
        return exp(1-x)

# We define the function g 
class g(Function):
    @classmethod
    def eval(cls, x):
        # the rule of g 
        return log(x-1)

print('The rule of fg')
#Compose f(g(x)) 
display(Math(f'fg(x)={ latex(f(g(x)))}'))

```

    The rule of fg



$\displaystyle fg(x)=\frac{e}{x - 1}$


### Example 18
The functions $f$ and $g$ are defined as
$$\begin{align*}
    f:x & \mapsto \frac{x}{ax-b}, x\in \mathbb{R}, & x\neq \frac{b}{a} \\
    g:x & \mapsto \sqrt{x}, & x\in (0,\infty)
\end{align*}$$
 
Explain whether the composite function $fg$ exists, justifying your answer.

*Solution*
Since 
$R_g=(0,\infty) \nsubseteq \mathbb{R}\backslash \{\frac{b}{a}\}$ , the composite function $fg$ does not exist.

### Example 19
The functions $f$ and $g$ are defined as 
$$\begin{align*}
    f:x &\mapsto x+1, \quad & x\in \mathbb{R}, \\
    g:x &\mapsto x^2, \quad &x\in \mathbb{R}.
\end{align*}$$
 
It is given that $fg$ and $gf$ exists (the hardworking reader should verify this), find $fg(x)$ and $gf(x)$.

*Solution*:
So this example is asking us to find the rules of the composite function $fg$ and $gf$. As defined earlier, $fg(x)=f(g(x))$ and $gf(x)=g(f(x))$. As such,

Since $f(x)=x+1$ and $g(x)=x^2$,
$$\begin{align*}
fg(x)&=f(g(x))\\
    &=f(x^2)\\
    &=x^2+1,\\
gf(x)&=g(f(x))\\
    &=g(x+1)\\
    &=(x+1)^2.
\end{align*}$$

> Example 11 illustrates the fact that generally $gf$ and $fg$ are **NOT** equivalent, that is, composition of functions are not commutative.

To work my abstract mathematical functions in Python, we can use the `Sympy` package. The precise functionalities are left for the reader to read up. In the code below, we will show how to:
1. define a function in Python that behaves like a mathematical function,
2. do function compositions.


```python
from sympy import *
from IPython.display import Math,display,Latex

# Defining the symbol for the parameters/arguments/input of the functions f,g
x = symbols('x')

# We define the function f 
class f(Function):
    @classmethod
    def eval(cls, x):
        # the rule of f is the value that is returned by the function
        return x+1

# We define the function g 
class g(Function):
    @classmethod
    def eval(cls, x):
        # the rule of g is the value that is returned by the function
        return x**2

print('The rule of fg')
#Note that we can compose f(g(x)) with Sympy module and it behaves as how we expect it to be in mathematics
display(Math(f'fg(x)={ latex(f(g(x)))}'))

print('The rule of gf')
#Similarly,we can compose g(f(x)) with Sympy module
display(Math(f'gf(x)={ latex(g(f(x)))}'))
```

    The rule of fg



$\displaystyle fg(x)=x^{2} + 1$


    The rule of gf



$\displaystyle gf(x)=\left(x + 1\right)^{2}$


### Example 20
The functions f and g are defined as 
$$\begin{align*}
f:x&\mapsto\frac{1}{x^{2}-1},\quad x\in\mathbb{R},x\neq-1,x\neq1\\
g:x&\mapsto\frac{1}{x-3},\quad x\in\mathbb{R},x\neq2,x\neq3,x\neq4
\end{align*}$$
Show that $fg\left(x\right)=\frac{\left(x-3\right)^{2}}{\left(4-x\right)\left(x-2\right)}$.

*Solution*

By definition,  

$$\begin{align*}

fg\left(x\right)	&=f\left(g\left(x\right)\right) \\
	&=f\left(\frac{1}{x-3}\right) \\
	&=\frac{1}{\left(\frac{1}{x-3}\right)^{2}-1} \\
	&=\frac{\left(x-3\right)^{2}}{1-\left(x-3\right)^{2}} \\
	&=\frac{\left(x-3\right)^{2}}{\left(1-\left(x-3\right)\right)\left(1+\left(x-3\right)\right)} \\
	&=\frac{\left(x-3\right)^{2}}{\left(4-x\right)\left(x-2\right)}
\end{align*}$$

We verify our answer with Python again.


```python
from sympy import *
from IPython.display import Math,display,Latex

x = symbols('x')

# We define the function f 
class f(Function):
    @classmethod
    def eval(cls, x):
        return 1/(x**2-1)

# We define the function g 
class g(Function):
    @classmethod
    def eval(cls, x):
        return 1/(x-3)

print('The rule of fg')
#fg(x)
display(Math(f'fg(x)={ latex(f(g(x)))}'))
print('After simplification')
display(Math(f'fg(x)={ latex(simplify(f(g(x))))}'))

```

    The rule of fg



$\displaystyle fg(x)=\frac{1}{-1 + \frac{1}{\left(x - 3\right)^{2}}}$


    After simplification



$\displaystyle fg(x)=- \frac{\left(x - 3\right)^{2}}{\left(x - 3\right)^{2} - 1}$


Note that the answer from Python is not yet the form required by the question, but a simple algebraic manipulation will get us there.

### Example 21
Based on the functions $f,g$ defined in Example 11, find $ff(x)$ and $gg(x)$.


```python
from sympy import *
from IPython.display import Math,display,Latex

# Defining the symbol for the parameters/arguments/input of the functions f,g
x = symbols('x')

# We define the function f 
class f(Function):
    @classmethod
    def eval(cls, x):
        return x+1

# We define the function g 
class g(Function):
    @classmethod
    def eval(cls, x):
        return x**2

print('The rule of ff')
display(Math(f'ff(x)={ latex(f(f(x)))}'))

print('The rule of gg')
display(Math(f'gg(x)={ latex(g(g(x)))}'))
```

    The rule of ff



$\displaystyle ff(x)=x + 2$


    The rule of gg



$\displaystyle gf(x)=x^{4}$


> Example 12 illustrates the fact that we can compose functions with itself.

Such composite functions illustrated in Example 12 is kinda special and has a certain notation attached with it. Below, we will write out the definition.

Let $f:X\rightarrow Y$ be a function that can be composed with itself repeatedly. Then if $n$ is an integer, $$f^{n}\left(x\right)=\underset{\text{There are }n \text{ number of }f \text{'s}}{\underbrace{f\left(f\left(f\left(f\left(f\left(\dots\right)\right)\right)\right)\right)}}.$$

With this notation, then the functions in Example 12 are just $ff(x)=f^2(x)$ and $gg(x)=g^2(x)$. And from this point, we shall use this notation.

It is important to note that:
1. In general, the rule of the composite function $f^2(x)=f(f(x))$ is **NOT THE SAME** as the square of the function rule $f(x)^2$. I.e., $f^2(x)\neq (f(x))^2$.
2. The only exception for this are the trigonometric functions, where $\sin ^{n}(x) =(\sin(x))^{n}, \cos ^{n}(x) =(\cos(x))^{n}$ etc. This mismatch of the definition is due to historical reasons and the community of mathematicians just go along with it.

# 7. Inverse Functions
In section 5, we have seen a special case of functions which are the one-one functions. Let $f:X\rightarrow Y$ be such one-one function. This means that for every element $x\in X$, there exists one and only one element $y\in Y$ such that $f(x)=y$. And if we think about it, this property is really nice because this means that for every such $y$, there is only one such $x$ that gets mapped to it. In other words, we can actually recover the element $x$ that gets mapped to it.

Using our analogy of ID number earlier, from an ID number, we can get the details of the person with such ID number and vice versa, knowing the details of the person, we can get the ID number.

Graphically, we have that

<center>



</center>

All seems dandy so far. However, we note that if $R_f \neq Y$ for $f$, then there are some elements ($y_5,y_6$ in the diagram above) that doesn't get paired with any $x\in X$. This means that **if we want the reverse mapping to also be a function**, we need to restrict the elements of $Y$ to be considered for the reverse mapping to be just those who belongs in $R_f$ as well.

Such reverse mapping of $f$ that is also a function is what we call an **inverse function** (not reverse). We will give the definition below.

Let $f:X\rightarrow Y$ be a one-to-one function. $g$ is the **inverse function** of $f$ if
1. $g:R_f\rightarrow X$ such that,
2. $g(f(x))=x$.

If $g$ is the inverse function of $f$, we write $g$ as $f^{-1}$. So, point (2) above is usually written as $f^{-1}(f(x))=x$.

> Note:
> 1. From the the definition, $D_{f^{-1}}=R_f$,
> 2. $R_{f^{-1}}=D_f$, i.e., we can recover every element of $X$ just from an element $y\in R_f$.
> 3. For any $y\in Y$ and $x\in X$, if $y=f(x)$, then $f^{-1}(y)=x$.
> 4. $f^{-1}(x) \neq \frac{1}{f(x)}$. This is similar to the notation used for the trigonometric functions, e.g., $\sin x \neq \frac{1}{\sin x}$. (Well, we see again that the notations for the trigo functions are not consistent 🙄)
> 5. If $f^{-1}$ is an inverse of $f$, then $f$ must also be an inverse of $g$. In other words, we also have $f(f^{-1}(x))=x$.
> 6. For $f$ to have an inverse function (i.e. for $f^{-1}$ to exist), $f$ must be a one-one function.

We will illustrate point 6 graphically. Let us recall that a function is either a many-to-one relation or a one-to-one relation. However, it cannot be a one-to-many relation.

- Case 1. $f$ is a many-to-one function
    
    <center>

    

    </center>

    Both $x_1$ and $x_2$  are mapped to the value $y_0$ by the function $f$. For the inverse function to exist, every $y$-value in the range of $f$ can only be mapped from at most one $x$-value. In this case, we cannot have an inverse function.

- Case 2. $f$ is a one-to-one function
    
    <center>

    

    </center>

    Since every $y$-value is mapped from a unique $x$-value (Since $f(x_1)=f(x_2) \implies x_1=x_2$ for one-to-one function), every $x$-value can be *reversely* mapped from at most one $y$-value. Thus the inverse function exists.

### Example 22
For each of the following functions, determine if the inverse function exist. Justify your answer. 
- **(i)** $f:x \mapsto x^4$, $x\in \mathbb{R}$, 	 
- **(ii)** $g:x \mapsto x^4$, $x\in \mathbb{R}$, $x\geq 1$.	 

*Solution*
- **(i)** Since the horizontal line $y = 1$ cuts the graph of $y=x^4$ twice, $f$ is not 1-1. Therefore $f^{-1}$  does not exist.
    <center>

    

    </center>

- **(ii)** The sketch of the graph looks like.

    <center>

    

    </center>    


    Since any horizontal line $y=b$, $b\in\mathbb{R}$, cuts the graph of $y=g(x)$ at most once, $g$ is 1-1. Therefore $g^{-1}$ exists.
    Note:
    - The horizontal line test **does not directly lead to the existence of inverse functions**. Thus, it is **NOT** correct to write:
        “Since any horizontal line $y=b$, $b\in \mathbb{R}$, cuts the graph of $y = g(x)$ at most once, then $g^{-1}$ exists.” It must be first stated that the function is 1-1. 

## 7.1 Graphical Representation of Inverse Functions
Consider the graph of a one-to-one function $f:x\mapsto y, x\in D_f$. We have seen in Section 2 that the function can be representation as a set of points on a Cartesian plane. Symbolically this set is $\{(a,f(a)): a \in D_f\}$. Consequently, if $f^{-1}$ maps back the values in $R_f$ to $D_f$, then this means that if we are to represent $f^{-1}$ then the set of points that we need to consider is $$\{(f(a),a) : a \in D_f\}.$$

Note:
1. this means that we are just swapping the $x$ and $y$ coordinates of the points in the representation.
2. the graph of function $f:x\mapsto x, x\in D_f$ is special because if we are to swap the $x,y$ coordinates of the points and collect them, the resulting set of points is still the same.  

### Example 23
Consider the function $f(x)=x^2$, $x>0$. We see that 

<center>

|Value of $a$ |  $f(x)=x^2$, $x>0$ | $f^{-1}(x)=\sqrt{x}$, $x>0$
|- | - | -
|1 |	$(1, 1)$ |	$(1, 1)$
|2 |	$(2, 4)$ |	$(4, 2)$
|3 |	$(3, 9)$ |	$(9, 3)$
|5 |	$(5, 25)$ |	$(25, 5)$

</center>


```python
# We import the following so
from matplotlib import pyplot as plt

# Rules of f, g and h
def f(x):
    return x**2

# Mimicking domain of f
upper_x_bound = 0
lower_x_bound = 5
no_of_points = 100
domain_of_f = [0+((5-(0))/no_of_points)*i for i in range(no_of_points)]

# Plotting f, red 
plt.clf()
range_of_f = [f(x) for x in domain_of_f]
plt.plot(domain_of_f,range_of_f,'ro')

# We annotate the left boundary point
left_point = (0,f(0))
plt.annotate(f'{left_point}',(left_point[0],left_point[1]+0.3))

# Since the points in the inverse of f can be obtained by 
# swapping the x and y coordinates of the points
# we have the domain of the inverse of f to be just the range of f,
# range of f inv is the domain of f
# plotting f inverse, blue
# Note that we didn't even need to recompute the mappings
domain_of_f_inv = range_of_f
range_of_f_inv = domain_of_f
plt.plot(domain_of_f_inv,range_of_f_inv,'bx')
ax = plt.gca()
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.set_xbound(lower=-1, upper=3)
ax.set_ybound(lower=-1, upper=3)

plt.show()
```

A very important observation from the example above is that when we plot these points on the Cartesian plane will help us see that the coordinates in the right column are reflection of those in the left column in the line $y=x$. 

To see this, we will include the line $y=x$ in the diagram above in green colour.


```python
# We import the following so
from matplotlib import pyplot as plt

# Rules of f, g and h
def f(x):
    return x**2

# Mimicking domain of f
upper_x_bound = 0
lower_x_bound = 5
no_of_points = 100
domain_of_f = [0+((5-(0))/no_of_points)*i for i in range(no_of_points)]

# Plotting f, red 
plt.clf()
range_of_f = [f(x) for x in domain_of_f]
plt.plot(domain_of_f,range_of_f,'ro')

# We annotate the left boundary point
left_point = (0,f(0))
plt.annotate(f'{left_point}',(left_point[0],left_point[1]+0.3))

# Since the points in the inverse of f can be obtained by 
# swapping the x and y coordinates of the points
# we have the domain of the inverse of f to be just the range of f,
# range of f inv is the domain of f
# plotting f inverse, blue
# Note that we didn't even need to recompute the mappings
domain_of_f_inv = range_of_f
range_of_f_inv = domain_of_f
plt.plot(domain_of_f_inv,range_of_f_inv,'bx')


# Plotting h(x) = x, green
upper_x_bound = -5
lower_x_bound = 5
no_of_points = 1000
domain_of_h = [-5+((5-(-5))/no_of_points)*i for i in range(no_of_points)]
range_of_h = [-5+((5-(-5))/no_of_points)*i for i in range(no_of_points)]
plt.plot(domain_of_h,range_of_h,'go')
ax = plt.gca()
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.set_xbound(lower=-1, upper=3)
ax.set_ybound(lower=-1, upper=3)

plt.show()
```

> In other words, let $f:X\mapsto Y$ be a function such that the inverse function $f^{-1}$ exist. Then the graph of $y=f^{-1}(x)$ is the reflection of the graph $y=f(x)$ in the line $y=x$, i.e. the graphs of $f$ and $f^-1$ is symmetric about the line $y=x$.

### Example 24
The graphs of $y = f(x)$ and $y = x$ are shown below. Sketch, on the same diagram, the graph of $y=f^{-1}(x)$, labelling the coordinates of the endpoints. 

<center>



</center>

*Solution*

The graph of $f^{-1}(x)$ is indicated in red.

<center>



</center>

## 7.2 Finding the Rule of Inverse Functions
Recall that in our dealing of function $f:X \rightarrow Y$, we have $y=f(x)$, where $f(x)$ is just an expression in $x$. In other words, to get $y$, we need to operate on expressions in $x$. Now, if inverse functions reverse the mapping, the core idea is then to get $x$, we need to operate on expressions in $y$. As such, the logical thing to find the rule of the inverse function is to start with the original equation involving $y$ and $x$ where $y$ is the subject and manipulate them algebraically until $x$ becomes the subject instead. As such, we have the steps outlined below.

Suppose $f$ is the rule for a one-to-one function $f$. To find $f^{-1}(x)$, the rule of the inverse function of $f$, we apply the following steps.
1. Let $y=f(x)$
2. Make $x$ the subject of the equation, i.e. express $x$ in terms of $y$.  
3. This expression in terms of $y$ is the rule of the $f^{1}$, i.e. it's the $f^{-1}(y)$.
4. As it is customary for functions to use $x$ as input, we replace the occurences of $y$ with $x$. 

### Example 25
Let $f:x\mapsto 2x-2$, $x\in \mathbb{R}$. 
- Explain why $f^{-1}$ exists.
- Find the rule of $f^{-1}$.
- Determine the domain of $f^{-1}$.

*Solution*
- It is straightforward to see that $f$ is a one-one function (left as an exercise for reader). As such, $f^{-1}$ must exist.
- To determine the rule, following the steps above gives:
    1. Let $y=2x-2$
    2. Make $x$ the subject of the equation, $$x=\frac{y+2}{2}.$$
    3. Rule of $f^{-1}$: $f^{-1}(y)=\frac{y+2}{2}$.
    4. Replacing $y$ with $x$ in the rule in (3): $f^{-1}(x)=\frac{x+2}{2}$
- $D_{f^{-1}}=R_f=\mathbb{R}$.

In conclusion, $f^{-1}:x\mapsto \frac{x+2}{2}$, $D_{f^{-1}}=R_f=\mathbb{R}$.

We will verify that $f^{-1}(x)$ defined above is indeed the inverse, by checking the composite functions $g(f(x))$ and $f(g(x))$ where $g(x)=\frac{x+2}{2}$. Note that we are not writing as $f^{-1}$ because if we are checking, we can't assume that $g$ is already the inverse of $f$.


```python
from sympy import *
from IPython.display import Math,display,Latex

# Defining the symbol for the parameters/arguments/input of the functions f,g
x = symbols('x')

# We define the function f 
class f(Function):
    @classmethod
    def eval(cls, x):
        return 2*x-2

# We define the function g 
class g(Function):
    @classmethod
    def eval(cls, x):
        return (x+2)/2

print('The rule of gf')
display(Math(f'gf(x)={ latex(g(f(x)))}'))

print('The rule of fg')
display(Math(f'fg(x)={ latex(f(g(x)))}'))
```

    The rule of gf



$\displaystyle gf(x)=x$


    The rule of fg



$\displaystyle fg(x)=x$


Since $gf(x)=x=fg(x)$, it is indeed the case that $f^{-1}(x)=g(x)=\frac{x+2}{2}$.

### Example 26
We have seen earlier that the standard logistic function $S$ is one-to-one and thus, $S$ has an inverse. 

Next, we find $S^{-1}(x)$ with Python.


```python
# Modules for algebraic manipulation and pretty printing
from sympy import *
from IPython.display import Math,display,Latex

x, y = symbols('x y')

#the solve(expr,x) method takes the parameters expression 'expr' and variable 'x' to solve the equation 'expr' = 0 for the variable 'x'
#as such as we start with y=f(x), we need to rearrage to 'y-f(x)=0' and pass in 'y-f(x)' instead 
class logistic(Function):
    @classmethod
    def eval(cls, x):
        return 1/(1+exp(-x))

logistic_inverse = solve(y-logistic(x),x)

#Finding the rule of the inverse of the logistic function
display(Math(f'x={ latex(logistic_inverse[0])}'))
```


$\displaystyle x=\log{\left(- \frac{y}{y - 1} \right)}$


Consequently, we have that $S^{-1}(x)=\ln(\frac{x}{1-x})$. The inverse of logistic function is important enough that it has a special name. It is also called the **logit** function. So, instead of calling it $S^{-1}$, we will call it $\operatorname{logit}$. So, 
$$\operatorname{logit}(x)=\ln(\frac{x}{1-x}).$$
It has many uses in data analysis and machine learning, especially in data transformations. It can be thought as type of function that maps probability values from $(0,1)$ to real numbers in $\mathbb{R}$. *Readers without background in probablility might want to read up more on probability first. 

Note that:
- $R_{\operatorname{logit}}=D_S=\mathbb{R}$,
- $D_{\operatorname{logit}}=R_S=(0,1)$.

### Example 27
It is given that $f:x\mapsto 2x^2+3x$, $x\geq 0$. Find $f^{-1}$ in a similar form. 

*Solution*

We will use Python to help us find the rule of $f^{-1}(x)$.


```python
# Modules for algebraic manipulation and pretty printing
from sympy import *
from IPython.display import Math,display,Latex

x, y = symbols('x y')

#the solve(expr,x) method takes the parameters expression 'expr' and variable 'x' to solve the equation 'expr' = 0 for the variable 'x'
#as such as we start with y=f(x), we need to rearrage to 'y-f(x)=0' and pass in 'y-f(x)' instead 
expr = 2*(x**2)+3*x
f_inverse = solve(y-expr,x)

#Note that there are 2 solutions in f_inverse and we need to reject the 
#appropriate one    
for i in range(2):
    display(Math(f'x={ latex(f_inverse[i])}'))
```


$\displaystyle x=- \frac{\sqrt{8 y + 9}}{4} - \frac{3}{4}$



$\displaystyle x=\frac{\sqrt{8 y + 9}}{4} - \frac{3}{4}$


At this point, we have that either:
1. $x=-\frac{3}{4}+\frac{\sqrt{8y+9}}{4}$, OR
2. $x=-\frac{3}{4}-\frac{\sqrt{8y+9}}{4}$.

However, it cannot be both as $f^{-1}$ will no longer be a function (why?). 

To determine the correct one to accept, we observe that the question says that for $x$ to be in $D_f$, $x\geq 0$, as such (2) has to be rejected as $x\leq 0$ in such case.

Furthermore, $D_{f^{-1}}=R_f=[0,\infty)$. (check)

Consequently, $f^{-1}(x)=-\frac{3}{4}+\frac{\sqrt{8x+9}}{4}$ where $D_{f^{-1}}=[0,\infty)$.

### Example 28  Specimen Paper / II / Q1(ii) modified ( Restriction of Domain for Inverse to Exist)
The function $f$ is defined as follows: 
$$ f:x \mapsto 3\cos x-2\sin x,x\in \mathbb{R}, -\pi \leq x <\pi.$$

If the domain of $f$ is further restricted to $[a,b]$, the function $f^{-1}$ exists. Write down the largest value of $b$ for which $f^{-1}$  exists, where $a$ is a negative real constant. Find $f^{-1}(x)$.

*Solution*
We first plot the function in the given interval.


```python
from sympy.calculus.util import *

x = symbols('x')

# We define the function f 
class f(Function):
    @classmethod
    def eval(cls, x):
        return 3*cos(x)-2*sin(x)

print(plot(f(x),(x,-pi,pi)))
```


```python
#Find the stationary points of the function 
stat_points = stationary_points(f(x), x, ivl)

print(f'The coordinates of the maximum and minimum points, in this order, is/are')
for i in stat_points:
    display(Math(f'({latex(i)},{latex(f(i))})'))
```

    The coordinates of the maximum and minimum points, in this order, is/are



$\displaystyle (- \operatorname{atan}{\left(\frac{2}{3} \right)},\sqrt{13})$



$\displaystyle (\pi - \operatorname{atan}{\left(\frac{2}{3} \right)},- \sqrt{13})$


From the graph, we see that the local minimum and maximum values of the function occur at $x=−\arctan(\frac{3}{2})$ and $x=\pi−\arctan(\frac{3}{2})$ respectively.

Since $a$ is a negative real constant, the largest value of $b$ for which $f^{-1}$ exists must be the $x$-coordinate of the minimum point i.e. $b= \pi -\tan^{-1}(\frac{2}{3})$.

Next, we find the rule of $f^{-1}(x)$.


```python
from sympy import *
from IPython.display import Math,display,Latex

x, y = symbols('x y')

# Making x the subject
expr = 3*cos(x)-2*sin(x)
f_inverse = solve(y-expr,x)

# We use try-except block in case there's only 1 value
# Generally, A level functions don't have more than 2 possible relation for the inverse
try:    
    for i in range(2):
        display(Math(f'x={ latex(f_inverse[i])}'))
except:
    pass
```


$\displaystyle x=2 \operatorname{atan}{\left(\frac{\sqrt{13 - y^{2}} - 2}{y + 3} \right)}$



$\displaystyle x=- 2 \operatorname{atan}{\left(\frac{\sqrt{13 - y^{2}} + 2}{y + 3} \right)}$


Since $a\leq x \leq b=\pi−\arctan(\frac{3}{2})$, by checking the graph of the $y=2\tan^{-1}\left(\frac{\sqrt{13-x^{2}}-2}{x+3}\right)$ and $y=-2\tan^{-1}\left(\frac{\sqrt{13-x^{2}}-2}{x+3}\right)$, we conclude that $$x=2\tan^{-1}\left(\frac{\sqrt{13-y^{2}}-2}{y+3}\right).$$

And, thus, $f^{-1}(x)=2\tan^{-1}\left(\frac{\sqrt{13-x^{2}}-2}{x+3}\right)$.

*Alternative Solution with R formula*

Note that, from R formula, we can write
$$\begin{align*}
f\left(x\right)	&=3\cos x-2\sin x \\
	&=\sqrt{3^{2}+2^{2}}\cos\left(x+\tan^{-1}\left(\frac{2}{3}\right)\right) \\
	&=\sqrt{13}\cos\left(x+0.588\right).
\end{align*}$$

As such, $f^{-1}(x)=\cos^{-1}\left(\frac{x}{\sqrt{13}}\right)-0.588$. (Left as exercise for reader)

## 7.3 Composition of a 1-1 Function with its Inverse
Recall that the inverse function of $f$ *reverses* the mapping done by the function $f$. As such, if $f^{-1}(x)$ is applied to $f(x)$, then we will get $x$. In other words, we obtain $f^{-1}f(x)=x$  without performing any algebraic manipulation. Similarly, we can also deduce that $f^{-1}f(x)=x$.

However, even though the rules of $f^{-1}f(x)$ and $f^{-1}f(x)$ both simplifies to $x$. We **cannot conclude** that the two functions $ff^{-1}$ and $f^{-1}f$ are the same function as their domains might differ. Note that both $ff^{-1}$ and $f^{-1}f$ are just composite functions and thus, 
$$\begin{align*}
    D_{ff^{-1}}&=D_{f^{-1}}\\
    D_{f^{-1}f}&=D_{f}
\end{align*}$$

and there's no guarantee that they will be the same. Consequently, the two functions need not be equal even though they share the same rule.

### Example 29
It is given that $f$ is a one-to-one function defined by $f:x\mapsto 2x+3$, $-2\leq x\leq 2$. Find
- $f^{-1}f$
- $ff^{-1}$

Sketch the graphs of $y=f^{-1}f(x)$ and $y=ff^{-1}$ on a single diagram.
Hence solve $ f^{-1}f(x) = ff^{-1}(x)$.

*Solution*

We note that 
- $f^{-1}f(x)=x$, $D_{f^{-1}f}=D_f=[-2,2]$
- $f(x)f^{-1}=x$, $D_{ff^{-1}}=D_{f^{-1}}=R_f=[-1,7]$


```python
from sympy.calculus.util import *

x = symbols('x')

# We define the function ff^{-1} 
class f_f_inv(Function):
    @classmethod
    def eval(cls, x):
        return x

# We define the function f^{-1}f 
class f_inv_f(Function):
    @classmethod
    def eval(cls, x):
        return x

#Plotting ff^{-1}, blue
plot(f_f_inv(x),(x,-1,7),xlim=(-10,10),ylim=(-10,10))

#Plotting f^{-1}f, red
plot(f_inv_f(x),(x,-2,2),xlim=(-10,10),ylim=(-10,10),line_color='red')
```

We see that that the two functions are indeed different. Since the question wants both of them sketched on the same diagram. 

<center>



</center>

From the diagram, it is easy then to read that when $ f^{-1}f(x) = ff^{-1}(x)$, the two graphs overlaps, which happens when $-1\leq x\leq 2$.

### Example 30
The function $f$ is defined by
$$ f:x\mapsto \frac{3x}{2x-3}, \text{ for }x\in \mathbb{R},x\neq \frac{3}{2}.$$
- **(i)**	Find $f^{-1}(x)$. Hence or otherwise find $f^{2}(x)$ and state the range of $f^{2}$.
- **(ii)**	Evaluate $f^{2019}(5)$.

*Solution*

We first find the rule of $f^{-1}$.


```python
from sympy import *
from IPython.display import Math,display,Latex

x, y = symbols('x y')

#defining f
class f(Function):
    @classmethod
    def eval(cls, x):
        return (3*x)/(2*x-3)

# Making x the subject
f_inverse = solve(y-f(x),x)

# We use try-except block in case there's only 1 value
# Generally, A level functions don't have more than 2 possible relation for the inverse
try:    
    for i in range(2):
        display(Math(f'x={ latex(f_inverse[i])}'))
except:
    pass
```


$\displaystyle x=\frac{3 y}{2 y - 3}$


So, we see that $f^{-1}(x)=\frac{3x}{2x-3}=f(x)$. Consequently, 

$$\begin{align*}
    f^{-1}(x)&=f(x) \\
    f(f^{-1}(x))&=f(f(x)) \\
    x & =f^{2}(x).
\end{align*}$$

Since $D_{f^2}=D_{ff}=D_{f}=\mathbb{R}\backslash \{\frac{3}{2}\}$, and the rule $f^2(x)$ is just $x$, we have $R_{f^2}=\mathbb{R}\backslash \{\frac{3}{2}\}$.

- (ii) If we're bruteforcing this and doing it in a non-smart way, we see that we need to apply the function $f$ 2019 times.


```python
from sympy import Function

x = symbols('x')

# We need to do the following to define the function f in the question
class f(Function):
    @classmethod
    def eval(cls, x):
        return (3*x)/(2*x-3)

composite_function = x

for n in range(2019):
    #Note that we simplify here because otherwise, the expression just gets
    #into a bigger and bigger nest
    composite_function = simplify(f(composite_function))

print('Checking the composition of function')
display(Math(f'f^{{2019}}(x)={ latex(composite_function)}'))

print('Evaluating at x=5')
display(Math(f'f^{{2019}}(5)={ latex(composite_function.subs(x,4))}'))
```

    Checking the composition of function



$\displaystyle f^{2019}(x)=\frac{3 x}{2 x - 3}$


    Evaluating at x=5



$\displaystyle f^{2019}(5)=\frac{12}{5}$


On my machine this takes ~5 seconds to run. However, we can actually do this in a smarter way. 

In the previous part, we have shown that $f^{2}(x)=x$. Consequently, we observe that repeated application of $f$ on both sides of the equation gives,
$$\begin{align*}
f(x)&=f^{3}(x)=f^{5}(x)=f^{7}(x)=\cdots \\
x&=f^2(x)=f^{4}(x)=f^{6}(x)=f^{8}(x)=\cdots
\end{align*}$$

In other words, if $f^2(x) = x$, $$ f^{n}\left(x\right)=\begin{cases}
x & ,\,\text{if }n\,\text{is even}\\
f\left(x\right) & ,\,\text{if }n\,\text{is odd}
\end{cases} $$

Therefore, the working for this question can be simplified to 

$$\begin{align*}
f^{2019}\left(x\right)	&=f\left(f^{2018}\left(x\right)\right) \\
	&=f\left(x\right) \\
	&=\frac{3x}{2x-3} \\
f^{2019}\left(5\right)	&=\frac{3\left(5\right)}{2\left(5\right)-3} \\
	&=\frac{15}{7}.
\end{align*}$$

This is definitely much better than just bruteforcing it! :D

### Example 31
It is given that
$$\begin{align*}
f:x&\mapsto  e^x&, x\in \mathbb{R}^{-}, \\
g:x&\mapsto -x^2&, x\in \mathbb{R}^{+}.

\end{align*}$$
Find $fg(x)$  and hence find the exact value of $(fg)^{-1}(0.5)$ .

*Solution*


```python
from sympy import *
from IPython.display import Math,display,Latex

x = symbols('x')

# We define the function f 
class f(Function):
    @classmethod
    def eval(cls, x):
        return exp(x)

# We define the function g 
class g(Function):
    @classmethod
    def eval(cls, x):
        return -x**2

print('The rule of fg')
#fg(x)
display(Math(f'fg(x)={ latex(f(g(x)))}'))

```

    The rule of fg



$\displaystyle fg(x)=e^{- x^{2}}$


Since $fg$ is a function, to find the exact value of $(fg)^{-1}(0.5)$, it is natural to consider finding $(fg)^{-1}(x)$ first and then, plugging in the value of 0.5 to get the result. 

However, we will first propose a better way to do this via the property of composition of a function and it's inverse. We see that 

Suppose $(fg)^{-1}(0.5)$ evaluates to $a$. Then,

$$\begin{align*}
\left(fg\right)^{-1}\left(0.5\right)&=a\\fg\left(\left(fg\right)^{-1}\left(0.5\right)\right)
&=fg\left(a\right)\\
0.5&=e^{-a^{2}}
\end{align*}$$


```python
from sympy import *
from IPython.display import Math,display,Latex

a = symbols('a')

#Defining a function for the expression on the RHS of the equation
class f(Function):
    @classmethod
    def eval(cls, x):
        return exp(-x**2)

# Solving for a
soln = solve(0.5-f(a),a)

try:    
    for i in range(2):
        display(Math(f'a={ latex(soln[i])}'))
except:
    pass
```


$\displaystyle a=-0.832554611157698$



$\displaystyle a=0.832554611157698$


Since $D_{fg}=\mathbb{R^{+}}$, $a>0$ and thus, $a=0.832455.. =\sqrt{\ln 2}$ (How do we know it's equal to $\sqrt{\ln 2}$?) and reject $a=-\sqrt{\ln 2}$. 
> Note that sympy doesn't give exact answer. So, to satisfy the question requirement, we could work from the equation $0.5=e^{-a^2}$ and do the algebraic manipulation by hand.

# 8 Closing Remarks
In this chapter, we see how functions formalize our notion of linking different objects together. We also see ways to create function based on existing functions by composing them together. This process of chaining one function to another is a very powerful concept and should feel natural. The important thing to note is to make sure the sequence of mapping could hold by ensuring the values are valid when being passed from one function to another. 

We also saw the notion of inverse function, which is a nice way to capture the idea of being able to go back and forth easily between two sets of objects.

In A-Level, the objects linked are real valued quantities. However, the concept of a mathematical function is easily generalizable to cover more type of objects. E.g., we can think of a mapping between the set of English words to the set of Chinese characters (think dictionary) and neither sets are real valued quantities. 

In the subsequent chapters, we will try to include some of such examples where appropriate.


c86c9062e806d94caf5a901de1f46b82e556e7bf
ipynb
Jupyter Notebook
Notes/NOTE_01_Functions.ipynb
beertino/H2_math_w_python
322169987bc3f3b46d4c5ba48db54ab6f6224ccf
MIT
Notes/NOTE_01_Functions.ipynb
beertino/H2_math_w_python
322169987bc3f3b46d4c5ba48db54ab6f6224ccf
MIT
Notes/NOTE_01_Functions.ipynb
beertino/H2_math_w_python
322169987bc3f3b46d4c5ba48db54ab6f6224ccf
MIT
Qwen/Qwen-72B
1. YES
2. YES