(* :Title: Ito's Lemma *)

(* :Author: Mark Fisher *)

(* :Context: ItosLemma` *)

(* :Mathematica Version: 3.0 *)

(* :History: Version 1.0, June 1999.
			 Version 1.1, June 2000.
			 	Simplified ItoD.
			 	Added option Scalarize to Diffusion.
			 	Added new versions of ItoMake for simplified entry that
			 		use new global symbols DriftSymbol and DiffusionSymbol.
			 Version 1.1.1, April 2003. Minor changes.
*)

(* :Sources:
A preliminary version was inspired by the package "Diffusion" written
by Steele and Stine (described in "Mathematica and Diffusions",
chapter 9 of "Economic and Financial Modeling with Mathematica", 1993,
Springer-Verlag, edited by Hal R. Varian).

For an introduction to stochastic differential equations and Ito's lemma,
see Bernt Øksendal, Stochastic Differential Equations: An Introduction with
Applications, Springer-Verlag (currently in its 5th edition).
*)

(* :Keywords: Ito's lemma, stochastic calculus, stochastic differential
equations, Brownian motion *)

(* :Summary:
This package implements Ito's lemma for any number of Ito processes
with any number of arbitrarily-correlated Brownian motions.

There are two main functions in this package: ItoMake and ItoD. ItoMake
should be used to declare an Ito process prior to using ItoD to compute the
stochastic derivative. For example (assuming the global symbols have been
set as in the housekeeping example below), ItoMake[x[t], \[Mu], \[Sigma]]
creates the global rule x[t + dt] -> x[t] + \[Mu] dt + \[Sigma] Subscript[dB, 1].
ItoD[f[x[t], t]] constructs a Taylor series for f[x[t + dt], t + dt] around
dt = 0 and Subscript[dB, 1] = 0. The series is first-order in dt and
second-order in Subscript[dB, 1], where "Ito multiplication rules" are
applied to the second-order term.

The functions Drift and Diffusion can be used to extract the drift and
diffusion from the output of ItoD.

(***** Housekeeping Notice *****)

The package relies on six global symbols: TimeSymbol, TimeIncrement,
BrownianIncrement, CorrelationSymbol, DriftSymbol, and DiffusionSymbol.
They can be set at the beginning of each session (after loading ItosLemma)
to more convenient symbols. Here is an example:

Clear[t, dt, dB, \[Rho], \[Mu], \[Sigma]]
{TimeSymbol, TimeIncrement, BrownianIncrement,
	CorrelationSymbol, DriftSymbol, DiffusionSymbol} =
	{t, dt, dB, \[Rho], \[Mu], \[Sigma]}

One way to make these assignments automatic is to copy the preceding
code to the bottom of this file, after the EndPackage[] statement.

There is a "brief explanation to the code" in the Notebook ItosLemma.nb.

*)

(* :Examples: Supplied in the Notebook ItosLemma.nb. *)

BeginPackage["ItosLemma`"]

ItosLemma::usage = "ItosLemma.m is a package that implements Ito's Lemma.
The package uses six global symbols: TimeSymbol, TimeIncrement,
BrownianIncrement, CorrelationSymbol, DriftSymbol, and DiffusionSymbol.
They can be defined in terms of more convenient symbols.\n
Example:\n
Clear[t, dt, dB, \[Rho], \[Mu], \[Sigma]];\n
{TimeSymbol, TimeIncrement, BrownianIncrement, CorrelationSymbol,
	DriftSymbol, DiffusionSymbol} = {t, dt, dB, \[Rho], \[Mu], \[Sigma]}"

TimeSymbol::usage = "TimeSymbol is the symbol that represents time."

TimeIncrement::usage = "TimeIncrement is the symbol that represents an
infinitesimal change in time."

BrownianIncrement::usage = "BrownianIncrement is the symbol that
represents an infinitesimal change in a Brownian motion."

CorrelationSymbol::usage = "CorrelationSymbol is the symbol that
represents the correlation between Brownian motions."

DriftSymbol::usage = "DriftSymbol is the symbol that represents the
drift."

DiffusionSymbol::usage = "DiffusionSymbol is the symbol that represents
the diffusion."

ItoD::usage = "ItoD[expr] applies Ito's lemma to expr. ItoD takes the
option OrthogonalBrownians."

RelativeItoD::usage = "RelativeItoD[expr] computes ItoD[expr]/expr."

OrthogonalBrownians::usage = "OrthogonalBrownians is an option for ItoD. The
default setting is OrthogonalBrownians -> True."

SuppressTime::usage = "SuppressTime is an option for ItoD that specifies
whether to suppress the dependence on time in the output. The default
setting is SuppressTime -> True."

ItoMake::usage = "ItoMake[x[args], mu, sigma] associates a rule with the
(ultimate) head of the x[args]. The TimeSymbol should be one of the
arguments; for example, ItoMake[x[t], \[Mu], \[Sigma]]. For multiple
Brownians, sigma may be a list."

ItoMakeFromItoD::usage = "ItoMakeFromItoD[name, expr] calls
ItoMake[x[args], Drift[dx], Diffusion[dx]], where dx is the output from
ItoD. ItoMakeFromItoD passes the option BrownianList to Diffusion."

IncludeArguments::usage = "IncludeArguments is an option for ItoMake (and
related functions) that specifies whether to include the arguments in the
drift and diffusion specifications. IncludeArguments is only used in
vector versions of ItoMake-related functions."

RelativeItoMake::usage = "RelativeItoMake[name, mu, sigma] calls
ItoMake[name, name mu, name sigma]."

RelativeItoMakeFromItoD::usage = "RelativeItoMakeFromItoD[name, expr] calls
ItoMake[name, name Drift[expr], name Diffusion[expr]]. RelativeItoMakeFromItoD
passes the option BrownianList to Diffusion."

ExponentialItoMake::usage = "ExponentialItoMake[name, mu, sigma] calls
ItoMake[name, name (mu + sigma^2/2), name sigma]."

ExponentialItoMakeFromItoD::usage = "ExponentialItoMakeFromItoD[name, expr]
calls ItoMake[name (Drift[expr] + (1/2)Diffusion[expr].Diffusion[expr]),
Diffusion[expr]]. ExponentialItoMakeFromItoD passes the option BrownianList
to Diffusion."

VectorItoMake::usage = "VectorItoMake[name, n] makes a vector of n Ito
processes."

Drift::usage = "Drift[expr] returns the drift of an Ito process."

Diffusion::usage = "Diffusion[expr] returns the diffusion of an Ito
process. Diffusion takes the options BrownianList and Scalarize."

BrownianList::usage = "BrownianList is an option for Diffusion. The default
setting is BrownianList -> Automatic."

Scalarize::usage = "Scalarize is an option for Diffusion. The default
setting is Scalarize -> True, which specifies that a length-one Diffusion
should be returned as a scalar."

Begin["`Private`"]
(*****************  code starts here **********************)

(***** main engine *****)

Options[ItoD] = {OrthogonalBrownians -> True, SuppressTime -> True}

SetAttributes[ItoD, Listable]

ItoD[expr_, opts___?OptionQ] :=
	Module[{t, dt, dB, rho, suppress, ortho, zerorule,
		dtexpr, brownians, dtime, dbrown, d2brown, timedrift,
		diffusion, jensen},
	(* get the global values for these symbols *)
	{t, dt, dB, rho} = {TimeSymbol, TimeIncrement, BrownianIncrement,
		CorrelationSymbol};
	{suppress, ortho} = {SuppressTime, OrthogonalBrownians} /.
		{opts} /. Options[ItoD];
	zerorule = Subscript[dB, _] | dt -> 0;
	(* identify the Ito processes *)
	dtexpr = expr /. t -> t + dt;
	(* find the brownians *)
	(* if there are no brownians, make one up *)
	brownians = Union[Cases[dtexpr, Subscript[dB, _], {0, Infinity}]];
	If[brownians === {}, brownians = {Subscript[dB, 1]}];
	(* compute the derivatives *)
	dtime = D[dtexpr, dt];
    dbrown = D[dtexpr, #]& /@ brownians;
	d2brown = D[dbrown, #]& /@ brownians;
	(* compute the "ito-taylor series" *)
	timedrift = (dtime /. zerorule) dt;
	diffusion = (dbrown /. zerorule) . brownians;
	jensen = (1/2) *
		Expand[brownians . (d2brown /. zerorule) . brownians] /.
			{Subscript[dB, _]^2 -> dt,
			 Subscript[dB, i_] Subscript[dB, j_] ->
				If[TrueQ[ortho],
					0,
					Subscript[rho, i, j] dt]};
	(* assemble the parts *)
	(diffusion + Collect[timedrift + jensen, dt]) /.
		If[TrueQ[suppress], f_[t] -> f, {}]
	]

(* related function to simplify output in special cases *)

RelativeItoD[expr_, opts___?OptionQ] :=
	Collect[ItoD[expr, opts]/expr /.
		If[TrueQ[SuppressTime /. {opts} /. Options[ItoD]],
			(* then *)
			f_[TimeSymbol] -> f,
			(* else *)
			{}],
		TimeIncrement, Simplify]

(***** extractors *****)

Drift[expr_] := Coefficient[expr, TimeIncrement]

Diffusion::lost = "BrownianList omitted ``."

Options[Diffusion] = {BrownianList -> Automatic, Scalarize -> True}

Diffusion[expr_, opts___?OptionQ] :=
	Module[{dB, blist, scalar, found, brownians, lost},
	(* get the global value for this symbol *)
	dB = BrownianIncrement;
	{blist, scalar} = {BrownianList, Scalarize} /. {opts} /.
		Options[Diffusion];
	found = Union[Cases[expr, Subscript[dB, _], {0, Infinity}]];
	If[TrueQ[blist === Automatic],
		(* then *)
		brownians = found,
		(* else *)
		brownians = blist;
		lost = Complement[found, blist];
		If[lost =!= {}, Message[Diffusion::lost, lost]]];
	If[TrueQ[scalar] && Length[brownians] === 1,
		(* then *)
		Coefficient[expr, First @ brownians],
		(* else *)
		Coefficient[expr, #]& /@ brownians]
	]

(***** constructor *****)

ItoMake::notime = "TimeSymbol `1` does not appear as an argument in `2`."
ItoMake::badhead = "The Head of `1` matches one of `2`, `3`, or `4`."
ItoMake::badarg = "One or more arguments of `1` matches either `2` or `3`."

(* only used in vector functions (see below) *)
Options[ItoMake] = {IncludeArguments -> False}

ItoMake[x_[args__], mu_, sig_] :=
	Module[{t, dt, dB, diffusion, head, lhs},
	(* get the global values for these symbols *)
	{t, dt, dB} = {TimeSymbol, TimeIncrement, BrownianIncrement};
	If[FreeQ[{args}, t],
		Message[ItoMake::notime, t, x[args]]; Return[$Failed]];
	If[MatchQ[x, t | dt | dB],
		Message[ItoMake::badhead, x[args], t, dt, dB]; Return[$Failed]];
	If[MemberQ[{args}, dt | dB, Infinity],
		Message[ItoMake::badarg, x[args], dt, dB]; Return[$Failed]];
	diffusion = Switch[sig,
		_List, 	sig . Array[Subscript[dB, #]&, Length[sig]],
		_, 		sig Subscript[dB, 1]];
	(* find the ultimate Head *)
	head = FixedPointList[Head, x][[-3]];
	lhs = Block[Evaluate[{head}],
		(* construct the pattern and Hold it *)
		Hold @ Evaluate[
			If[# === t, # + dt, Pattern[#, Blank[]]]& /@ x[args]
			]];
	(* define the rule and return the SDE *)
	(Set @@ Append[lhs, x[args] + mu dt + diffusion]) - x[args]
	]

(*****************************************************************)
(* ItoMake-related functions to simplify input for special cases *)
(*****************************************************************)

RelativeItoMake[x_[args__], mu_, sig_] :=
	ItoMake[x[args], x[args] mu, x[args] sig]

ExponentialItoMake[x_[args__], mu_, sig_]:=
	ItoMake[x[args], x[args] (mu + sig.sig/2) /. Dot -> Times, x[args] sig]

(* make Ito process from output of ItoD *)

ItoMakeFromItoD[x_[args__], expr_, opts___?OptionQ] :=
	With[{diff = Diffusion[expr, opts]},
	ItoMake[x[args], Drift[expr], If[diff === {}, {0}, diff]]
	]

RelativeItoMakeFromItoD[x_[args__], expr_, opts___?OptionQ] :=
	ItoMake[x[args], x[args] Drift[expr],
		x[args] Diffusion[expr, opts]]

ExponentialItoMakeFromItoD[x_[args__], expr_, opts___?OptionQ] :=
	With[{diff = Diffusion[expr, Scalarize -> False, opts]},
		ItoMake[x[args], x[args] (Drift[expr] + (1/2) diff.diff),
			x[args] diff]
		]

(* scalar process with vector browians *)

ItoMake[x_[args__], mu_, sig_, n_Integer, opts___?OptionQ] :=
	ItoMake[x[args], ##]& @@ makeargs[x[args], mu, sig, n, opts]

RelativeItoMake[x_[args__], mu_, sig_, n_Integer, opts___?OptionQ] :=
	RelativeItoMake[x[args], ##]& @@ makeargs[x[args], mu, sig, n, opts]

ExponentialItoMake[x_[args__], mu_, sig_, n_Integer, opts___?OptionQ] :=
	ExponentialItoMake[x[args], ##]& @@ makeargs[x[args], mu, sig, n, opts]

(* auxiliary function *)
makeargs[x_[args__], mu_, sig_, n_, opts___?OptionQ] :=
	{mu[x][args], Array[Subscript[sig, #][x][args]&, n]} /. 
		If[TrueQ[IncludeArguments /. {opts} /. Options[ItoMake]], {},
			f_[x][args] :> f[x]]

(* vector Ito processes *)

(* n Brownian motion processes *)
VectorItoMake[x_[args__], 0, 1, n_Integer] :=
	Table[ItoMake[Subscript[x, i][args], 0,
		Table[If[i == j, 1, 0], {j, n}]], {i, n}]

(* n Ito processes, n Brownians *)
VectorItoMake[x_[args__], mu_, sig_, n_Integer, opts___?OptionQ] :=
	Table[ItoMake[Subscript[x, i][args], mu, sig, n, opts], {i, n}]

(* m Ito processes, n Brownians *)
VectorItoMake[x_[args__], mu_, sig_, {m_Integer, n_Integer},
		opts___?OptionQ] :=
	Table[ItoMake[Subscript[x, i][args], mu, sig, n, opts], {i, m}]

(* use default symbols for drift and diffusion *)

(#[x_[args__]] := #[x[args],
	Subscript[DriftSymbol, x], Subscript[DiffusionSymbol, x]])& /@
		{ItoMake, RelativeItoMake}

Options[ExponentialItoMake] = {Global`OverTilde -> True}

ExponentialItoMake[x_[args__], opts___?OptionQ] :=
	Module[{tilde, fun},
	tilde = Global`OverTilde /. {opts} /. Options[ExponentialItoMake];
	fun = If[TrueQ[tilde], Global`OverTilde, Identity];
	ExponentialItoMake[x[args],
			Subscript[fun[DriftSymbol], x],
			Subscript[DiffusionSymbol, x]]
	]

(#[x_[args__], n_Integer, opts___?OptionQ] := #[x[args],
	DriftSymbol, DiffusionSymbol, n, opts])& /@
		{ItoMake, RelativeItoMake, ExponentialItoMake}

VectorItoMake[x_[args__], n_Integer, opts___?OptionQ] :=
	VectorItoMake[x[args], DriftSymbol, DiffusionSymbol, n, opts]

VectorItoMake[x_[args__], {m_Integer, n_Integer}, opts___?OptionQ] :=
	VectorItoMake[x[args], DriftSymbol, DiffusionSymbol, {m, n}, opts]

End[]
EndPackage[]
