This notebook loads and documents the  Integral.m  package:

Ultra permissive "MIT copyright" notice

Copyright (c) 2018 through 2020, David R. Stoutemyer 

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Purpose, references, requirements, and recommendations

Integral [...]  is a function that supplements the built-in Mathematica  Integrate [...]  function by computing more robust antiderivatives of integrands containing symbolic parameters.

For example,   Integral [(z^α),z]  returns   ({{{, ({{((z^(1+α))/(1+α)), α≠-1}, {Log[z], True}})}}) 
rather than  ((z^(1+α))/(1+α)).

Motivating examples are described by Robert M. Corless, David J. Jeffrey and David R. Stoutemyer,  Integrals of functions containing parameters, Mathematical Gazette, Volume 104, Issue 561, November 2020, pp. 412 - 426, DOI: https://doi.org/10.1017/mag.2020.96

The recommended place to store this notebook file and package  Integral.m  on your computer is your value of

FileNameJoin [{$UserBaseDirectory,  “Applications”}]

but it can be run from almost anywhere, including your Download directory as long as this file and Integral.m are stored in the same directory.

The most advanced features of Integral.m require Mathematica version 11.0 or later.  Some of the capabilities are enabled for earlier versions of Mathematica, but this has not been thoroughly tested.

To compute needed  generic  antiderivatives,  Integral [...]   optionally uses the Mathematica version of the Rubi  Int [...]  function, followed by the built-in Mathematica  Integrate [...]  function for any portions that Rubi  Int[...]  cannot determine.  To thus benefit from the union of their abilities, download then install free Rubi from

https://rulebasedintegration.org/

You can postpone that decision until after trying Integral.m

This notebook file and the associated Integral.m package were implemented by David R. Stoutemyer.

We plan to describe the implementation details in a forthcoming article.

To load the Integral.m package

1.  If the Rubi antiderivative package is installed on you computer and you want  Integral [...]  to use it, then  first  apply  ⇧⌤  to the following command, which quietly loads very quickly if properly installed :

Get ["Rubi`"]

2.  Next, apply  ⇧⌤  to the following command :

Get [FileNameJoin [{NotebookDirectory [ ],"Integral.m"}]]

that should quietly load very quickly.

3.  As a test for successful loading of  Integral.m,  executing the following input should produce an initialization message followed by a Piecewise result :

Integral[(\[FormalZ]^α),\[FormalZ]]

4.  Open a possibly-new notebook for your use of  Integral [...]   if one is not already open, because this file  LoadAndUseIntegral.nb  is write-protected to deter corruption:
      You will not be allowed to save this file, thus losing any work you do here unless you copy it then paste elsewhere.

Documentation with examples

Basic use of Integral [...]

You can use the  Integral [...]  function in all the same ways as the built-in Mathematica  Integrate [...] function.

However,  Integral [...]  can also

1.  return antiderivatives that are applicable for all values of any parameters therein.

2.  make integrals maximally continuous with respect to those parameters.

3.  optionally benefit from the union of the capabilities of the built-in Mathematica Integrate [...] function with the  Rubi  Int [...]  function if you have downloaded that antiderivative package from

https://rulebasedintegration.org/

For attempted uniqueness, this documentation uses examples that return the same result as of May 2019 whether or not Rubi is loaded

Antiderivatives are requested as

Integral [integrand, integrationVariable].

1D definite integrals are requested as

Integral [integrand,  {integrationVar,  lowerLimit,  upperLimit}],

which first tries to obtain an antiderivative of the requested type discussed in the next subsection. If successful and Integral can determine that there is no discontinuity in the integration interval, then it returns the difference in the internal limits of that antiderivative.  Otherwise Integral invokes the Mathematica  Integrate  function, which considers internal discontinuities, differentiation with respect to a parameter, contour integration, and any request for Cauchy principal values.

Integral [ ...] directy invokes  Integrate  for definite integrals specified by a region -- such as

Integral [(x^2)y,  {x, y} ∈ RegionIntersection [Disk [{0, 0}, 2], Disk [{0, 3}, 4]]] .

The next subsection demonstrates the above benefits 1 and 2:

Most important optional arguments & defaults

As indicated below, many of the default optional argument values are stored in global variables having names of the form  $Default ...

You can change those defaults with assignments such as  $DefaultParameterTreatment = Continuous .

ParameterTreatment  :>  ($DefaultParameterTreatment = Comprehensive)

The optional argument named ParameterTreatment controls how antiderivatives of parametric integrands are treated.  The alternative values are the symbols in

{Generic,  Conditional,  Comprehensive,  Continuous,   ApartContinuous}

that are successively more ambitious and time consuming :

ParameterTreatment  ->  Generic

Generic  enables you to  benefit from the union of the capabilities of the built-in Mathematica  Integrate [...]  function with the  Rubi  Int [...]  function if loaded.  Otherwise  Generic  simply invokes the Mathematica  Integrate [...]  function.

generic = Integral [(\[FormalZ]^α),\[FormalZ], ParameterTreatment->Generic]

However, this generic antiderivative quietly returns an  incorrect  antiderivative when later specialized to  α -> -1 :

generic /. α -> -1

For Generic antiderivatives, substitution does not necessarily commute with integration :

Integral [(\[FormalZ]^α)/. α -> -1,\[FormalZ], ParameterTreatment->Generic]

ParameterTreatment  ->  Conditional

ParameterTreatment -> Conditional  returns a partial function that immediately indicates what specializations of a generic antiderivative are valid before you attempt specialization :

conditional= Integral [(\[FormalZ]^α),\[FormalZ], ParameterTreatment->Conditional]

And the result of an invalid substitution  is more appropriately  Undefined  rather than ComplexInfinity :

conditional /. α -> -1

(By default, Mathematica Integrate [...] already returns ConditionalExpressions when appropriate for parametric definite integrals.)

ParameterTreatment  ->  Comprehensive

On Mathematica versions prior to 11.0, this alternative and $DefaultParameterTreatment are demoted to Conditional after issuing a warning.

For antiderivatives,  Integral [..., ParameterTreatment -> Comprehensive]  returns a Piecewise  total  function that is valid for all combinations of parameter values. For parametric integrands, the result is often a Piecewise result wherein one of the consequents is a generic antiderivative :

comprehensive = Integral [(\[FormalZ]^α), \[FormalZ], ParameterTreatment->Comprehensive]

comprehensive /.α -> -1

The following plot illustrates the discontinuity of this Piecewise result with respect to the integration variable values  \[FormalZ]=0   and with respect to parameter value  α=-1 :

PlotSurfaceAnd2DCurve[Re[((\[FormalZ]^(1+α))/(1+α))],Re[Log[\[FormalZ]]],{\[FormalZ],-2,2},{α,-3,1},{-5,5},α ->-1, Exclusions -> All]

ParameterTreatment  ->  Continuous

On Mathematica versions prior to 11.0, this alternative and $DefaultParameterTreatment are demoted to Conditional after issuing a warning.

At the expense of more computing time,  ParameterTreatment -> Continuous  attempts to return a Piecewise result that is maximally continuous :

parametricallyContinuous= Integral [(\[FormalZ]^(α )),\[FormalZ], ParameterTreatment->Continuous]

The generic first consequent  ((-1+(\[FormalZ]^(1+α)))/(1+α)) is different from the corresponding  ((\[FormalZ]^(1+α))/(1+α))  in the generic consequent of the  merely Comprehensive result.  To verify that consequent ((-1+(\[FormalZ]^(1+α)))/(1+α)) is valid :

D[((-1+(\[FormalZ]^(1+α)))/(1+α)), \[FormalZ]]

The different consequent makes the overall antiderivative continuous with respect to parameter α, as suggested by the following plot :

PlotSurfaceAnd2DCurve[Re[((-1+(\[FormalZ]^(1+α)))/(1+α))],Re[Log[\[FormalZ]]],{\[FormalZ],-2,2},{α,-3,1},{-5,5},α ->-1, Exclusions -> All]

As illustrated, the discontinuity with respect to the integration variable \[FormalZ] is unavoidable for  α < -1  .

The parametric continuity was accomplished by introducing a particular integration constant that is a function of at most the parameter alone, making its partial derivative with respect to the integration variable be 0.

Often that particular constant is 0, making a  Comprehensive  result fortuitously continuous.

But Parametrically continuous results often have more complicated consequents and sometimes have more Piecewise clauses  than merely-comprehensive results.

Although parametric continuity is not necessary for some applications, including definite integration, the Section below titled “Why continuity can matter” shows a differential equation example where parametric continuity is necessary.

ParameterTreatment  ->  ApartContinuous

At the expense of more time, and perhaps also more catastrophic cancellation when substituting floating-point numbers near a special case,  ParameterTreatment -> ApartContinuous  attempts to make the presence of an intentionally-introduced parametric or non-parametric constant more obvious in a separate term :

apartContinuous= Integral [(\[FormalZ]^α),\[FormalZ], ParameterTreatment->ApartContinuous]

SubtaskTimeLimit  :>  ($DefaultSubTaskTimeLimit = 4.0)

The optional argument named  SubtaskTimeLimit  sets the maximum allowed seconds for each Limit, Solve, FullSimplify and Simplify done within  Integral [...].

Timeouts occur most often for  ParameterTreatment -> Continuous  and  ParameterTreatment -> ApartContinuous; and the likelihood tends to increase strongly with the number of parameters in the integrand -- particularly scaling parameters such as  β  rather than translational parameters such as  γ  in integrals of the form  ∫f[β z+ γ]dz

Increasing the time limit to eliminate  PerhapsInconsequentialTimeOut  warnings often makes no difference in the result -- because  Integral [...]  tries several alternative methods, and the next method often succeeds when an early method times out.

Increasing the time limit to eliminate  MaybeConsequentialTimeOut warnings is more likely to be beneficial -- especially if the warnings are are followed by a MaybeDiscontinuous message, which might thereby be eliminated.

Advanced optional arguments & defaults

MostGeneralFirstQ  :> ($DefaultMostGeneralFirstQ = True)

MostGeneralFirstQ -> True  tries to order the most general generic case first in comprehensive Piecewise results returned by  Integral [...].

MostGeneralFirstQ -> False is slightly faster and tries to order the most general generic case last in Piecewise.  This often leads to simpler predicates overall and is closer to the way many programmers implement functions.

TrueInPiecewiseMeans  :> ($DefaultTrueInPiecewiseMeans = Automatic)

For Piecewise results from  ParameterTreatment -> Comprehensive,  ParameterTreatment -> Continuous, or ParameterTreatment -> ApartContinuous :
•  Alternative  TrueINPiecewise -> All  always Prints a line containing the meaning of the True condition.
•  Alternative  TrueINPiecewise -> None  never prints such a Message.
•  Alternative  TrueINPiecewise -> Automatic  prints such a message if and only if there are more than two clauses in a comprehensive Piecewise result.

NLimitUse  :> ($DefaultNLimitUse  = None)

Integral [...]  uses the Mathematica  Limit [...]  function to compute the constants introduced for  ParameterTreatment -> Continuous  and  ParameterTreatment -> ApartContinuous.

But  Integral [...] can optionally  try  the Mathematica  NLimit [...] function when Limit [...]  does not succeed and  NLimit [...]  is applicable.

A warning about possible incorrectness is issued if  Integral [...]  returns a result that uses a limit determined via NLimit.

NLimitUse -> None

NLimit is not used.

NLimitUse -> InexactNumberQ

If NLimit [...] returns a value  (Subscript[z, 0])  and  InexactNumberQ [(Subscript[z, 0])]  returns  True,  then (Subscript[z, 0]) is used in the antiderivative.

NLimitUse -> ExactNumberQ

If NLimit [...] returns a value  (Subscript[z, 0])  and  InexactNumberQ [(Subscript[z, 0])]  returns  True  and  Integral [...]  guesses a plausible non-float constant  (Subscript[w, 0])  that the float approaches for increasing WorkingPrecision, then  (Subscript[w, 0])  is used in the antiderivative rather than (Subscript[z, 0]).

Otherwise the attempt to compute that limit is abandoned.

If the  Propose [...]  function in the package freely downloadable from

AskConstants.org

is loaded before package  Integral.m,  then Integral [...] uses the  ProposeBestOrInput [...]  function to try guessing the best plausible non-float that closely approximates the float.

Otherwise Integral [...] uses the built-in Mathematica  Rationalize [...]  function, which is limited to proposing rational constants.

NLimitUse -> Automatic

This alternative is like the  ExactNumberQ  alternative,  except that if NLimit [...] succeeds in returning a float but  Integral [...]  cannot determine a plausible non-float that closely approximates the float, then the float is used in the antiderivative.

GenericIntegrationFunction  :> (DefaultGenericIntegrationFunction  = IntThenIntegrate)

DefaultGenericIntegrationFunction  is assigned the value  Integrate  if package  Rubi.m  is not loaded  before  package  Integral.m .

For computing generic antiderivatives, the alternatives are :

Alternative  Integrate  uses only the built-in Mathematica Integrate [...] function.

Alternative  Int  uses only the Rubi  Int [...]  function, issuing an Error message if package  Rubi.m  has not been loaded  before  package  Integral.m.

Alternative  IntThenIntegrate  first uses the Rubi  Int [...]  function if the Rubi package is loaded before Integral.m, but  Integral [...]  then applies  Integrate [...]  to any pieces that Rubi was unable to integrate.

PrincipalValue -> False

This optional argument is described in the Mathematica documentation for Integrate [...].

Integral [...] merely passes this argument to Integrate [...]  if and only if Integral [...]  invokes Integrate [...].

GenerateConditions -> True

This optional argument is described in the Mathematica documentation for Integrate [...].

Integral [...] merely passes this argument to Integrate [...]  if and only if Integral [...]  invokes Integrate [...].

The default value of GenerateConditions for  Integrate [...]  is True, but it applies only to definite integrals for Mathematica through version 12.0.

In contrast, Integral [...] also generates conditions if needed for antiderivatives whenever the value of optional parameter  ParameterTreatment  is not  Generic.

Assumptions  :> $Assumptions

• This optional argument is described in the Mathematica documentation for Integrate [...].

• It is generally more  appropriate and effective to use

Assuming [assumptions,  Integral [...]]

rather than

Integral [...,  Assumptions -> ...]

because Assuming [assumptions, ...]  consistently applies assumptions throughout the steps done by  Integral [...],  whereas  using Assumptions -> ...  applies only when Integrate [...]  is used by  Integral [...], which might be never unless the value of optional parameter   GenericIntegrationFunction   is  Integrate.

IntThenIntegrateUsedIntegrateQ  :> (DefaultIntThenIntegrateUsedIntegrateQ = False)

If  IntThenIntegrateUsedIntegrateQ  is True, then a message is Printed if and only if :

•  The Rubi package is loaded, and
•  Optional argument  GenericIntegrationFunction  has value   IntThenIntegrate,  and
•  Int [...]  needed to be followed by  Integrate [...], and
•  Integrate [...] fully succeeded.

At most one such message is issued per invocation of  Integral [...]  for an antiderivative.

The purpose of the  True  setting is to learn examples that  Int [...]  cannot fully complete but  Integrate [...]  can complete.

ShowIntegralStepsQ  :> ($DefaultShowIntegralStepsQ  = False)

ShowIntegralStepsQ -> True  for is for my debugging and tuning. This option is not a polished tutorial feature such as the ShowStep global variable in the Rubi Int function.

(The Rubi ShowStep feature is usable with Integral [...], but it is likely to be confusing when the  ParameterTreatment  has one of the values Comprehensive,  Continuous, or ApartContinuous, which often involve computing several generic antiderivatives.)

Assuming [...,  Integral [...]]

Using  Integral [...]  within the Mathematica  Assuming [..., ...]  function often returns a  ConditionalExpression [...]  that might or might not contain a piecewise result.

Assuming [α > -3, Integral [(\[FormalZ]^α), \[FormalZ], ParameterTreatment->Comprehensive]]

Assuming [α > -1, Integral [(\[FormalZ]^α), \[FormalZ], ParameterTreatment->Comprehensive]]

If an example generates new constants  C[1],  C[2], ...;  and you are interested only in particular instances such as  C[1] -> 0  and  C[2] -> 1,  then  Integral [...]  is more likely to succeed and to do so quickly if you do

Assuming [C[1] == 0 && C[2] == 1,  Integral [...]]

rather than doing a replacement after obtaining a more general result.

$Assumptions

The default value of the Mathematica global variable named  $Assumptions  is  True.

A   worst-practice  alternative to using the  best practice  form  Assuming [constraint,  subexpression]  is a top-level alteration of this initial True  value:

$Assumptions = constraint;
      ...
      ... subexpression ...
      ...

There is a great risk of forgetting to restore  $Assumptions to True as soon as the constraint is no longer needed.

An  intermediate-practice  alternative is to use

Block [{$Assumptions = ...,
                     ...},
                   ...
                   ... subexpression ...;
                   ... ]

which completely replaces the temporally-prior value of $Assumptions when the Block is entered, but automatically restores  $Assumptions  to that prior value immediately after the Block [...] is exited.

Unwanted results or warnings are likely when conflicting assumptions are nested. Therefore my main use of the  Block [...]  alternative is within a function such as  Integral [...]  to insulate my calculations from unwanted assumptions by users of my function as follows:

Block [{$Assumptions = True,
                     ...},
            ... ;
            ...  Assuming [(Subscript[constraint, 1]),  (Subscript[subexpression, 1])];
            ... ;
            ...   Assuming [(Subscript[constraint, 2]),  (Subscript[subexpression, 2])];
            ... ]

With [...]  and Module [...]  are less likely than  Block [...]  to do what you want, because the definitions of functions such as  Integral [...]  and  Simplify [...]  are  outside  the  lexical  scope of your  With [...]  or  Module [...].

Booleans, Integers, Primes, Rationals, Algebraics, Reals and Complexes

As with most Mathematica functions, the default domain for the integration variable and parameters is the  Complexes.

However, you can use   Assuming [..., ...]   to restrict the integration variable and parameters to the  Reals.  For example,

Assuming [Element [\[FormalZ]|α, Reals], Integral [(\[FormalZ]^α), \[FormalZ], ParameterTreatment->Comprehensive]]

In contrast, for the Complexes :

Integral [(\[FormalZ]^α), \[FormalZ], ParameterTreatment->Comprehensive]

As illustrated by this result, restricting parameters or the integration variable to the Reals often results in a less concise result, and it usually requires more computing time.

Thus since a result that is valid for the Complexes is also valid for the Reals, I recommend first trying  Integral [...]  without such assumptions.

Integral [...]  uses a undocumented Mathematica  11  Simplify`FunctionSingularities [...]   function that is currently restricted to  all  of the variables in an expression being  Reals  or  all  being  Complexes.

Therefore I recommend making the integration variable and  all  parameters be Reals or making  none  of them Reals.

You might sometimes benefit from making some parameters be elements of the  Integers,  particularly if they occur in exponents or in subexpressions such as  Sin [n π].

There might even be examples that benefit rather than suffer from declaring some parameters to be  Booleans, Primes, Rationals  or Algebraics.

But for such examples I recommend trying first without such an assumption, because such restrictions often increase the complexity and computing time for no significant benefit.

When can continuity matter ?

All CAS that I am familiar with currently return generic general solutions to ordinary differential equations without boundary conditions.  For example, ...

FullSimplify [DSolve [y''[t]+(ω^2)y[t]==Cos[κ t], y[t],t]]

But the specialization of the particular portion of the above generic solution for the resonance as  ω -> κ  is incorrect ...

Limit[-((Cos[t κ])/((κ^2)-(ω^2))), ω->κ]

If  we  instead compute the particular portion of the generic general solution with parametrically-continuous comprehensive antiderivatives ...

y1 =Cos [κ t];
y2 =Sin[κ t];
Wrongskian = Simplify [y1*D[y2,t]-y2*D[y1,t]];

parametricallyContinuousParticular = FullSimplify[y2*Integral [((y1 Cos[ω t])/Wrongskian), t,ParameterTreatment -> Continuous]-y1*Integral [((y2 Cos[ω t])/Wrongskian),t,ParameterTreatment -> Continuous]]

The first clause of this Piecewise result is the generic particular solution.

The second clause is its specialization to the particular solution for  ω -> κ.

Each of the Integrals in the argument of FullSimplify generated a  “In the ..., True means ...”  message.