# How to make a square wave function with controllable period, phase and duty cycle that can work really fast?

I actually need two things:

1. A square wave function that allows me to control its duty cycle, period and phase.
2. It must work fast enough to be evaluated at least a million times per second.

For the first problem, I found a number of solutions. Some of them may be found here: How to create a rectangle wave (duty cycle $\ne$ 50 %)?

The best one that I tried yet is as follows:

squareWave[period_, duty_, phase_,  x_] := Sign[TriangleWave[x/period - phase] - 1 + 2 duty]


Unfortunately, on my current machine it takes about 34 seconds to evaluate it 10^6 times. To compare with, Sin[x] evaluates 10^6 times in 0.09 seconds.

Is it even possible to achieve my goal? And how?

• Try squareWave[x_, period_: 1, duty_: (1/2), phase_: 0] := Sign[Mod[x + phase period, period] - (1 - duty) period] Commented Nov 9, 2017 at 17:20
• The test that gives 34 seconds is actually AbsoluteTiming[Do[squareWave[1, 0.5, 0, 0.3], 10^6]] with the definition of squareWave provided above. Some insignificant details were omitted to simplify the question. I have a PC that can handle the same task in 7 seconds but it's still too slow. I tried your definition with the test AbsoluteTiming[Do[squareWave[0.3], 10^6]] on a good PC and it just took 8 seconds instead of 7. Commented Nov 9, 2017 at 19:57
• Btw, the good PC evaluates AbsoluteTiming[Do[Sin[1], 10^6]] in 0.038 seconds. Commented Nov 9, 2017 at 20:19
• Maybe you could use Compile? Commented Nov 9, 2017 at 20:41
• Have you tried compiling J.M.'s version? It uses only low level functions, I guess, so should be fast. Commented Nov 9, 2017 at 21:08

The following maps directly to the List of values:

x = RandomReal[1, 10^6];
squareWave[x_, period_: 1, duty_: (1/2), phase_: 0] :=
Sign[Mod[x + phase period, period] - (1 - duty) period] (* J.M.'s def. *)
squareWave[x, 1, .5, 0]; // AbsoluteTiming
(* 0.0078 *)


If you want to evaluate with multiple period, duty and phase values, stack all values in Lists as in:

x = RandomReal[2, 10^6];
period = RandomReal[1, 10^6];
duty = RandomReal[.7, 10^6];
phase = RandomReal[.5, 10^6];
squareWave[x, period, duty, phase]; // AbsoluteTiming
(* 0.061 *)


This is 150 times faster than the sequential approach Table[squareWave[x[[i]], period[[i]], duty[[i]], phase[[i]]], {i, 10^6}], however it assumes that you know the list of values before running the calculation, which seems to be the case.

• Well this is a very interesting and useful result. I will definitely try to apply it to the real task and mark the answer accepted if it helps. As of now, a quick test shows about 10x improvement in speed so on a good PC it may be enough. Commented Nov 9, 2017 at 21:45
• Somehow I figured that something wonky was going on with either the OP's computer or with how the function I proposed is being applied for it to be "slower" than Sin[], but lost interest in trying to diagnose it. Commented Nov 10, 2017 at 3:22

Using SawtoothWave instead of TriangleWave seems to run a little faster.

t = RandomReal[1, 10^6];
pulseTrain[t_, period_, duty_, phase_, a_] :=
0.5 a (1 + Sign[SawtoothWave[t/period + (1 - duty/2) + phase] - (1 -duty)]);
pulseTrain[t, 1, .5, 0.3, 1]; // AbsoluteTiming

(* ~0.101 *)


When I use TriangleWave, I have

x = RandomReal[1, 10^6];
squareWaveAlt[period_, duty_, phase_, x_] :=
Sign[TriangleWave[x/period - phase] - 1 + 2 duty];
squareWaveAlt[1, .5, 0, x]; // AbsoluteTiming

(* ~0.192 *)


This seems to take about twice as long, and I'm not sure why that is.

• It might be better to use UnitStep[] + Mod[] instead: pulseTrain[t_, period_, duty_, phase_, a_] := a UnitStep[Mod[t/period + (1 - duty/2) + phase, 1] - (1 - duty)] Commented Feb 11, 2021 at 6:19