2
$\begingroup$

I'm new to Mathematica, and I have a problem with the following kind of code:

f[x_] := {1, 2, 3, 4, 5}[[Mod[Floor[x], 5] + 1]]
Integrate[f[x], x]

When I run this, I get the message:

Part::pkspec1: "The expression 1+Mod[Floor[x],5] cannot be used as a part specification. "

Why does this not work? Can't you use expressions/symbols as a list index? I tried all sorts of combinations of Unevaluted[], HoldFirst[], and assumptions about stuff being an integer (I assume it might fail because Mathematica doesn't know that the expression will always be integer), but none seemed to work.

I've asked this as part of another question, but I think it might have been buried and could be asked in a simpler way, so I've created this new question.

$\endgroup$
  • $\begingroup$ You could go numerically: f[x_?NumericQ] := {1, 2, 3, 4, 5}[[Mod[Floor[x], 5] + 1]] NIntegrate[f[x], {x, 0, 10}] $\endgroup$ – Dr. belisarius Nov 18 '15 at 15:49
  • $\begingroup$ Or analytically with f[x_] := Switch[Mod[Floor[x], 5], 0, 1, 1, 2, 2, 3, 3, 4, 4, 5]; Integrate[f[x], {x, 0, 10}] $\endgroup$ – ybeltukov Nov 18 '15 at 15:53
  • $\begingroup$ @belisariushassettled: The numeric solution isn't enough for me. I need to obtain a general analytic solution to an integral involving a look-up table. $\endgroup$ – Alemarius Nexus Nov 18 '15 at 15:57
  • $\begingroup$ @ybeltukov: That might work. But why is Mathematica not able to do this with a look-up table like I'm using? Does that mean you can't use any symbolic expression for list indexing? $\endgroup$ – Alemarius Nexus Nov 18 '15 at 16:00
  • $\begingroup$ You can also use f = ListInterpolation[{1, 2, 3, 4, 5}, InterpolationOrder -> 0]. However this approach is very limited for analytic calculations. Also there is option PeriodicInterpolation -> True, but the question is: why Integrate gives wrong result with periodic interpolation? $\endgroup$ – ybeltukov Nov 18 '15 at 16:10
3
$\begingroup$

Instead of Part, use Indexed, which works quite similarly, but doesn't try to do any indexing unless its second argument has head Integer (or is a list of integers):

In[1]:= f[x_] := Indexed[{1, 2, 3, 4, 5}, Mod[Floor[x], 5] + 1];

In[2]:= f[t]
Out[2]= Indexed[{1, 2, 3, 4, 5}, {1 + Mod[Floor[t], 5]}]

In[3]:= f[Pi]
Out[3]= 4

The result of doing the integration is very similar to what @March got, but Mathematica takes care of the conversion to Piecewise for me:

In[4]:= Integrate[f[t], {t, 0, x}, Assumptions -> {0 <= x <= 10}]
Out[4]= Piecewise[{
          {5 (-4 + x), 9 < x <= 10},
          {5 (-2 + x), 4 < x <= 5},
          {3 (-1 + x), 2 < x <= 3 || 7 < x <= 8},
          {x, 0 < x <= 1},
          {2 (2 + x), 6 < x <= 7},
          {10 + x, 5 < x <= 6},
          {2 (-3 + 2 x), 3 < x <= 4},
          {-1 + 2 x, 1 < x <= 2},
          {-11 + 4 x, 8 < x <= 9}}, 0]

EDIT to add: Using the new FunctionPeriod function, I tried to use rules and pattern matching to simplify fairly general definite integrals with periodic integrands, but it's tricky. Here's what I came up with:

int = Integrate[f[t], {t, 0, x}] /. Integrate[expr_, {t_, a_, b_}] :> 
   With[{p = FunctionPeriod[expr, t, Reals]}, 
    Assuming[Element[{a, b}, Reals], 
     FullSimplify@With[{
        lo = Mod[a, p], 
        n1 = Ceiling[a/p],
        n2 = Floor[b/p], 
        hi = Mod[b, p]
       }, 
       Integrate[expr, {t, lo, p}, Assumptions -> 0 <= lo < p] + 
        (n2 - n1 - 1)*Integrate[expr, {t, 0, p}] + 
        Integrate[expr, {t, 0, hi}, Assumptions -> 0 <= hi < p]]]]];

Massaging the TeXForm output a bit, we get

$$ 15 \left\lfloor \frac{x}{5}\right\rfloor + \left\{ \begin{array}{cc} 5 ((x \bmod 5)-2) & (x \bmod 5)>4 \\ 3 ((x \bmod 5)-1) & 2<(x \bmod 5)\leq 3 \\ (x \bmod 5) & 0<(x \bmod 5)\leq 1 \\ 4 (x \bmod 5)-6 & 3<(x \bmod 5)\leq 4 \\ 2 (x \bmod 5)-1 & 1<(x \bmod 5)\leq 2 \\ \end{array} \right. $$

Plotting the two approaches shows that they're identical:

GraphicsColumn[{Plot[int, {x, -20, 20}, PlotLabel -> "FunctionPeriod"], 
  Plot[Evaluate[Integrate[f[t], {t, 0, x}, Assumptions -> 
      {-20 <= x <= 20}]], {x, -20, 20}, 
   PlotLabel -> "explicit range Assumption"]}]

identical

Nonetheless, I can't swear with 100% certainty that my simplification rule is correct in general. As the famous aphorism goes, "There are only two hard problems in programming: cache invalidation, naming things, and off-by-one errors."

|improve this answer|||||
$\endgroup$
  • $\begingroup$ It does seem to work with Indexed[]. Could have sworn I tried that. But it's interesting that integration only works with the assumption that x is in a certain range. Can't Mathematica make use of the fact that the function is periodic and find an integral for the general case, without the assumption? $\endgroup$ – Alemarius Nexus Nov 18 '15 at 17:35
  • $\begingroup$ @AlemariusNexus. The general case is hard. I mean, the integral of the function will not be periodic. You might be able to massage the resulting function into a form that allows you to feed it to FindSequenceFunction, perhaps, but otherwise I don't think there's any reason to expect that Mathematica could find a general form for the integral straightforwardly. I will try though. $\endgroup$ – march Nov 18 '15 at 18:07
  • $\begingroup$ Thanks a lot for your edit. This splitting of the definite integral into a sum of multiple full period integrals plus a small remainder on each side is what I thought Mathematica could do automatically. The list I'm actually using is longer than in this example (at least a few hundred values). Also, the function I'm integrating is actually interpolating values from the list. I'm not sure Mathematica will still be able to do all this automatically, but I'll experiment a bit. $\endgroup$ – Alemarius Nexus Nov 18 '15 at 20:06
2
$\begingroup$

This is a work-around for your problem. It doesn't use Part, because I don't think that will work. Instead, use Piecewise:

Clear[f]
f[x_] := Piecewise[Table[{kk, Mod[Floor[x], 5] == kk - 1}, {kk, 1, 5}]]
Plot[f[x], {x, -5, 10}, PlotRange -> {-0.1, 5.1}]
func = Integrate[f[y], {y, 0, x}, Assumptions -> {0 <= x <= 10}]

enter image description here


Here is my attempt to extract a closed-form solution. We need to massage the output into a form that gives us the sequences of coefficients of the polynomials defining the Piecewise function. First, extract the data and process it to "de-compress" the different parts (Piecewise puts two of the intervals together):

data = func[[1]] /. {expr_, HoldPattern[Or[b__Inequality]]} :> Sequence @@ ({expr, #} & /@ {b})
(* {{5 (-4 + x), 9 < x <= 10}, {5 (-2 + x), 4 < x <= 5}
  , {3 (-1 + x), 2 < x <= 3}, {3 (-1 + x), 7 < x <= 8}
  , {x, 0 < x <= 1}, {2 (2 + x), 6 < x <= 7}
  , {10 + x, 5 < x <= 6}, {2 (-3 + 2 x), 3 < x <= 4}
  , {-1 + 2 x, 1 < x <= 2}, {-11 + 4 x, 8 < x <= 9}} *)

We now sort this by the interval and extract the functions

sortedFunctions = Sort[Reverse /@ data][[All, 2]]
(* {x, -1 + 2 x, -3 + 3 x, -6 + 4 x, -10 + 5 x, 10 + x
  , 4 + 2 x, -3 + 3 x, -11 + 4 x, -20 + 5 x} *)

Finally, we collect the polynomial coefficients:

lists = CoefficientList[#, x] & /@ sortedFunctions // Transpose
(* {{0, -1, -3, -6, -10, 10, 4, -3, -11, -20}
  , {1, 2, 3, 4, 5, 1, 2, 3, 4, 5}} *)

Feeding these functions to FindSequenceFunction yields one good fit and one weird one:

FindSequenceFunction /@ lists

enter image description here

The first one makes perfect sense: it's just the Mod[x, 5] repeated. However, it doesn't seem to find a closed-form fit for the first set of coefficients. I have no notion of how good FindSequenceFunction is, so perhaps there is a simple closed form (perhaps some set of quadratics or something). In fact, here's the sequence of constant coefficients if we take the limit out to 50 instead of 10:

GraphicsRow[{ListPlot[lists[[1]]], ListLinePlot[lists[[1]]]}]

enter image description here

There is a clear pattern, but I cannot discern it.

|improve this answer|||||
$\endgroup$
  • $\begingroup$ Thanks for your suggestions. With Pillsy's ideas I can now get Mathematica to create the Piecewise automatically. I don't think FindSequenceFunction[] will help me much because the list I'm actually using is longer than in the example, and consists of random values, so the sequence function is probably too complicated. $\endgroup$ – Alemarius Nexus Nov 18 '15 at 20:11
  • $\begingroup$ @AlemariusNexus. I have an idea that might be useful, even for your more general case. I'll post the solution when I have the time. In the meantime, don't forget to upvote useful answers (when you get enough reputation), and don't forget to accept one of the answers that answered your question (although it is often a good idea to wait at least a day before accepting in order to encourage more people to answer; the more answers the better). $\endgroup$ – march Nov 18 '15 at 21:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.