Skip to content

Commit d30a374

Browse files
author
Oskar Lundström
committed
2 parents e610336 + e45996b commit d30a374

File tree

9 files changed

+279
-254
lines changed

9 files changed

+279
-254
lines changed

Book/build.py

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import os
99
import shutil
10+
from urllib.parse import quote
1011
from subprocess import Popen, PIPE
1112

1213
def read_file(filepath):
@@ -54,7 +55,7 @@ def build_sections(sources):
5455
next_chap_name = "Table of contents"
5556
if (i + 1) < len(chapters):
5657
next_section, next_chap_name, _ = chapters[i + 1]
57-
next_chap_href = "../{}/{}.html".format(next_section, next_chap_name)
58+
next_chap_href = "../{}/{}.html".format(quote(next_section, safe=''), quote(next_chap_name, safe=''))
5859
if not os.path.exists(section):
5960
os.makedirs(section)
6061
chapter_path = "../../" + chapter_source
@@ -75,10 +76,11 @@ def build_sections(sources):
7576
"lhs-source-href": lhs_source_href,
7677
"lhs-source-name": lhs_source_name,
7778
})
78-
out_path = "{}/{}.html".format(section, chapter_name)
79+
out_path = "{}/{}.html".format(section, chapter_name)
80+
out_path_u = "{}/{}.html".format(quote(section, safe=''), quote(chapter_name, safe=''))
7981
write_string_to_file(chapter, out_path)
8082
copy_images(os.path.dirname(chapter_path), section)
81-
prev_chap_href = "../" + out_path
83+
prev_chap_href = "../" + out_path_u
8284
prev_chap_name = chapter_name
8385

8486
def build_index(sources):
@@ -88,7 +90,7 @@ def build_index(sources):
8890
toc += "<div>" + section_name + "</div>\n"
8991
toc += "<ul>\n"
9092
for (chapter_name, _) in chapter_sources:
91-
chapter_path = "{}/{}.html".format(section_name, chapter_name)
93+
chapter_path = "{}/{}.html".format(quote(section_name, safe=''), quote(chapter_name, safe=''))
9294
toc += "<li><a href=\"{}\">{}</a></li>\n".format(chapter_path, chapter_name)
9395
toc += "</ul>\n</li>\n"
9496
index_template = open_template("index")
@@ -97,17 +99,19 @@ def build_index(sources):
9799

98100
sources = [
99101
("Introduction", [
100-
("Introduction'", "Physics/src/Introduction/Introduction.lhs"),
102+
("About this book", "Physics/src/Introduction/About.lhs"),
103+
("So what's a DSL?", "Physics/src/Introduction/WhatIsADsl.lhs"),
104+
("Getting started", "Physics/src/Introduction/GettingStarted.lhs"),
101105
]),
102106
("Calculus", [
103107
("Introduction", "Physics/src/Calculus/Intro.lhs"),
104108
("Function expressions", "Physics/src/Calculus/FunExpr.lhs"),
105109
("Differential calculus", "Physics/src/Calculus/DifferentialCalc.lhs"),
106110
("Integral calculus", "Physics/src/Calculus/IntegralCalc.lhs"),
107-
("Visualization", "Physics/src/Calculus/VisVerApp.lhs"),
111+
("Plotting graphs", "Physics/src/Calculus/VisVerApp.lhs"),
108112
]),
109-
("Vectors", [
110-
("Vector", "Physics/src/Vector/Vector.lhs")
113+
("Linear algebra", [
114+
("Vectors", "Physics/src/Vector/Vector.lhs")
111115
]),
112116
("Dimensions", [
113117
("Introduction", "Physics/src/Dimensions/Intro.lhs"),
@@ -116,16 +120,14 @@ def build_index(sources):
116120
"Physics/src/Dimensions/ValueLevel/Test.lhs"),
117121
("Type-level dimensions", "Physics/src/Dimensions/TypeLevel.lhs"),
118122
("Quantities", "Physics/src/Dimensions/Quantity.lhs"),
119-
("Testing of Quantity",
123+
("Testing of Quantities",
120124
"Physics/src/Dimensions/Quantity/Test.lhs"),
121125
("Usage", "Physics/src/Dimensions/Usage.lhs"),
122-
]),
123-
("Examples", [
124-
("Gungbräda", "Physics/src/Examples/Gungbraeda.lhs"),
125-
("krafter på lådor", "Physics/src/Examples/krafter_pa_lador.lhs"),
126-
127126
])
128-
127+
# ("Examples", [
128+
# ("Gungbräda", "Physics/src/Examples/Gungbraeda.lhs"),
129+
# ("krafter på lådor", "Physics/src/Examples/krafter_pa_lador.lhs"),
130+
# ])
129131
]
130132

131133
if not os.path.exists("build"):

Physics/src/Calculus/DifferentialCalc.lhs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ to derive these bad boys when we haven't even covered them yet! We'll
365365
prove why this works later, but for now, just know that another name
366366
for integral is *Antiderivative*...
367367

368-
> derive (I c f) = f
368+
> derive (I f) = f
369369

370370
</div>
371371
</details>
@@ -492,7 +492,7 @@ Intuitively, the identity function is the identity element for function composit
492492

493493
> simplify (Delta h f) = Delta h (simplify f)
494494
> simplify (D f) = D (simplify f)
495-
> simplify (I c f) = I c (simplify f)
495+
> simplify (I f) = I (simplify f)
496496
> simplify f = f
497497

498498
With this new function, many expressions become much more readable!

Physics/src/Calculus/FunExpr.lhs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ define the syntax for them as
111111

112112
> | Delta RealNum FunExpr
113113
> | D FunExpr
114-
> | I RealNum FunExpr
114+
> | I FunExpr
115115

116116
Even more finally, we add a `deriving` modifier to automatically allow
117117
for equality tests between `FunExpr`s.
@@ -157,7 +157,7 @@ Try modifying `FunExpr` to derive `Show`, so that our expressions can be printed
157157

158158
< deriving Eq, Show
159159

160-
Consider now how GHCI prints out a function expression we create
160+
Consider now how GHCi prints out a function expression we create
161161

162162
< ghci> carAccel = Const 20
163163
< ghci> carSpeed = Const 50 :+ carAccel :* Id
@@ -186,7 +186,7 @@ So if the `Show` is bad, we'll just have to make our own `Show`!
186186
> show (f :. g) = "(" ++ show f ++ " . " ++ show g ++ ")"
187187
> show (Delta h f) = "(delta_" ++ showReal h ++ " " ++ show f ++ ")"
188188
> show (D f) = "(D " ++ show f ++ ")"
189-
> show (I c f) = "(I at " ++ show c ++ " for " ++ show f ++ ")"
189+
> show (I f) = "(I " ++ show f ++ ")"
190190
>
191191
> showReal x = if isInt x then show (round x) else show x
192192
> where isInt x = x == fromInteger (round x)
@@ -228,7 +228,7 @@ generated expressions in complexity.
228228
> , (10, genBinaryApp (:.))
229229
> , (5 , genBinaryApp Delta)
230230
> , (5 , fmap D arbitrary)
231-
> , (5 , genBinaryApp I) ]
231+
> , (5 , fmap I arbitrary) ]
232232
> where genElementary = elements [Exp, Log, Sin, Cos, Asin, Acos]
233233
> genBinaryApp op = fmap (\(f, g) -> f `op` g) arbitrary
234234
> genBinaryOperation = elements [(:+), (:-), (:*), (:/), (:^)]

Physics/src/Calculus/IntegralCalc.lhs

Lines changed: 87 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ that's our the type of our semantics!). So, we simply say that when
116116
integrating a function, the constant term must be supplied in order to
117117
nail the result down to a single function!
118118

119-
< | I RealNum FunExpr
119+
< | I FunExpr
120120

121121

122122

@@ -265,66 +265,67 @@ Let's implement these rules as a function for symbolic (indefinite)
265265
integration of functions. We'll start with the nicer cases, and
266266
progress to the not so nice ones.
267267

268-
`integrate` takes a function to symbolically integrate, and a real
269-
number that decides the vertical offset (i.e. the value of $f(0)$ /
270-
the constant term) of the function. The antiderivative with the given
271-
vertical offset is returned.
268+
`integrate` takes a function to symbolically integrate, and returns
269+
the antiderivative where $F(0) = 0$.
272270

273271
Important to note is that not all functions are integrable. Unlike
274272
derivatives, some antiderivatives of elementary functions simply
275273
cannot be expressed as elementary functions themselves, according to
276274
[Liouville's
277275
theorem](https://en.wikipedia.org/wiki/Liouville%27s_theorem_%28differential_algebra%29). Some examples include $e^{-x^2}$, $\frac{sin(x)}{x}$, and $x^x$.
278276

279-
> integrate :: FunExpr -> RealNum -> FunExpr
277+
> integrate :: FunExpr -> FunExpr
280278

281279
First, our elementary functions. You can prove them using the methods
282280
described above, but the easiest way to find them is to just look them
283281
up in some table of integrals (dust of that old calculus cheat sheet)
284282
or on WolframAlpha (or Wikipedia, or whatever. Up to you).
285283

286-
> integrate Exp c = Exp :+ Const c
287-
> integrate Log c = Id :* (Log :- Const 1) :+ Const c
288-
> integrate Sin c = Const 0 :- Cos :+ Const c
289-
> integrate Cos c = Sin :+ Const c
290-
> integrate Asin c = (Const 1 :- Id:^(Const 2)):^(Const 0.5)
291-
> :+ Id :* Asin
292-
> :+ Const c
293-
> integrate Acos c = Id :* Acos
294-
> :- (Const 1 :- Id:^(Const 2)):^(Const 0.5)
295-
> :+ Const c
284+
> integrate Exp = Exp :- Const 1
285+
286+
Note that $log(x)$ is not defined in $x=0$, so we don't have to add
287+
any corrective constant as $F(0)$ wouldn't make sense anyways.
288+
289+
> integrate Log = Id :* (Log :- Const 1)
290+
> integrate Sin = Const 1 :- Cos
291+
> integrate Cos = Sin
292+
> integrate Asin = (Const 1 :- Id:^(Const 2)):^(Const 0.5)
293+
> :+ Id :* Asin
294+
> :- Const 0
295+
> integrate Acos = Id :* Acos
296+
> :- (Const 1 :- Id:^(Const 2)):^(Const 0.5)
297+
> :+ Const 1
296298

297299
These two good boys. Very simple as well.
298300

299-
> integrate Id c = Id:^Const 2 :/ Const 2 :+ Const c
300-
> integrate (Const d) c = Const d :* Id :+ Const c
301+
> integrate Id = Id:^Const 2 :/ Const 2
302+
> integrate (Const d) = Const d :* Id
301303

302304
Addition and subtraction is trivial. Just use the backwards method and
303305
compare to how sums and differences are differentiated.
304306

305-
> integrate (f :+ g) c = integrate f c :+ integrate g 0
306-
> integrate (f :- g) c = integrate f c :- integrate g 0
307+
> integrate (f :+ g) = integrate f :+ integrate g
308+
> integrate (f :- g) = integrate f :- integrate g
307309

308310
Delta is easy. Just expand it to the difference that it is, and
309311
integrate.
310312

311-
> integrate (Delta h f) c = integrate (f :. (Id :+ Const h) :- f) c
313+
> integrate (Delta h f) = integrate (f :. (Id :+ Const h) :- f)
312314

313315
A derivative? That's trivial, the integration and differentiation
314-
cancel each other, right? Nope, not so simple! We have to make sure
315-
that the constant coefficient is equal to `c`, which it might not be
316-
if we just cancel the operations and add the `c`. The simplest way to
317-
solve this is to evaluate the function at $x=0$, and check the
318-
value. We then add a term that corrects the function such that
319-
$I(D(f), c)[0] = c$. As we haven't implemented an evaluator yet, you
320-
can leave this "incorrect" for now, and fix it later!
316+
cancel each other, right? Nope, not so simple! To conform to our
317+
specification of `integrate` that $F(0)=0$, we have to make sure that
318+
the constant coefficient is equal to $0$, which it might not be if we
319+
just cancel the operations. The simplest way to solve this is to
320+
evaluate the function at $x=0$, and check the value. We then subtract
321+
a term that corrects the function such that $I(D(f))[0] = 0$. We'll
322+
write a simple function `center` for this later.
321323

322-
> integrate (D f) c = f :+ Const (c - (eval f) 0) -- Correct, but requires `eval`
323-
> -- integrate (D f) c = f -- Incorrect (unless we don't care about `c`)
324+
> integrate (D f) = center f
324325

325326
Integrating an integral? Just integrate the integral!
326327

327-
> integrate (I d f) c = integrate (integrate f d) c
328+
> integrate (I f) = integrate (integrate f)
328329

329330
Aaaaaand now it starts to get complicated.
330331

@@ -333,7 +334,7 @@ not for integration. There just isn't any nice way to integrate a
333334
product that always works! The integration rule that's most analogous
334335
to the product rule for differentiation, is integration by parts:
335336

336-
$$ \int f(x) g(x) dx = f(x) G(x) - \int f'(x) g(x) dx $$
337+
$$ \int f(x) g(x) dx = f(x) G(x) - \int f'(x) G(x) dx $$
337338

338339
Hmm, this doesn't look quite as helpful as the differentiation product
339340
rule, does it? We want this rule to give us an expression of simpler
@@ -348,9 +349,9 @@ the expressions, we get a case where the integration by parts rule
348349
only makes things worse:
349350

350351
\begin{align*}
351-
\int e^x x dx = &e^x x^2 - \int e^x x dx \\
352-
= &e^x x^2 - (e^x x^2 - \int e^x x dx) \\
353-
= &e^x x^2 - (e^x x^2 - (e^x x^2 - \int e^x x dx)) \\
352+
\int e^x x dx = &e^x \frac{x^2}{2} - \int e^x \frac{x^2}{2} dx \\
353+
= &e^x \frac{x^2}{2} - (e^x \frac{x^3}{3!} - \int e^x \frac{x^3}{3} dx) \\
354+
= &e^x \frac{x^2}{2} - (e^x \frac{x^3}{3!} - (e^x \frac{x^4}{4!} - \int e^x \frac{x^4}{4!} dx)) \\
354355
= &...
355356
\end{align*}
356357

@@ -363,38 +364,40 @@ scope for this book.
363364
Further, as a consequence of Liouville's theorem, the integration by
364365
parts rule is simply not defined in the case of $g(x)$ not being
365366
integrable to $G(x)$. And so, as there exists no definitely good way
366-
to do it, we're forced to settle for a solution that works sometimes
367-
but not always. We'll define the integration of a product to use
368-
integration by parts, but before integrating we'll simplify the
369-
expression in the hopes that it will become better suited for
370-
integration.
371-
372-
> integrate (f :* g) c =
373-
> let simplified = simplify (f :* g)
374-
> in if simplified == f :* g
375-
> then f :* integrate g 0 :- integrate (derive f :* g) 0 :+ Const c
376-
> else integrate simplified c
367+
to do it in ALL cases, we're forced to settle for a conservative approach.
368+
369+
If we look at the formula for integration by parts
370+
371+
$$ \int f(x) g(x) dx = f(x) G(x) - \int f'(x) G(x) dx $$
372+
373+
We see that there are two cases where the integral is well defined:
374+
375+
$$ \int f(x) g'(x) dx = f(x) g(x) - \int f'(x) g(x) dx $$
376+
377+
and
378+
379+
$$ \int f'(x) g(x) dx = f(x) g(x) - \int f(x) g'(x) dx $$
380+
381+
I.e., if we already know the integral of one factor, we can integrate the product.
382+
383+
> integrate (D f :* g) = center (f :* g :- integrate (f :* derive g))
384+
> integrate (f :* D g) = center (f :* g :- integrate (derive f :* g))
385+
386+
Also, we can add a few cases for integrals we know, like multiplication with a constant
387+
388+
> integrate (Const c :* f) = Const c :* integrate f
377389

378390
The rule for quotients is very similar
379391

380-
> integrate (f :/ g) c =
381-
> let simplified = simplify (f :/ g)
382-
> in if simplified == f :/ g
383-
> then let _F = integrate f 0
384-
> in _F :/ g
385-
> :+ integrate (_F :* (derive g :/ (g:^Const 2))) 0
386-
> :+ Const c
387-
> else integrate simplified c
392+
> integrate (D f :/ g) =
393+
> center (f :/ g :+ integrate (f :* (D g :/ (g:^Const 2))))
388394

389395
There is no good rule for exponentials in general. Only for certain
390396
combinations of functions in the base and exponent is symbolic
391397
integration well defined. We'll only treat the special case of $x^c$,
392398
which at least implies that we can use polynomials.
393399

394-
> integrate (f :^ g) c =
395-
> case (simplify f, simplify g) of
396-
> (Id, Const c) -> Id:^(Const (c+1)) :/ Const (c+1)
397-
> (_, _) -> error "Can't integrate integrals like that!"
400+
> integrate (Id :^ Const c) = Id:^(Const (c+1)) :/ Const (c+1)
398401

399402
**Exercise.** Find more rules of integrating exponentials and add to
400403
the implementation.
@@ -416,13 +419,23 @@ differentiation. This method is tricky to implement, as the way humans
416419
execute this method is highly dependent on intuition and a mind for
417420
patterns. A brute-force solution would be possible to implement, but
418421
is out of scope for this book, and not really relevant to what we want
419-
to learn here. We'll leave integration of composition undefined.
422+
to learn here. We'll leave symbolic integration of composition undefined.
420423

421-
As long as we ensure our input functions are not composed functions,
422-
`integrate` will still be well behaved.
424+
And if we couldn't integrate the expression as is, first try
425+
simplifying it and see if we know how to integrate the new
426+
expression. If that fails, just wrap the expression in the `I`
427+
constructor, unchanged. This will signify that we don't know how to
428+
symbolically integrate the expression. During evaluation, we can use
429+
`integrateApprox` to compute the integral numerically.
423430

424-
> integrate (f :. g) c
425-
> = error "Please don't try to integrate function compositions!"
431+
> integrate f = let fsim = simplify f
432+
> in if f == fsim
433+
> then I f
434+
> else integrate fsim
435+
436+
Finally, the helper function `center` to center functions such that $f(0) = 0$.
437+
438+
> center f = f :- Const ((eval f) 0)
426439

427440

428441

@@ -477,8 +490,16 @@ Delta is just expanded to the difference that it really is
477490

478491
> eval (Delta h f) = eval (f :. (Id :+ Const h) :- f)
479492

480-
For derivatives and integrals, we apply the symbolic operations we
481-
wrote, and then evaluate the result.
493+
For derivatives, we just apply the symbolic operation we wrote, and
494+
then evaluate the result.
482495

483496
> eval (D f) = eval (derive f)
484-
> eval (I c f) = eval (integrate f c)
497+
498+
With integrals, we first symbolically integrate the expression as far
499+
as possible, then apply the numerical `integrateApprox` if we can't
500+
figure out the integral further.
501+
502+
> eval (I f) = let _F = simplify (integrate f)
503+
> in if _F == (I f)
504+
> then integrateApprox (eval f) 0.01 0
505+
> else eval _F

Physics/src/Calculus/VisVerApp.lhs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ First, we create some function expressions ready to be `show`n and
1818

1919
> f = Const 3 :* Id:^Const 2
2020
> f' = simplify (derive f)
21-
> _F = simplify (integrate f 0)
21+
> _F = simplify (integrate f)
2222

2323
Then, we define a helper function to plot a list of function
2424
expressions with Hatlab.

0 commit comments

Comments
 (0)