Math Homework #1¶
Objective¶
Using 1 Dimensional Vector math, given a function definition in Python for celsius to kelvin and for fahrenheit to celsius, implement in Python
fahrenheit to kelvin
celsius to fahrenheit
kelvin to fahrenheit
This book provides a math library in Python. We import them here.
We’ll use pytest’s approx
method to test if two floating point values are close enough to the same
value. We import a type InvertibleFunction,
compose,
inverse
Important note. The links in the previous paragraphs are links to API documentation. API documentation is like a guidebook that explains how to use a library. It tells you what functions, classes, and modules are available, what inputs they require, what they return, and examples of how to use them correctly. Instead of guessing or relying on scattered internet posts, the API gives you the most accurate and up to date information straight from the source. For your assignments (and your future work), clicking on the API docs will save you time, help you avoid mistakes and show you features that you might not realize exists.
47import warnings
48
49import modelviewprojection.mathutils1d as mu1d
50
51# turn warnings into exceptions
52warnings.filterwarnings("error", category=RuntimeWarning)
53
We can do addition on Vector1D using “+”, __add__
60mu1d.Vector1D(x=1.0) + mu1d.Vector1D(x=3.0)
We can do subtraction on Vector1D using “-”, __sub__
65mu1d.Vector1D(x=5.0) - mu1d.Vector1D(x=1.0)
We can do multiply a scalar by Vector1D using “*”, __mul__
704.0 * mu1d.Vector1D(x=2.0)
We can do negate a Vector1D using “-”, __neg__
75-mu1d.Vector1D(x=2.0)
Translate Implementation¶
Next we have a very import function, translate. Read
the API documentation in the link, it’s a very important function.
Translate is a function which partially binds a constant Vector1D to
one of the arguments of __add__, thus creating
a new function of one argument.
In high school math, you’d learn about classes of functions, such as affine functions that follow the pattern \(f(x) = m \times x + b\). You were told that \(m\) and \(b\) were constant.
You could recognize \(f(x) = 2 \times x + 3\) as being an affine function where \(m=2\) and \(b=3\). You could recognize \(f(x) = 5 \times x + 0\) as being an affine function where \(m=5\) and \(b=0\). You could recognize \(f(x) = x \times x\) as not being affine, although it’s implicit that \(b=0\), there is no constant times \(x\) But could you generate a new function for a given \(m\) and given \(b\)?
Perhaps you could use notation such as \(f_{m=2,b=3}(x)\) to be a function \(2 \times x + 3\), or \(f(x; m=2,b=3)\) to be a function \(2 \times x + 3\).
We will use the following notation for translate, \(T_{b}(x) = x + b\), where if we specify a constant \(b\), it will be notated as \(T_{b=3}\) equals an expression \(x + 3\).
Here, we call the translate function to create a new function, named “fn”, notated \(T_{b=2}\), which is a function of a variable \(x\), and a constant 2, \(T_{b=2}(x) = x + 2\).
Usage (Black Box)¶
87fn: mu1d.InvertibleFunction = mu1d.translate(mu1d.Vector1D(2.0))
Now that we’ve generated a function using translate, we can now apply it, 0, 1, or many times.
96assert fn(mu1d.Vector1D(0)) == mu1d.Vector1D(2.0)
97assert fn(mu1d.Vector1D(1)) == mu1d.Vector1D(3.0)
98assert fn(mu1d.Vector1D(5)) == mu1d.Vector1D(7.0)
Inverting such a function is done by negating \(b\), so (\({T_{b=2}}^{-1} \circ {T_{b=2}}) (x) = ({T_{b=-2}} \circ {T_{b=2}}) (x) = x\)
To get the inverse in Python, we can call the inverse function
on our function, without having to worry about how it’s implemented.
104assert mu1d.inverse(fn)(mu1d.Vector1D(2)) == mu1d.Vector1D(0.0)
105assert mu1d.inverse(fn)(mu1d.Vector1D(3)) == mu1d.Vector1D(1.0)
106assert mu1d.inverse(fn)(mu1d.Vector1D(7)) == mu1d.Vector1D(5.0)
What’s nice about that is we can look at the implementation of
translate
once, understand how it works internally, and then forget those details and treat
it as an invertible BlackBox.
Definition (White Box)¶
522def translate(b: Vector) -> InvertibleFunction:
523 def f(vector: Vector) -> Vector:
524 return vector + b
525
526 def f_inv(vector: Vector) -> Vector:
527 return vector - b
528
529 values = dataclasses.astuple(b)
530 tex_str: str = (
531 f"T_{{<[{str(values[0]) if len(values) == 1 else str(values)[1:-1]}]>}}"
532 )
533 negative_values = dataclasses.astuple(-b)
534 inv_str: str = f"T_{{<[{str(negative_values[0]) if len(negative_values) == 1 else str(negative_values)[1:-1]}]>}}"
535 return InvertibleFunction(f, f_inv, tex_str, inv_str)
Function Composition¶
Similarly to how we defined \(T_{b}(x) = x + b\) for adding a constant \(b\), we can define a “scaling” function \(S_{m}(x) = m \times x\). We can use function composition of a partially bound \(S\) and partially bound \(T\) to generate new instances of \(f(x) = m \times x + b\)
\(f(x) = {m}{x} + b = T_{b=2} \circ S_{m=5}\)
111m: float = 5.0
112b: float = 2.0
113fn: mu1d.InvertibleFunction = mu1d.compose(
114 [mu1d.translate(mu1d.Vector1D(b)), mu1d.uniform_scale(m)]
115)
116print(fn(mu1d.Vector1D(0.0)))
117print(fn(mu1d.Vector1D(1.0)))
118
119assert fn(mu1d.Vector1D(0.0)) == mu1d.Vector1D(2.0)
120assert fn(mu1d.Vector1D(1.0)) == mu1d.Vector1D(7.0)
Assignment¶
Provided functions¶
In Temperature Conversion you were provided definitions of functions to convert between fahrenheit, celsius, and kelvin. Provided to you are Python implementations of three of those functions
135celsius_to_kelvin: mu1d.InvertibleFunction = mu1d.translate(
136 mu1d.Vector1D(273.15)
137)
138assert celsius_to_kelvin(mu1d.Vector1D(0.0)).isclose(mu1d.Vector1D(273.15))
139
140assert celsius_to_kelvin(mu1d.Vector1D(100.0)).isclose(mu1d.Vector1D(373.15))
141
142
143fahrenheit_to_celsius: mu1d.InvertibleFunction = mu1d.compose(
144 [mu1d.uniform_scale(5.0 / 9.0), mu1d.translate(mu1d.Vector1D(-32.0))]
145)
146assert fahrenheit_to_celsius(mu1d.Vector1D(32.0)).isclose(mu1d.Vector1D(0.0))
147
148assert fahrenheit_to_celsius(mu1d.Vector1D(212.0)).isclose(mu1d.Vector1D(100.0))
149
150
151kelvin_to_celsius: mu1d.InvertibleFunction = mu1d.inverse(celsius_to_kelvin)
152assert kelvin_to_celsius(mu1d.Vector1D(273.15)).isclose(mu1d.Vector1D(0.0))
153assert kelvin_to_celsius(mu1d.Vector1D(373.15)).isclose(mu1d.Vector1D(100.0))
Functions to implement¶
Your task is to modify the three functions below so that the asserts all pass
167fahrenheit_to_kelvin: mu1d.InvertibleFunction = mu1d.translate(
168 mu1d.Vector1D(0.0)
169)
170assert fahrenheit_to_kelvin(mu1d.Vector1D(32.0)).isclose(mu1d.Vector1D(273.15))
171assert fahrenheit_to_kelvin(mu1d.Vector1D(212.0)).isclose(mu1d.Vector1D(373.15))
172
173celsius_to_fahrenheit: mu1d.InvertibleFunction = mu1d.translate(
174 mu1d.Vector1D(0.0)
175)
176assert celsius_to_fahrenheit(mu1d.Vector1D(0.0)).isclose(mu1d.Vector1D(32.0))
177
178assert celsius_to_fahrenheit(mu1d.Vector1D(100.0)).isclose(mu1d.Vector1D(212.0))
179
180
181kelvin_to_fahrenheit: mu1d.InvertibleFunction = mu1d.translate(
182 mu1d.Vector1D(0.0)
183)
184assert kelvin_to_fahrenheit(mu1d.Vector1D(273.15)).isclose(mu1d.Vector1D(32.0))
185assert kelvin_to_fahrenheit(mu1d.Vector1D(373.15)).isclose(mu1d.Vector1D(212.0))