<jupyter_start><jupyter_text>Stupid Tensorflow Tricks, IISolving an old physics problem with Tensorflow!_part one [here](https://towardsdatascience.com/stupid-tensorflow-tricks-3a837194b7a0)_  Brachistocrone problemWhat's the fastest way down a ramp? Let's solve for the height of the green dots that minimize the time. ![](figures/plane.png)<jupyter_code>import tensorflow as tf
import numpy as np
print(tf.__version__)

# This lets us see computations as soon as we run them!
tf.enable_eager_execution()
# Setup the "ramp"
N = 20
width = 3
y = tf.Variable(np.linspace(0.99, 0.01, N,), dtype=tf.float32)
x = tf.linspace(0.0, 1.0, N+2) * width
print(y)<jupyter_output><tf.Variable 'Variable:0' shape=(20,) dtype=float32, numpy=
array([0.99      , 0.9384211 , 0.88684213, 0.83526313, 0.7836842 ,
       0.73210526, 0.6805263 , 0.6289474 , 0.57736844, 0.5257895 ,
       0.47421053, 0.4226316 , 0.37105262, 0.31947368, 0.26789474,
       0.21631579, 0.16473684, 0.1131579 , 0.06157895, 0.01      ],
      dtype=float32)><jupyter_text>Conservation of Energy$$U=K$$$$mgh = \frac{1}{2}mv^2$$$$v = \sqrt{2gh}$$<jupyter_code>g = tf.constant(9.8)
v = tf.sqrt(2 * g * (1 - y));
print(v)<jupyter_output>tf.Tensor(
[0.44271865 1.0986115  1.4892596  1.7968981  2.059075   2.2914486
 2.5023358  2.6967816  2.8781207  3.0486925  3.2102141  3.3639889
 3.5110352  3.652166   3.788042   3.9192104  4.0461287  4.169185
 4.2887125  4.4049973 ], shape=(20,), dtype=float32)<jupyter_text>Time to roll down can compute for each little segment...$$ z(t) = z_0 + v_0 t + \frac{1}{2}a_z t^2 = z_0 + v_0 t + \frac{g \sin(\theta) t^2}{2} $$$$ t = \frac{-v_0 \pm \sqrt{v_0^2 - 2 g \sin(\theta)z_0}} {g \sin(\theta)} $$Ugh. We have to keep track of the root: the first root if going up the ramp, the second root if we are moving up the ramp.... that's stupid and too much work.   Lazy physics ...Over a short enough time span we can ignore gravity, as long as we fix the velocity each segment.$$ z(t) = z_0 + v_0 t $$$$ t = z_0 / v_0  $$The change along the ramp:![](figures/plane_crop.png)$$ t = \frac{\sqrt{\Delta x^2 + \Delta y^2}}{v_0}  $$<jupyter_code>dx = x[1] - x[0]
dy = y[:-1] - y[1:]
dz = tf.sqrt(dx ** 2 + dy ** 2)
print(dz)<jupyter_output>tf.Tensor(
[0.15188335 0.15188335 0.15188336 0.15188335 0.15188335 0.15188335
 0.15188335 0.15188335 0.15188335 0.15188336 0.15188335 0.15188336
 0.15188335 0.15188335 0.15188335 0.15188335 0.15188335 0.15188335
 0.15188335], shape=(19,), dtype=float32)<jupyter_text>Let's clean it up, and encapsulate it all in a function.<jupyter_code>def compute_ramp(y):
    g = tf.constant(9.8)
    
    # Fix the endpoints
    y = tf.concat([[1.0], y, [0.0]], axis=0)
    
    # All the widths are the same
    dx = x[1] - x[0]
    dy = y[:-1] - y[1:]   
    dz = tf.sqrt(dx ** 2 + dy ** 2)

    # Convervation of energy
    v = tf.sqrt(2 * g * (y[0] - y))
    
    # The average of the starting and ending velocity at each segment
    avg_v = (v[1:] + v[:-1]) / 2

    total_time = tf.reduce_sum(dz / avg_v)
    return total_time
# For a 3x1 ramp, this should be ~ 1.34s
print(compute_ramp(y).numpy())
# Oof. Let's try it with more points!
N = 2000
width = 3
y = tf.Variable(np.linspace(0.90, 0,N,), dtype=tf.float32)
x = tf.linspace(0.0, 1.0, N+2) * width
print(compute_ramp(y).numpy())
# Develop our training step
optimizer = tf.train.AdamOptimizer(learning_rate=0.01)

def train_step(y):
    with tf.GradientTape() as tape:
        time = compute_ramp(y)

    variables = [y]
    grads = tape.gradient(time, variables)
    optimizer.apply_gradients(zip(grads, variables))

    return time.numpy()
%%capture
import pylab as plt
import seaborn as sns
import matplotlib.pylab as plt

fig, ax = plt.subplots(figsize=(7,4))
plt.rcParams['figure.dpi'] = 125
N = 500
width = 3
n_steps = 5000

y = tf.Variable(np.random.uniform(-1,0.5,size=(N,)), dtype=tf.float32)
x = tf.linspace(0.0, 1.0, N+2) * width

for i in range(n_steps):
    t = train_step(y)

    if i % 100 == 0 and i > 0:
        print(f"{i} {t:0.16f}")
        yx = np.hstack([[1], y.numpy(), [0]])
        ax.plot(x.numpy(), yx, color='b',alpha=i/n_steps)
ax.plot(x.numpy(), yx,'r',lw=1)
ax.plot(x.numpy(), 0*x.numpy(),'k',lw=2,alpha=0.6)
sns.despine(ax=ax)
fig.tight_layout()
fig.savefig("figures/final_solution.png")
fig<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.16261855193152902, 'fraction_numerical': 0.148970622253065, 'mean_word_length': 3.614727854855923, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 16, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '4', 'dir': 'jupyter-structured-clean-dedup', 'id': '139600', 'license': 'MIT', 'n_tokens_mistral': 2191, 'n_tokens_neox': 1807, 'n_words': 445, 'path': 'docs/DSDC/Brachistochrone.ipynb', 'repo_name': 'thoppe/Presentation_Topics', 'size': '4323'}