9 min read

Creating piecewise functions using only conventional functions

Table of Contents

Article

Summary

This post is about writing a piecewise function as a single equation, using more conventional-algebraic functions than the usual Heaviside step function. Specifically it describes a way of condensing continuous piecewise functions into a single equation using only the absolute value function (which can be written out of conventional functions).

Code

The scripts can be found at this repository, check the readme there for more info.

#1: Heaviside, and constructing our function from parts

I’ll briefly describe how to use the Heaviside step function to do it, because my solution works in a similar way.

The Heaviside step function is defined as follows:

H(x)={0,x<0,1,xβ‰₯0,H(x)=\begin{cases} 0, & x < 0, \\ 1, & x \ge 0, \end{cases}

Using it we can create the boxcar function, which is only equal to 1 over a certain interval:

Ba,b(x)=H(xβˆ’a)βˆ’H(xβˆ’b)={0,x<a1,x=a1,a<x<b0,x=b0,x>bB_{a,b}(x) = H(x-a) - H(x-b) = \begin{cases} 0, & x < a \\ 1, & x = a \\ 1, & a < x < b \\ 0, & x = b \\ 0, & x > b \end{cases}

(We can also create boxcar functions which go off to infinity:)

Bβˆ’βˆž,b(x)=H(x+∞)βˆ’H(xβˆ’b)=1βˆ’H(xβˆ’b)Ba,∞(x)=H(xβˆ’a)βˆ’H(xβˆ’βˆž)=H(xβˆ’a)βˆ’0B_{-\infty,b}(x) = H(x+\infty) - H(x-b) = 1 - H(x-b) \\ B_{a,\infty}(x) = H(x-a) - H(x-\infty) = H(x-a) - 0

(and similar functions exist which include/exclude the endpoints, though we don’t have to worry about it for this example). Then we take our piecewise function and split it into parts:

f(x)={f1(x)=2(x+1),x<βˆ’1f2(x)=x2βˆ’1,βˆ’1≀x≀2f3(x)=3,x>2f(x)=\begin{cases} f_1(x) = 2(x+1), & x < -1 \\ f_2(x) = x^2-1, & -1 \leq x \leq 2 \\ f_3(x) = 3, & x > 2 \end{cases}

And we multiply each part by the boxcar function over its interval:

f(x)=f1(x)Bβˆ’βˆž,βˆ’1(x)+f2(x)Bβˆ’1,2(x)+f3(x)B2,∞(x)f(x) = f_1(x) B_{-\infty,-1}(x) + f_2(x)B_{-1,2}(x) + f_3(x)B_{2,\infty}(x)

Simplifying gives the final equation:

f(x)=2(x+1)(1βˆ’H(x+1))+(x2βˆ’1)(H(x+1)βˆ’H(xβˆ’2))+3H(xβˆ’2)f(x) = \textcolor{green}{2(x+1)(1 - H(x+1))} + \textcolor{blue}{(x^2-1)(H(x+1) - H(x-2))} + \textcolor{purple}{3H(x-2)}
graph of f(x)|50%

The colours show how the function is made up of slice functions which contain each of the component functions over a certain interval. Our approach will be to create something like these slice functions, without using weird functions like the Heaviside step function.

#2: Continuous, flat slices

Though the Heaviside method produces slices which become 0 outside of their interval, it’s enough to have slices which are constant outside of their interval.

Let me notate s:a→bs: a \rightarrow b as a slice function which is constant at y=ay=a to the left and constant at y=by=b to the right. Say we wanted to add another slice, t:c→dt: c \rightarrow d to the right of it.

  • For ss to not affect ttβ€˜s position, we have to subtract bb from tt. tβˆ’b:cβˆ’bβ†’dβˆ’bt-b : c-b \rightarrow d-b.
  • But for tβˆ’bt-b to not affect ssβ€˜s position, cβˆ’b=0c-b = 0.

Therefore only slice functions that match up in this way s1:aβ†’bs_1: a\rightarrow b, s2:bβ†’cs_2: b\rightarrow c, s3:cβ†’ds_3: c\rightarrow d, … can be connected. However, this doesn’t say anything about the actual function piece, which can jump from aa to wherever it needs to go, then connect/jump back to bb when finished, so long as we’re able to represent such a slice function within our constraints. I’ll only consider continuous slices for now to keep things simple.

|50%

An example of a continuous slice, and another slice which jumps around.

So we can join together constant slices by doing the following:

  • Sort them ascending by x-position.
  • Adjust each slice to start at y=0y=0, besides the first one.

#3: The absolute value function

I ended up looking at the absolute value for two reasons. First, you can make this almost step function:

∣x∣x={βˆ’1,x<0undefinedx=01x>0\frac{|x|}{x} = \begin{cases} -1, & x < 0 \\ undefined & x = 0 \\ 1 & x > 0 \end{cases}

Second, you can write it entirely out of β€œconventional” functions:

∣x∣=x2|x| = \sqrt{x^2}

That makes it a prime candidate for this problem.

#4: The ramp and incline functions

After playing around with the absolute value function a bit, I found two functions which proved to be useful. The ramp function is a function which is constant on one end, and a line on the other:

R(x)=12(x+∣x∣)={0,x<0x,xβ‰₯0R(x) = \frac{1}{2}(x + |x|) = \begin{cases} 0, & x<0 \\ x, & x \geq 0 \end{cases}
  • when x<0x<0, the xx and ∣x∣|x| cancel each other out.
  • when x>0x>0, they add together to make a line of slope 2, so we divide by 2 to normalize the line’s slope to 1.

And by staggering two ramp functions, we can create something I call the incline function, which is constant on both ends.

I(x)=R(x)βˆ’R(xβˆ’1)=12(∣xβˆ£βˆ’βˆ£xβˆ’1∣+1)={0,x<0x,0≀x≀11,x>1I(x) = R(x) - R(x-1) = \frac{1}{2}(|x| - |x-1| + 1) = \begin{cases} 0, & x<0 \\ x, & 0 \leq x \leq 1 \\ 1, & x > 1 \end{cases}
  • when x<0x<0, both ramps are 0 so the result is 0.
  • when 0≀x≀10\leq x \leq 1, one ramp function is increasing while the other stays flat, creating a line.
  • when x>1x>1, the ramp functions cancel each other out, keeping it constant.
A graph of the ramp function
A graph of the incline function

This form of the incline function generates a line segment from (0,0) to (1,1). Through translation and scaling we can create incline functions from any points (a,b) to (c,d):

  • Incline from (0,0) to (1,1) = I(x)I(x)
  • Incline from (a,b) to (a+1,b+1) = I(xβˆ’a)+bI(x-a)+b
  • Incline from (a,b) to (a+(c-a),b+(d-b)) = I((xβˆ’a)/(cβˆ’a))βˆ—(dβˆ’b)+bI((x-a)/(c-a))*(d-b)+b

Similarly, R(x)R(x) is a β€œright” ramp around (0,0) with slope 1 (the line appears to the right of the point). We can scale it like so:

  • Right ramp around (0,0) with slope 1: R(x)R(x)
  • Right ramp around (0,0) with slope n: nR(x)nR(x)
  • Right ramp around (a,b) with slope n: nR(xβˆ’a)+bnR(x-a)+b
  • Left ramp around (0,0) with slope 1: βˆ’R(βˆ’x)-R(-x)
  • Left ramp around (a,b) with slope n: βˆ’nR(βˆ’(xβˆ’a))+b-nR(-(x-a))+b

#5: Constructing slices

Surprisingly (dissapointingly) it’s only one easy trick from the incline function to a slice of any function we want.

To motivate it a bit, say we did have a slice of f(x)f(x) from [a,b][a,b]. For this function to be continuous and flat on both ends, it would have to remain at f(a)f(a) for x<ax < a, and at f(b)f(b) for x>bx > b.

{f(a),x<af(x),a≀x≀bf(b),b<x\begin{cases} f(a), & x < a \\ f(x), & a \leq x \leq b \\ f(b), & b < x\end{cases}

But the value plugged into f(x)f(x) looks suspiciously like an incline function β€” and if we set I(x)I(x) to be the incline function from (a,a) to (b,b) and plug it into f(x)f(x), we get the function we want:

f(I(x))={f(a),x<af(x),a≀x≀bf(b),b<xf(I(x)) = \begin{cases} f(a), & x < a \\ f(x), & a \leq x \leq b \\ f(b), & b < x\end{cases}

If a=βˆ’βˆža = -\infty, we would use a left ramp around (b,b) with slope 1.
If b=∞b = \infty, we would use a right ramp around (a,a) with slope 1.
If both are infinity, then just use y=xy=x (nothing gets changed).

Example

Let’s try the same function from the Heaviside example:

f(x)={f1(x)=2(x+1),x<βˆ’1f2(x)=x2βˆ’1,βˆ’1≀x≀2f3(x)=3,x>2f(x)=\begin{cases} f_1(x) = 2(x+1), & x < -1 \\ f_2(x) = x^2-1, & -1 \leq x \leq 2 \\ f_3(x) = 3, & x > 2 \end{cases}

Create our inclines:

  • I1(x)I_1(x) = Left ramp around (-1,-1) with slope 1 = x2βˆ’βˆ£x+1∣2βˆ’12\frac{x}{2} - \frac{\left|{x + 1}\right|}{2} - \frac{1}{2}
  • I2(x)I_2(x) = Incline from (-1,-1) to (2,2) = βˆ’12∣xβˆ’2∣+12∣x+1∣+12-\frac{1}{2}|x-2| + \frac{1}{2} |x+1| + \frac{1}{2}
  • I3(x)I_3(x) = Right ramp around (2,2) with slope 1 = x2+∣xβˆ’2∣2+1\frac{x}{2} + \frac{\left|{x - 2}\right|}{2} + 1

Create and connect together the slices:

  • f(x)=f1(S1(x))+(f2(S2(x))βˆ’0)+(f3(S3(x))βˆ’3)f(x) = f_1(S_1(x)) + (f_2(S_2(x))-0) + (f_3(S_3(x))-3)

Simplifying:

  • f(x)=(xβˆ’βˆ£x+1∣+1)+((βˆ’12∣xβˆ’2∣+12∣x+1∣+12)2βˆ’1)+(3βˆ’3)f(x) = (x - |x+1| + 1) + (\left(-\frac{1}{2}|x-2| + \frac{1}{2} |x+1| + \frac{1}{2}\right)^{2}-1) + (3-3)
  • f(x)=xβˆ’βˆ£x+1∣+14(βˆ’βˆ£xβˆ’2∣+∣x+1∣+1)2f(x) = x - \left|{x + 1}\right| + \frac{1}{4}\left(- \left|{x - 2}\right| + \left|{x + 1}\right| + 1\right)^{2}

For fun, rewriting the absolute values using square roots:

  • f(x)=xβˆ’(x+1)2+14(βˆ’(xβˆ’2)2+(x+1)2+1)2f(x) = x - \sqrt{(x + 1)^2} + \frac{1}{4}\left(- \sqrt{(x - 2)^2} + \sqrt{(x+1)^2} + 1\right)^{2}

Graph of f(x)=xβˆ’(x+1)2+14(βˆ’(xβˆ’2)2+(x+1)2+1)2f(x) = x - \sqrt{(x + 1)^2} + \frac{1}{4}\left(- \sqrt{(x - 2)^2} + \sqrt{(x+1)^2} + 1\right)^{2}

Success!

Bonus stuff

Introducing discontinuities

So far we’ve only been working with continuous piecewise functions, but is it also possible to do this with discontinuous piecewise functions?

The way we construct our slices from functions will allow for discontinuities within the function (for example, we can construct a slice of 1x\frac{1}{x} on [βˆ’1,1][-1,1]). However I don’t think it’s possible to have jump discontinuities between the pieces, for the following reason:

πŸ’‘

Say we had a function s(x)s(x) with 1 or more of these discontinuities, and there was a way to write it under our constraints. We can remove all of the discontinuities by shifting the pieces around to get sΛ‰(x)\bar{s}(x). sΛ‰(x)\bar{s}(x) now has no discontinuities between its pieces, so we can use our algorithm to find an equation for it. But now consider s(x)βˆ’sΛ‰(x)s(x) - \bar{s}(x). This would be some sort of step function, and we would have just found a representation of it following our constraints. I don’t know how to prove that such a representation doesn’t exist, but I can’t find any evidence saying that it does, leading me to believe that it’s impossible.

Unless there is a function with a naturally occuring jump discontinuity, I don’t believe it’s possible to create piecewises containing jump discontinuities using this method.

Undefined slices

We can create slices which are undefined over a certain interval by leveraging the function (xβˆ’b)(xβˆ’c)\sqrt{(x-b)(x-c)}, which is undefined between b<x<cb < x < c and 0 at x=b,cx=b,c. Slicing it between [b,c][b,c] gives us

Slice((xβˆ’b)(xβˆ’c),b,c)={0,x≀bundefined,b<x<c0,xβ‰₯c\text{Slice}(\sqrt{(x-b)(x-c)}, b, c) = \begin{cases} 0, & x \leq b \\ undefined, & b < x < c \\ 0, & x \geq c \end{cases}

Running this through our algorithm above and simplifying, we can nicely write this as (xβˆ’b)2+(xβˆ’c)2βˆ’2∣(xβˆ’b)(xβˆ’c)βˆ£βˆ’(bβˆ’c)2\sqrt{(x-b)^2 + (x-c)^2 -2|(x-b)(x-c)| - (b-c)^2}

If we wanted to make a single point x=bx=b undefined, we can use slice functions like xβˆ’bxβˆ’bβˆ’1\frac{x-b}{x-b} - 1, eln⁑∣xβˆ’bβˆ£βˆ’βˆ£xβˆ’b∣e^{\ln{|x-b|}} - |x-b|, …

Periodic piecewise functions

Is there a trick we can use to create periodic piecewise equations?

After searching around I found a couple of interesting functions:

  • sinβ‘βˆ’1(sin⁑(x))\sin^{-1}(\sin(x)) is a linear function which oscillates between [βˆ’Ο€2,Ο€2][-\frac{\pi}{2}, \frac{\pi}{2}] with period 2Ο€2\pi. This is actually listed on the Wikipedia article for triangle waves. cosβ‘βˆ’1(cos⁑(x))\cos^{-1}(\cos(x)) is similar, oscillating between [0,Ο€][0, \pi] instead.
  • The wikipedia page for sawtooth waves also lists the function tanβ‘βˆ’1(cot⁑(x))\tan^{-1}(\cot(x)). However this function is undefined at each multiple of Ο€2\frac{\pi}{2}, so it doesn’t work well enough for our purposes.
A graph of asin(sin(x))
A graph of atan(tan(x))

Ideally we’d like a sawtooth shape for our periodic functions, but I can’t find one and I have a hunch that there isn’t one based on the problems encountered with discontinuities. As far as I know there are no other elementary periodic functions besides the trigonometric ones, so any search for more periodic functions would have to involve a trigonometric term. Combining two continuous functions (+, -, x, / by nonzero, composition) will always gives you a continuous function, so a discontinuous function would also have to be involved (sqrt, log, 1/x, tan, asin, acos).

For now, we can only make oscillating periodic functions. By exchanging xx with sinβ‘βˆ’1(sin⁑(x))\sin^{-1}(\sin(x)) and performing some other transformations, we can make an oscillating version of f(x)f(x), where [a,b][a,b] within f(x)f(x) gets mapped to [c,d][c,d] in the output (see the source code for details).

x^3 oscillated over [-2,2]

Conclusion

Thanks for reading!

uwu

f(x) = abs(112*abs(x) - 7*abs(16*abs(x) - 13) + 13)/78 - 545/192 + 3179/(64*(-512*x^2 + 1536*abs(x) + 512*abs((abs(x) - 2)*(abs(x) - 1)) - 991))

uwu uwu uwu uwu uwu

f(x) = abs(1932*abs(acos(cos(pi*(20*x/69 + 1))) - pi) - 7*abs(276*acos(cos(pi*(20*x/69 + 1))) - 211*pi) + 65*pi)/(390*pi) - 545/192 + 79475*pi^2/(-1024*(69*acos(-cos(20*pi*x/69)) - 49*pi)^2 - 1024*(69*acos(-cos(20*pi*x/69)) - 29*pi)^2 + 2048*abs((69*acos(-cos(20*pi*x/69)) - 49*pi)*(69*acos(-cos(20*pi*x/69)) - 29*pi)) + 462400*pi^2)