Skip to content

Commit

Permalink
## A Bouncing Ball Graph
Browse files Browse the repository at this point in the history
* TOC
{: toc }

After banging my head against the ball for an hour or so trying to get Newton's method to work in JavaScript, I bailed for the Wolfram Language which, as you'd expect, has excellent primitives for solving equations, both analytic and computational.

However, I did really get fed up with constantly re-evaluating old cells to make their definitions update. This was glitchy some of the time too, making me thing there were bugs when it was that I was looking at older version of expressions.

### Wolfram Bouncing Ball

Anyways, it took me a 3-5 hours, but I finally got a piecewise, pure, mostly-non-recursive function which can be infinitely expanded of a bouncing ball. Let's look at the code:

```
sim[0] = <|start -> 0, end -> 0,  vel -> Function[t, 0], pos -> Function[t, 0] |>
sim[n_] := sim[n] = 
  With[{start1 := sim[n - 1][end]},
    With[{previousV := sim[n - 1][vel][start1]},
      With[{vel1 := Function[t, -previousV*0.9 + -10*(t-start1)]},
        With[{startPos := sim[n - 1][pos][start1]},
          With[{pos1 := Function[t, startPos + ((vel1[t] + vel1[start1])/2)*(t -start1)]},
            With[{end1 := First[t/.Solve[{pos1[t] == -473 && t > start1}], t]},
              <|start ->  start1, end -> end1,  vel -> vel1, pos -> pos1  |>
            ]
          ]
        ]
      ]
    ]
  ]  

piece := Function[n, {sim[n][pos][t], t < sim[n][end]}]

Plot[
  {Piecewise[Map[piece, {1,2,3,4,5, 6, 7, 8}]]},
  {t, 0, 100}
]
```

Which gives us the glorious result:

![screenshot 2018-05-31 at 5 43 05 pm](https://user-images.githubusercontent.com/2288939/40809942-2d49c7fa-64fa-11e8-9662-c3fc1ac54195.png)

#### Top-level explaination

This code isn't at all read-able, even by me, a few hours after I wrote it.

First a top-level explaination: the code is creating a piecewise function. Every time the ball hits the bottom (-473), we need to create a new function to represent it's motion post-bounce. 

What's tricky is that each piece of the function depends on the previous piece's end values (when the ball bounces, we need it's old position and velocity). So we construct this recursive function `sim` which will generate us parts of bounces. 

#### Line-by-line explaination

```
sim[0] = <|start -> 0, end -> 0,  vel -> Function[t, 0], pos -> Function[t, 0] |>
```

`sim[0]` represents the initial conditions, lasting 0 seconds (as the start and end times are the same). 

```
sim[n_] := sim[n] = 
```

We define `sim[n]` here and also let Wolfram know to (memorize the results of prior runs to `sim` so it doesn't need to recompute the results)[http://reference.wolfram.com/language/tutorial/FunctionsThatRememberValuesTheyHaveFound.html]. 

```
  With[{start1 := sim[n - 1][end]},
```

Here I begin a nested series of `With` statements. They are nested because the computations depend on each other and Wolfram `With` statements aren't smart enough to figure out how to handle that.

Some of the variables defined in the `With` statements, such as `start1` above are eventually placed into the association (it's like a dictionary or object) below. I didn't name this variable `start` (as you would in another langauge), because Wolfram would then then I wanted the *key* to be the value of the start variable, such as `<| 4.34 -> 4.34, ... |>` -- so that's why there's a `1` after certain variable names.

And now to the meat of this line: it says that the start of the current piece is the end of the prior piece. Pretty straight forward.

```
    With[{previousV := sim[n - 1][vel][start1]},
```

Here we calculate the final velocity of the previous piece, which we do by evaluating its velocity function on the starting time of this piece (which as we calculated above is the ending time of the previous piece). 

```
      With[{vel1 := Function[t, -previousV*0.9 + -10*(t-start1)]},
```

Here we calulate the velocity of this piece:

  * We reverse the end velocity of the previous piece, because the ball should bounce up with the same speed it hit the ground
  * But not quite the same speed because balls never bounce as high as they did when they were dropped, so we multiply the speed by 0.9
  * And then we changing the speed by -10, which is our acceleration, every second after the starting time of this piece.
  
```
        With[{startPos := sim[n - 1][pos][start1]},
```

The starting position of this piece is the position of the previous piece at the end time of that piece (which is the starting time of this piece). This is the same way we found `previousV`.

```
          With[{pos1 := Function[t, startPos + ((vel1[t] + vel1[start1])/2)*(t -start1)]},

```

Then we calculate position:

  * starting at the initial position
  * then taking the average of velocity at the current moment and the velocity at the beginning of this piece
  * and multiplying the average velocity by the time elapsed in the current piece so far

```
            With[{end1 := First[t/.Solve[{pos1[t] == -473 && t > start1}], t]},
```

And here we calcuate the time at which this piece hits the floor which I set here to be -473. The meat of this line `Solve[{pos1[t] == -473 && t > start1}], t]` simply asks to solve for `t` where the position is at the floor and the time is after the starting time of this piece. `First[t/.` is just extracting the computed solution out of the result.

```
              <|start ->  start1, end -> end1,  vel -> vel1, pos -> pos1  |>
```

And here we boringly compile the results of our hard work into a single data structure.

```
piece := Function[n, {sim[n][pos][t], t < sim[n][end]}]
```

This function turns the nth simulation into an array suitable for the `Piecewise` function. It's first argument it the function to be plotted and the second argument is the domain over which it should be plotted. In this case, the first argument is simply the position and the second argument is that the time shall be less than the ending time of the piece.

```
Plot[
  {Piecewise[Map[piece, {1,2,3,4,5, 6, 7, 8}]]},
  {t, 0, 100}
]
```

And here we plot 8 pieces of the simulation, resulting in the beautiful graph you saw above.

### But why?

I've been talking a lot about piecewise functions these last few days, to my mom and my girlfriend, and they both think the idea is kinda neat, but don't understand why I'm doing this. I don't have a great defense. Instead of spending 4ish hours making this graph, I could've done the [same thing in WoofJS in 9 lines of very readable code](http://woofjs.com/create#very-simple-ball) in *under 60 seconds* (I timed it):

```javascript
var circle = new Circle({})
circle.v = 0
forever(() => {
  circle.v--
  circle.y += circle.v
  if (circle.y < minY) {
    circle.v = circle.v *-0.9
  }
})
```

#### I want definitions to be definitional goddammit! 

I want that when you define something it stays defined. I've had enough of these mutable reference cells that you can read to and write to at any time. I've had enough of playing Turing Machine!

Redux isn't all that different than global state we had before. Sure it's easily serialize-able, which gives you undo and redo, and time travel debugging, but that's not all I want.

I want to be able to look at a piece of code and know that the only things that can affect that code are written in it. A piece of code should depend on its dependencies. Nothing else should be able to change it. In this way you could understand a small section of your code without having to understand the whole thing. Without this we are doomed to read all our code if we want to understand what's going on.

I don't know much maths beyond high school, so I wonder if my salvation lies in a more powerful structure than a piecewise function. If any math geeks out there are reading this, please let me know if you have any ideas!

### Mitigating thoughts

Possibly these explicit formula and the Logo/Woof implicit, recursive formula are just two sides of the same coin, and we could allow programmers to write in the more intuitive (change the position by X every Y) and then take the derivitive of that to get the explicit form. (It seems like there are ways to get explicit formula for any implicit or recursive formula, so this seems likely...)

In the same way, I wonder if we can convert a codebase where any mutable cell can be changed by anyone, and statically parse all the places in the code that *could possibly* have an effect on that value, and bring them all together so you can see you things are tangled. (I am doubtful of this because 1. which values are being modified can change dymaically so we can't really assess well statically, 2. I bet this would be such a gnarled mess that it's not really useful expect to show us all the folly of our entangled ways.)

#### There are just a whole bunch of bad things I know about

1 - Control flow of any kind seems like a terrible idea all around. You should just describe the system and the system shall determine which lines of code to run when. Non-monadic Haskell does a great job of doing away with this.

2 - Mutable variables seem like a terrible idea, as discussed above.

3 - The global state reducer from actions also feels like a terrible idea. I don't want anything to be able to emit an action that can change any part of the system. If you look at a piece of state, how are you to know which actions could affect it? I also feel like it's too much power, allowing the system to change itsellf based on the past value of itself.

But how are these piecewise functions any better, you ask? Great qusetion! As you noticed, when a new piece of the piecewise function is being created, it depends on the values of the last piece. It has the entire state of the app to do what it wills with. Yeah I didn't like this either, but I wasn't able to thing up a way out of it. We really need to know the final velocity / position of the prior piece to start the next bounce. But I'm not terribly beat up about it: maybe allowing pieces to view prior pieces on piece transitions isn't so bad...?

As far as the first cricism of Redux from above, that the dependencies of a piece of start aren't listed in the state, I haven't yet dealt with that because I haven't built "prototype" to also work with unpredictable input, such as mouse clicks. But the idea is to allow mouse clicks to trigger new pieces to be generate at the time of the mouse click. Only time protoyping will tell if this new framework has any merits at all.

Part of me wonders if I need to build an interface for this prototype because exisiting ones (such as Wolfram and Woof) just don't cut it. And part of the whole point is that I'm just constructing transitions between pure mathmatic functions, so should it be super easy to visualize the state of all my functions as graphs?!

At the very least, I'm glad I was finally able to articulate above my dislike of the current frameworks: you can't tell what's going on at X by just looking at X and asking for its dependencies.
  • Loading branch information
Steve Krouse committed May 31, 2018
1 parent 177e678 commit e49f91c
Showing 0 changed files with 0 additions and 0 deletions.

0 comments on commit e49f91c

Please sign in to comment.