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 modelviewprojection.mathutils.InvertibleFunction
,
modelviewprojection.mathutils.compose()
, modelviewprojection.mathutils.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.
45import pytest
46
47import modelviewprojection.mathutils as mu
48import modelviewprojection.mathutils1d as mu1d
49import warnings
50
51# turn warnings into exceptions
52warnings.filterwarnings("error", category=RuntimeWarning)
53
We can do addition on modelviewprojection.mathutils1d.Vector1D
using “+”, modelviewprojection.mathutils1d.Vector1D.__add__()
60mu1d.Vector1D(x=1.0) + mu1d.Vector1D(x=3.0)
We can do subtraction on modelviewprojection.mathutils1d.Vector1D
using “-”, modelviewprojection.mathutils1d.Vector1D.__sub__()
65mu1d.Vector1D(x=5.0) - mu1d.Vector1D(x=1.0)
We can do multiply a scalar by modelviewprojection.mathutils1d.Vector1D
using “*”, modelviewprojection.mathutils1d.Vector1D.__mul__()
704.0 * mu1d.Vector1D(x=2.0)
We can do negate a modelviewprojection.mathutils1d.Vector1D
using “-”, modelviewprojection.mathutils1d.Vector1D.__neg__()
75-mu1d.Vector1D(x=2.0)
Translate Implementation¶
Next we have a very import function, modelviewprojection.mathutils1d.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 Vector1D.__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 folliwng 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: mu.InvertibleFunction[mu1d.Vector1D] = mu.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 modelviewprojection.mathutils.inverse()
function
on our function, without having to worry about how it’s implemented.
What’s nice about that is we can look at the implementation of modelviewprojection.mathutils1d.translate()
once, understand how it works internally, and then forget those details and treat
it as an invertible BlackBox.
Definition (White Box)¶
246def translate(b: T) -> InvertibleFunction[T]:
247 def f(vector: T) -> T:
248 return vector + b
249
250 def f_inv(vector: T) -> T:
251 return vector - b
252
253 return InvertibleFunction[T](f, f_inv)
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: mu.InvertibleFunction[mu1d.Vector1D] = mu.compose(
114 [mu.translate(mu1d.Vector1D(b)), mu.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: mu.InvertibleFunction[mu1d.Vector1D] = mu.translate(
136 mu1d.Vector1D(273.15)
137)
138assert celsius_to_kelvin(mu1d.Vector1D(0.0)) == mu1d.Vector1D(
139 pytest.approx(273.15)
140)
141assert celsius_to_kelvin(mu1d.Vector1D(100.0)) == mu1d.Vector1D(
142 pytest.approx(373.15)
143)
144
145
146fahrenheit_to_celsius: mu.InvertibleFunction[mu1d.Vector1D] = mu.compose(
147 [mu.uniform_scale(5.0 / 9.0), mu.translate(mu1d.Vector1D(-32.0))]
148)
149assert fahrenheit_to_celsius(mu1d.Vector1D(32.0)) == mu1d.Vector1D(
150 pytest.approx(0.0)
151)
152assert fahrenheit_to_celsius(mu1d.Vector1D(212.0)) == mu1d.Vector1D(
153 pytest.approx(100.0)
154)
155
156
157kelvin_to_celsius: mu.InvertibleFunction[mu1d.Vector1D] = mu.inverse(
158 celsius_to_kelvin
159)
160assert kelvin_to_celsius(mu1d.Vector1D(273.15)) == mu1d.Vector1D(
161 pytest.approx(0.0)
162)
163assert kelvin_to_celsius(mu1d.Vector1D(373.15)) == mu1d.Vector1D(
164 pytest.approx(100.0)
165)
Functions to implement¶
Your task is to modify the three functions below so that the asserts all pass
178fahrenheit_to_kelvin: mu.InvertibleFunction[mu1d.Vector1D] = mu.translate(
179 mu1d.Vector1D(0.0)
180)
181assert fahrenheit_to_kelvin(mu1d.Vector1D(32.0)) == mu1d.Vector1D(
182 pytest.approx(273.15)
183)
184assert fahrenheit_to_kelvin(mu1d.Vector1D(212.0)) == mu1d.Vector1D(
185 pytest.approx(373.15)
186)
187
188celsius_to_fahrenheit: mu.InvertibleFunction[mu1d.Vector1D] = mu.translate(
189 mu1d.Vector1D(0.0)
190)
191assert celsius_to_fahrenheit(mu1d.Vector1D(0.0)) == mu1d.Vector1D(
192 pytest.approx(32.0)
193)
194assert celsius_to_fahrenheit(mu1d.Vector1D(100.0)) == mu1d.Vector1D(
195 pytest.approx(212.0)
196)
197
198
199kelvin_to_fahrenheit: mu.InvertibleFunction[mu1d.Vector1D] = mu.translate(
200 mu1d.Vector1D(0.0)
201)
202assert kelvin_to_fahrenheit(mu1d.Vector1D(273.15)) == mu1d.Vector1D(
203 pytest.approx(32.0)
204)
205assert kelvin_to_fahrenheit(mu1d.Vector1D(373.15)) == mu1d.Vector1D(
206 pytest.approx(212.0)
207)