1

I am currently taking my first Python coding course and I am very stuck on this assignment. The goal is to define a function 'F(t)' using a piecewise function that would make a graph of a triangle wave that looks identical to:

enter image description here

This is what I have tried:

def F(t):
 if (t >= 0) & (t <= 1/2):
  return 4*t
elif (t >= 1/2) & (t < 1):
 return -4*t
else:
 return 0

f = np.vectorize(F)
t = np.linspace(0,4,num=8)
plt.plot(t,f(t))

Obviously, to no luck. The above code matches the ideal graph from 0 to 1 so I'm really just struggling with how to make the graph a continuous triangle wave for all t > 0.

Any tips or suggestions are much appreciated

  • Well instead of `else: return 0`, your function could return some actual values for `t >= 1`. – mkrieger1 Mar 22 '22 at 02:12
  • If your function works well for 01 down to a value 0 – konstanze Mar 22 '22 at 02:17
  • Ill try that, thanks! – Milky Fresh Mar 22 '22 at 02:21
  • Your function seems to go from 1 to -1 in the range [0, .5], so it looks like your function there is 1 - 4 * 5. From .5 to 1, it looks like F(t) = 4 * t - 3. The first line of your code should be t = t % 1. – Frank Yellin Mar 22 '22 at 02:47
  • maybe first use `print()` to see what you have in variables in `F()`. At this moment it gets `t` like `0.5714285714285714` or `1.1428571428571428` but if you would use `num=9` then you would get exactly `0.5`, `1.0` – furas Mar 22 '22 at 04:49

2 Answers2

2

Your function is wrong. If you want the troughs to be at odd multiples of 0.5 and the peaks to be at even multiples of 0.5, and you want the value to be 1 and -1, your function needs to be -4 * t + 1 between 0 and 0.5, and 4 * t - 3 between 0.5 and 1.

Also, your function returns 0 for t < 0 and for t > 1, so your output doesn't match the expected output outside those bounds.

Since your if conditions describe the correct behavior when you have only the part of the input after the decimal point, you can extract that part and use it instead of t:

def F(t):
    decimal_part = t % 1
    if 0 <= decimal_part <= 0.5:
        return -4*decimal_part + 1
    else:
        return 4*decimal_part - 3

Which gives this:

Expected plot

Remember to discretize the x-axis so it has enough points. I plotted the blue line with t = np.linspace(0, 4, 1000), and the red line with t = np.linspace(0, 4, 10) and everything else remained the same.

The np.vectorize() function doesn't really vectorize a function though. If you want to actually see speed improvements, you're going to have to vectorize the function yourself by expressing its logic as a vector operation.

def g(t):
    dec = (t % 1) - 0.5
    return (np.abs(dec) - 0.25) * 4

Both give the same plots, but timing the execution shows the difference:

import timeit

t1 = timeit.timeit('f(t)', setup='from __main__ import np, f, t', number=10000)
t2 = timeit.timeit('g(t)', setup='from __main__ import np, g, t', number=10000)

# t1 = 3.322270042001037
# t2 = 0.17450489799375646

print(t1 / t2)
# 19.038262422410074

The actually vectorized function is ~19x faster on my computer (Intel Python 3.7 with numpy+MKL on Macbook Air)

Pranav Hosangadi
  • 23,755
  • 7
  • 44
  • 70
0

First you could use print() to see what you have in variables.

In F() you get t like 0.5714285714285714, 1.1428571428571428 but if you would use num=9 instead of num=8 then you would get exactly 0.5, 1.0 and it would be much easier to draw it.

And using print() to display results for different combinations I created

def F(t):
    return 1 - ((t*2) % 2)

Which can be reduced to

def F(t):
    return 1 - (t % 1)

How I get it:

You have y in range (-1, 1) which gives length 2 so I started with y = t % 2 to get values in range (0, 2) which also gives length 2. Because for 0 I got 0 and I needed 1 so next I used 1 - ... to get a "mirror image". So I get y=1 for x=0 but next top was for y=2 but I needed for y=1 so I had to use *2 to get it. And finally I released that (t*2) % 2 can be (t % 2) * (2 % 2) so it gives (t % 2) * 1 and this gives (t % 2)


Minimal working code

import numpy as np
import matplotlib.pyplot as plt

def F(t):
    #y = t % 2
    #y = 1 - (t % 2)
    #y = 1 - ((t*2) % 2)
    y = 1 - (t % 1)
    print(t, y)
    return y

f = np.vectorize(F)
t = np.linspace(0, 4, num=9)
plt.plot(t, f(t))
plt.show()

enter image description here

furas
  • 134,197
  • 12
  • 106
  • 148
  • Changing the num pts in `linspace` to `9` actually _hides_ an issue with OP's code: Those lines aren't +/-4*t – Pranav Hosangadi Mar 22 '22 at 05:17
  • @PranavHosangadi yes, this is problem for values differen then `num=9` but frankly OP didn't say it has to use `num=8` or that it has to work for every `num`. So I decided to mak this modification. – furas Mar 22 '22 at 05:21
  • Also your function gives me this with 1000 points: https://i.stack.imgur.com/DOyP5.png – Pranav Hosangadi Mar 22 '22 at 05:23
  • Re. _"frankly OP didn't say it has to use num=8"_ Well OP said they need to reproduce the triangular wave. That would seem to indicate they want it to give the correct values for _all_ values of `t` – Pranav Hosangadi Mar 22 '22 at 05:25
  • @PranavHosangadi let OP decide if code has to work for every `num` or it can work only for `num=9` – furas Mar 22 '22 at 05:31