accepts a path ie [ ( t x_t ) ] and transforms it into [ ( t f ( x ) ] .

def transform_path(path,f):
    "accepts a path, ie [(t,x_t)], and transforms it into [(t,f(x)]."
    return [(p[0],f(p[1])) for p in path]

def transform_path ( path , f ) : return [ ( p [ 0 ] , f ( p [ 1 ] ) ) for p in path ]


This needs to solve the following : E [ min ( N ) | T_1 + T_2 + .. T_N > = t ] < = > E [ N | T_1 + T_2 + .. T_ { N - 1 } < t T_1 + T_2 + .. T_N > = t ] where we can sample T only .

def _mean_number_of_jumps(self,t, n_simulations = 10000):
        self._check_time(t)
        """
        This needs to solve the following:
          E[ min(N) | T_1 + T_2 + .. T_N >= t ]
        <=>
          E[ N | T_1 + T_2 + .. T_{N-1} < t, T_1 + T_2 + .. T_N >= t ]
        where we can sample T only.

        """
        warnings.warn("Attn: performing MC simulation. %d simulations."%n_simulations)
        v = np.zeros( n_simulations )
        continue_ = True
        i = 1
        t = t - self.startTime #should be positive.
        size = v.shape[0]
        sum = 0
        sum_sq = 0
        while continue_:
            v += self.T.rvs( size )
            #count how many are greater than t
            n_greater_than_t = (v>=t).sum()
            sum += n_greater_than_t*i

            v = v[v < t]
            i+=1
            size = v.shape[0]
            empty = size == 0

            if empty:
                continue_ = False

            if i > 10000:
                warnings.warn("The algorithm did not converge. Be sure 'T' is a non negative random \
                                variable, or set 't' lower.")
                break

        return sum/n_simulations


the forward recurrence time RV is the time need to wait till the next event after arriving to the system at a large future time

def forward_recurrence_time_pdf(self,x):
        "the forward recurrence time RV is the time need to wait till the next event, after arriving to the system at a large future time"
        return self.renewal_rate*self.T.sf(x)

def forward_recurrence_time_pdf ( self , x ) : return self . renewal_rate * self . T . sf ( x )


def backwards_recurrence_time_pdf(self,x):
        "the backwards recurrence time is the time since the last event after arriving to the system at a large future time"
        return self.forward_recurrence_time_pdf(x)

def backwards_recurrence_time_pdf ( self , x ) : return self . forward_recurrence_time_pdf ( x )


def forward_recurrence_time_cdf(self,x):
        "the forward recurrence time RV is the time need to wait till the next event, after arriving to the system at a large future time"
        return int.quad(self.forward_recurrence_time_pdf, 0, x)

def forward_recurrence_time_cdf ( self , x ) : return int . quad ( self . forward_recurrence_time_pdf , 0 , x )


def backward_recurrence_time_cdf(self,x):
        "the backwards recurrence time is the time since the last event after arriving to the system at a large future time"
        return int.quad(self.forward_recurrence_time_pdf, 0, x)

def backward_recurrence_time_cdf ( self , x ) : return int . quad ( self . forward_recurrence_time_pdf , 0 , x )


the RV distributed according to the spread_pdf is the ( random ) length of time between the previous and next event / jump when you arrive at a large future time .

def spread_pdf(self,x):
        """the RV distributed according to the spread_pdf is the (random) length of time between the previous and next event/jump
         when you arrive at a large future time."""
        try:
            return self.renewal_rate*x*self.T.pdf(x)
        except AttributeError:
            return self.renewal_rate*x*self.T.pmf(x)

def spread_pdf ( self , x ) : try : return self . renewal_rate * x * self . T . pdf ( x ) except AttributeError : return self . renewal_rate * x * self . T . pmf ( x )


parameters : time : an interable that returns floats OR if a single float returns ( path jumps ) up to time t . N : the number of paths to return . negative N returns an iterator of size N . returns : path : the sample path at the times in times jumps : the times when the process jumps Scales linearly with number of sequences and time .

def _sample_path(self,time, N =1):
        """
        parameters:
            time:  an interable that returns floats OR if a single float, returns (path,jumps) up to time t.
            N: the number of paths to return. negative N returns an iterator of size N.
        returns:
            path: the sample path at the times in times
            jumps: the times when the process jumps

        Scales linearly with number of sequences and time.

        """
        if N < 0:
            return self._iterator_sample_paths(time, -N)

        else:
            return self.__sample_path( time, N )

def _sample_path ( self , time , N = 1 ) : if N < 0 : return self . _iterator_sample_paths ( time , - N ) else : return self . __sample_path ( time , N )


Generate N simulations of jump times prior to some time t > startTime . parameters : t : the end of the processes . N : the number of samples to return - N to return a generator of size N returns : An irregular numpy array with first dimension N and each element an np . array with random length . example : >> print renewal_ps . generate_sample_jumps ( 1 N = 2 ) np . array ( [ [ 2 2 . 5 3 . 0 ] [ 1 . 0 2 . 0 ] ] dtype = 0bject )

def _sample_jumps(self,t, N=1):
        """
        Generate N simulations of jump times prior to some time t > startTime.

        parameters:
            t: the end of the processes.
            N: the number of samples to return, -N to return a generator of size N
        returns:
            An irregular numpy array, with first dimension N, and each element an np.array
            with random length.

        example:
        >> print renewal_ps.generate_sample_jumps( 1, N=2)
        np.array( [ [2,2.5,3.0], [1.0,2.0] ], dtype=0bject )

        """
        if N<0:
            return (  self.__sample_jumps(t) for i in xrange(-N) )
        else:
            return np.asarray( [ self.__sample_jumps(t) for i in xrange(N) ] )

def _sample_jumps ( self , t , N = 1 ) : if N < 0 : return ( self . __sample_jumps ( t ) for i in xrange ( - N ) ) else : return np . asarray ( [ self . __sample_jumps ( t ) for i in xrange ( N ) ] )


Generate the position of the process at time t > startTime parameters : N : number of samples t : position to sample at . returns : returns a ( n ) np . array

def _sample_position(self,t, N = 1):
        """
        Generate the position of the process at time t > startTime
        parameters:
            N: number of samples
            t: position to sample at.
        returns:
            returns a (n,) np.array
        """
        v = np.zeros( N )
        iv = np.ones( N, dtype=bool )
        x = self.startPosition*np.ones( N)
        continue_ = True
        i = 1
        t = t - self.startTime #should be positive.
        size = v.shape[0]
        while continue_:
            n_samples = iv.sum()
            v[iv] += self.T.rvs( n_samples )

            #if the next time is beyond reach, we do not add a new jump
            iv[ v >= t] = False
            n_samples = iv.sum()

            x[iv] += self.J.rvs( n_samples )

            size = iv.sum() #any left?
            empty = size == 0

            i+=1
            if empty:
                continue_ = False

            if i > 10000:
                warnings.warn("The algorithm did not converge. Be sure 'T' is a non negative random \
                                variable, or set 't' to a smaller value.")
                break

        return x


recall that a conditional poisson process N_t | N_T = n ~ Bin ( n t / T )

def _mean(self,t):
        """
        recall that a conditional poisson process N_t | N_T=n ~ Bin(n, t/T)
        """
        if not self.conditional:
            return self.startPosition + self.rate*(t-self.startTime)
        else:
            return self.endPosition*float(t)/self.endTime

def _mean ( self , t ) : if not self . conditional : return self . startPosition + self . rate * ( t - self . startTime ) else : return self . endPosition * float ( t ) / self . endTime


parameters : t : a time > startTime ( and less than endTime if present ) . returns : a float . recall that a conditional poisson process N_t | N_T = n ~ Bin ( n t / T )

def _var(self,t):
        """
        parameters:
            t: a time > startTime (and less than endTime if present).
        returns:
            a float.

        recall that a conditional poisson process N_t | N_T=n ~ Bin(n, t/T)

        """
        if self.conditional:
            return self.endPosition*(1-float(t)/self.endTime)*float(t)/self.endTime
        else:
            return self.rate*(t-self.startTime)

def _var ( self , t ) : if self . conditional : return self . endPosition * ( 1 - float ( t ) / self . endTime ) * float ( t ) / self . endTime else : return self . rate * ( t - self . startTime )


Probably to be deleted . T : a float N : the number of sample paths Returns : an ( N ) np . array of jump times .

def _sample_jumps(self, t, N=1):
        """
        Probably to be deleted.
            T:  a float
            N: the number of sample paths
        Returns:
            an (N,) np.array of jump times.
        """
        if N<0:
            return ( self.__sample_jumps(t) for i in xrange(-N) )
        else:
            return np.asarray( [self.__sample_jumps(t) for i in xrange(N)] )


this method calls self . _transition_pdf ( x t y ) in the subclass

def transition_pdf(self,t,y):
        self._check_time(t)
        "this method calls self._transition_pdf(x,t,y) in the subclass"
        try:
            if not self.conditional:
                return self._transition_pdf(self.startPosition, t-self.startTime, y)
            else:
                return self._transition_pdf(self.startPosition, t-self.startTime, y)*self._transition_pdf(y, self.endTime-t, self.endPosition)\
                        /self._transition_pdf(self.startPosition,self.endTime - self.startTime, self.endPosition)
        except AttributeError:
            raise AttributeError("Attn: transition density for process is not defined.")


This function calculates the expected value of E [ X_t | F ] where F includes start conditions and possibly end conditions .

def expected_value(self,t, f= lambda x:x, N=1e6):
        """
        This function calculates the expected value of  E[ X_t | F ] where F includes start conditions and possibly end conditions.

        """
        warnings.warn( "Performing Monte Carlo with %d simulations."%N)
        self._check_time(t)
        if not self.conditional:
            return f( self.sample_position(t, N) ).mean()
        else:
            #This uses a change of measure technique.
            """
            sum=0
            self.conditional=False
            for i in range(N):
                X = self.generate_position_at(t)
                sum+=self._transition_pdf(X,self.endTime-t,self.endPosition)*f(X)
            self.conditional=True
            """
            x = self.sample_position(t, N)
            mean = (self._transition_pdf(x,self.endTime-t,self.endPosition)*f(x)).mean()
            return mean/self._transition_pdf(self.startPosition, self.endTime-self.startTime, self.endPosition)


if _get_position_at () is not overwritten in a subclass this function will use euler scheme

def sample_position(self,t, N=1):
        """
        if _get_position_at() is not overwritten in a subclass, this function will use euler scheme
        """

        self._check_time(t)
        return self._sample_position(t, N)

def sample_position ( self , t , N = 1 ) : self . _check_time ( t ) return self . _sample_position ( t , N )


var = SampleVarStat () for i in range ( 10000 ) : var . push ( self . generate_position_at ( t )) return var . get_variance ()

def _var(self,t, N = 1e6):
        """
        var = SampleVarStat()
        for i in range(10000):
            var.push(self.generate_position_at(t))
        return var.get_variance()
        """
        return self.sample_position(t, n).var()

def _var ( self , t , N = 1e6 ) : return self . sample_position ( t , n ) . var ( )


times is an array of floats . The process needs the methods drift () and diffusion () defined .

def Euler_scheme(self, times,delta=0.001):
        """
        times is an array of floats.
        The process needs the methods drift() and diffusion() defined.
        """
        warnings.warn("Attn: starting an Euler scheme to approxmiate process.")

        Nor = stats.norm()
        finalTime = times[-1]
        steps = int(finalTime/delta)
        t = self.startTime
        x=self.startPosition
        path=[]
        j=0
        time = times[j]
        for i in xrange(steps):
            if t+delta>time>t:
                delta = time-t
                x += drift(x,t)*delta + np.sqrt(delta)*diffusion(x,t)*Nor.rvs()
                path.append((x,time))
                delta=0.001
                j+=1
                time = times[j]
            else:
                x += drift(x,t)*delta + np.sqrt(delta)*diffusion(x,t)*Nor.rvs()
                t += delta

        return path


def _sample_position(self,t, n=1):
        """
        This incorporates both conditional and unconditional
        """
        return self.mean(t) + np.sqrt(self.var(t))*self.Nor.rvs(n)

def _sample_position ( self , t , n = 1 ) : return self . mean ( t ) + np . sqrt ( self . var ( t ) ) * self . Nor . rvs ( n )


def sample_path(self,times, N= 1, return_normals = False):
        "the parameter Normals = 0 is used for the Integrated OU Process"
        if not self.conditional:
            path=np.zeros( (N,len(times)) )
            times = np.insert( times, 0,self.startTime)
            path[ :, 0] = self.startPosition

            deltas = np.diff(times )
            normals = np.random.randn( N, len(times)-1 )
            x = self.startPosition*np.ones( N)
            sigma = np.sqrt(self.sigma**2*(1-np.exp(-2*self.theta*deltas))/(2*self.theta))
            for i, delta in enumerate(deltas):
                mu = self.mu + np.exp(-self.theta*delta)*(x-self.mu)
                path[:, i] = mu + sigma[i]*normals[:,i]
                x = path[:,i]
                """
                It would be really cool if there was a numpy func like np.cumf( func, array )
                that applies func(next_x, prev_x) to each element. For example, lambda x,y: y + x
                is the cumsum, and lambda x,y: x*y is the cumprod function.
                """
            if return_normals:
                return (path, normals )
            else:
                return path

        else:
            #TODO
            path = bridge_creation(self,times)
            return path


set returnUO to 1 to return the underlying UO path as well as the integrated UO path .

def generate_sample_path(self,times, returnUO = 0):
        "set returnUO to 1 to return the underlying UO path as well as the integrated UO path."
        if not self.conditional:
            xPath, listOfNormals = self.OU.generate_sample_path(times, 1)
            path = []
            t = self.startTime
            y = self.startPosition
            for i, position in enumerate(xPath):
                delta = position[0]-t
                x = position[1]
                if delta != 0:
                    #there is an error here, I can smell it.
                    sigmaX = self.sigma**2*(1-np.exp(-2*self.theta*delta))/(2*self.theta)
                    sigmaY = self.sigma**2*(2*self.theta*delta-3+4*np.exp(-self.theta*delta)
                                      -np.exp(-2*self.theta*delta))/(2*self.sigma**3)
                    muY = y + (x-self.mu)/self.theta + self.mu*delta-(x-self.mu)*np.exp(-self.theta*delta)/self.theta
                    covXY = self.sigma**2*(1+np.exp(-2*self.theta*delta)-2*np.exp(-self.theta*delta))/(2*self.theta**2)
                    y = muY + np.sqrt(sigmaY - covXY**2/sigmaX)*self.Normal.rvs()+ covXY/np.sqrt(sigmaX)*listOfNormals[i]
                t = position[0]
                path.append((t,y))
            if returnUO==0:
                return path
            else:
                return path, xPath
        else:
           path = bridge_creation(self,times)
           if returnUO==0:
               return path
           else:
               return path, xPath


absb is a boolean true if absorbtion at 0 false else . See class __doc__ for when absorbtion is valid .

def generate_sample_path(self,times,absb=0):
        """
        absb is a boolean, true if absorbtion at 0, false else. See class' __doc__ for when
        absorbtion is valid.
        """
        if absb:
            return self._generate_sample_path_with_absorption(times)
        else:
            return self._generate_sample_path_no_absorption(times)

def generate_sample_path ( self , times , absb = 0 ) : if absb : return self . _generate_sample_path_with_absorption ( times ) else : return self . _generate_sample_path_no_absorption ( times )


mu must be greater than - 1 . The parameter times is a list of times to sample at .

def _generate_sample_path_no_absorption(self, times):
        "mu must be greater than -1. The parameter times is a list of times to sample at."
        if self.mu<=-1:
            print "Attn: mu must be greater than -1. It is currently %f."%self.mu
            return
        else:
            if not self.conditional:
                x=self.startPosition
                t=self.startTime
                path=[]
                for time in times:
                    delta=float(time-t)
                    try:
                        y=self.Poi.rvs(0.5*x/delta)
                        x=self.Gamma.rvs(y+self.mu+1)*2*delta
                    except:
                        pass
                    path.append((time,x))
                    t=time
            else:
                path = bridge_creation(self, times, 0)
                return path
            return [(p[0],self.rescalePath(p[1])) for p in path]


def _generate_sample_path_with_absorption(self,times):
        "mu must be less than 0."
        if self.mu>=0:
            print "Attn: mu must be less than 0. It is currently %f."%self.mu
        else:
            if not self.conditional:
                path=[]
                X=self.startPosition
                t=self.startTime
                tauEst=times[-1]+1
                for time in times:
                    delta = float(time - t)
                    if tauEst>times[-1]:
                        p_a = gammaincc(abs(self.mu),0.5*X/(delta))
                        if np.random.rand() < p_a:
                            tauEst = time
                    if time<tauEst:
                        Y = self.InGamma.rvs(abs(self.mu),0.5*X/(delta))
                        X = self.Gamma.rvs(Y+1)*2*delta
                    else:
                        X=0
                    t=time
                    path.append((t,X))
            else:
                path = bridge_creation(self, times, 1)
            return  [(p[0],self.rescalePath(p[1])) for p in path]


mu must be less than 0 . This process has absorption at L = 0 . It simulates the absorption at 0 at some random time tao and creates a bridge process .

def generate_sample_FHT_bridge(self,times):
        "mu must be less than 0. This process has absorption at L=0. It simulates the absorption at 0 at some random time, tao, and creates a bridge process."
        if self.mu>0:
            print "mu must be less than 0. It is currently %f."%self.mu
        else:
            X=self.startPosition
            t=self.t_0
            path=[]
            FHT=self.startPosition/(2*self.Gamma.rvs(abs(self.mu)))
            for time in times:
                if time<FHT:
                    d=(FHT-t)*(time-t)
                    Y=self.Poi.rvs(X*(FHT-time)/(2*d))
                    X=self.Gamma.rvs(Y-self.mu+1)*d/(FHT-t)
                else:
                    X=0
                t=time
                path.append((t,X))
            return  [(p[0],self.rescalePath(p[1])) for p in path]


abs is a boolean : true if desire nonzero probability of absorption at 0 false else .

def generate_sample_path(self, times, abs=0):
        "abs is a boolean: true if desire nonzero probability of absorption at 0, false else."
        #first, transform times:
        transformedTimes = [self._time_transformation(t) for t in times]
        path = self.SqB.generate_sample_path(transformedTimes,abs)
        tpath = [self._space_transformation(times[i],p[1]) for i,p in enumerate(path) ]
        path=[]
        for i in xrange(len(tpath)):
            path.append((times[i],tpath[i]))
        return path

def
generate_sample_path
(
self
,
times
,
abs
=
0
)
:
#first, transform times:
transformedTimes
=
[
self
.
_time_transformation
(
t
)
for
t
in
times
]
path
=
self
.
SqB
.
generate_sample_path
(
transformedTimes
,
abs
)
tpath
=
[
self
.
_space_transformation
(
times
[
i
]
,
p
[
1
]
)
for
i
,
p
in
enumerate
(
path
)
]
path
=
[
]
for
i
in
xrange
(
len
(
tpath
)
)
:
path
.
append
(
(
times
[
i
]
,
tpath
[
i
]
)
)
return
path


Note that space - time constraints cannot be given

def __init__(self, parameters, space_time_constraints):
        """Note that space-time constraints cannot be given"""
        space_time_constraints = {"startTime":0, "startPosition":0}
        super(Periodic_drift_process,self).__init__(space_time_constraints)
        self.psi = parameters["psi"]
        self.theta = parameters["theta"]
        self._findBounds()
        self.BB = Wiener_process({"mu":0, "sigma":1}, space_time_constraints)
        self.Poi = Marked_poisson_process({"rate":1, "U":self.max, "L":self.min, "startTime":0}) #need to create marked poisson process class
        self.Nor = stats.norm
        self.Uni = stats.uniform()


currently will only return a random path before time T . Can be connected by brownian bridges

def generate_sample_path(self,times):
        "currently will only return a random path before time T. Can be connected by brownian bridges"
        #this algorithm uses the EA1 algorithm by Beskos and
        # Roberts on exact simulation of diffusions. It's an AR algorithm.
        # For some parameters, the probability of acceptance can be very low.
        time = 0
        endPoint = 0
        skeleton=[]
        T = times[-1]
        while time<T:
            if time+2<T:
                delta=2
            else:
                delta = T-time
            endPoint, tempSkeleton = self.__generate_sample_path(delta,endPoint)
            tk = [(time + x[0],x[1]) for x in tempSkeleton]
            skeleton+=tk

            time+=2

        return self._construct_path_from_skeleton([(0,self.startPosition)]+skeleton, times)


this method calls self . _transition_pdf ( x ) in the subclass

def transition_pdf(self,t,y):
        "this method calls self._transition_pdf(x) in the subclass"
        self._check_time(t)
        if not self.conditional:
            return self._transition_pdf(self.startPosition, t-self.startTime, y)
        else:
            return self._transition_pdf(self.startPosition, t-self.startTime, y)*self._transition_pdf(y, self.endTime-t, self.endPosition)\
                    /self._transition_pdf(self.startPosition,self.endTime - self.startTime, self.endPosition)

def transition_pdf ( self , t , y ) : self . _check_time ( t ) if not self . conditional : return self . _transition_pdf ( self . startPosition , t - self . startTime , y ) else : return self . _transition_pdf ( self . startPosition , t - self . startTime , y ) * self . _transition_pdf ( y , self . endTime - t , self . endPosition ) / self . _transition_pdf ( self . startPosition , self . endTime - self . startTime , self . endPosition )


uses a monte carlo approach to evaluate the expected value of the process f ( X_t ) . N is the number of iterations . The parameter f \ is a univariate python function .

def expected_value(self,f,t,N):
        "uses a monte carlo approach to evaluate the expected value of the process f(X_t). N is the number of iterations. The parameter f\
        is a univariate python function."
        print "Attn: performing a Monte Carlo simulation..."
        self._check_time(t)
        if not self.conditional:
            sum=0
            for i in xrange(N):
                sum+=f(self.generate_position_at(t))
            return sum/N
        else:
            #This uses a change of measure technique.
            sum=0
            self.conditional=False
            for i in xrange(N):
                X = self.generate_position_at(t)
                sum+=self._transition_pdf(X,self.endTime-t,self.endPosition)*f(X)
            self.conditional=True
            return sum/(N*self._transition_pdf(self.startPosition, self.endTime-self.startTime, self.endPosition))


def generate_position_at(self,t):
        "if _get_position_at() is not overwritten in a subclass, this function will use euler scheme"
        self._check_time(t)
        if self.startTime<t:
            return self._generate_position_at(t)

def generate_position_at ( self , t ) : self . _check_time ( t ) if self . startTime < t : return self . _generate_position_at ( t )


times is a list of times with the first time greater than initial time .

def generate_sample_path(self,times):
        '"times" is a list of times, with the first time greater than initial time."'
        self._check_time(times[0])
        return self._generate_sample_path(times)

def generate_sample_path ( self , times ) : self . _check_time ( times [ 0 ] ) return self . _generate_sample_path ( times )


def _generate_sample_path(self,times):
        "I need some way to use a euler scheme AND evaluate the approximation at t in times"
        pass

def _generate_sample_path ( self , times ) : pass


if _get_mean_at () is not overwritten then we use MC methods ; 100000 iterations

def _get_mean_at(self,t):
        "if _get_mean_at() is not overwritten, then we use MC methods; 100000 iterations"
        def id(x):
            return x
        return self.expected_value(id, t, 100000)

def _get_mean_at ( self , t ) : def id ( x ) : return x return self . expected_value ( id , t , 100000 )


notice the conditional is independent of self . mean .

def _get_mean_at(self,t):
        "notice the conditional is independent of self.mean."
        if self.conditional:
            return self.startPosition + (self.endPosition-self.startPosition)*(t- self.startTime)/(self.endTime - self.startTime)
        else:
            return self.startPosition + self.mean*(t-self.startTime)

def _get_mean_at ( self , t ) : if self . conditional : return self . startPosition + ( self . endPosition - self . startPosition ) * ( t - self . startTime ) / ( self . endTime - self . startTime ) else : return self . startPosition + self . mean * ( t - self . startTime )


as this is a strictly increasing process the condition x<z must hold

def _transition_pdf(self,z,t,x):
            "as this is a strictly increasing process, the condition x<z must hold"
            alpha = self.mu**2*t/self.sigma
            beta = self.sigma/self.mu
            return (np.log((z-x)/self.startPosition))**(alpha-1)*((z-x)/self.startPosition)**(-1/beta)/((z-x)*beta**alpha*gamma(alpha))

def _transition_pdf ( self , z , t , x ) : alpha = self . mu ** 2 * t / self . sigma beta = self . sigma / self . mu return ( np . log ( ( z - x ) / self . startPosition ) ) ** ( alpha - 1 ) * ( ( z - x ) / self . startPosition ) ** ( - 1 / beta ) / ( ( z - x ) * beta ** alpha * gamma ( alpha ) )


returns an array of a path not an immutable list!

def generate_sample_path(self,times, *args):
        "returns an array of a path, not an immutable list!"
        path = [[t,0] for t in times]
        for i in xrange(len(self.processes)):
            try:
              tempPath = self.processes[i].generate_sample_path(times, args[i] )
            except:
              tempPath = self.processes[i].generate_sample_path(times)
            for k in xrange(len(tempPath)):
                path[k][1]+=tempPath[k][1]
        return path

def generate_sample_path ( self , times , * args ) : path = [ [ t , 0 ] for t in times ] for i in xrange ( len ( self . processes ) ) : try : tempPath = self . processes [ i ] . generate_sample_path ( times , args [ i ] ) except : tempPath = self . processes [ i ] . generate_sample_path ( times ) for k in xrange ( len ( tempPath ) ) : path [ k ] [ 1 ] += tempPath [ k ] [ 1 ] return path


uses a inversion method : the chop - down - search starting \ at the mode

def rvs(self,shape,scale):
        "uses a inversion method: the chop-down-search starting \
        at the mode"
        pos =  mode = float(max(0,int(shape-scale)))
        U = self.Uni.rvs()
        sum = self.pdf(mode,shape,scale)
        ub = mode+1
        lb = mode-1
        Pub = sum*scale/(mode+1+shape)
        Plb = sum*(mode+shape)/scale
        while sum<U:
            if Plb>Pub and lb>=0:
                sum+=Plb
                pos = lb
                Plb = (lb+shape)/scale*Plb
                lb-=1
            else:
                sum+=Pub
                pos= ub
                Pub = scale/(ub+1+shape)*Pub
                ub+=1
        return float(pos)

def rvs ( self , shape , scale ) : pos = mode = float ( max ( 0 , int ( shape - scale ) ) ) U = self . Uni . rvs ( ) sum = self . pdf ( mode , shape , scale ) ub = mode + 1 lb = mode - 1 Pub = sum * scale / ( mode + 1 + shape ) Plb = sum * ( mode + shape ) / scale while sum < U : if Plb > Pub and lb >= 0 : sum += Plb pos = lb Plb = ( lb + shape ) / scale * Plb lb -= 1 else : sum += Pub pos = ub Pub = scale / ( ub + 1 + shape ) * Pub ub += 1 return float ( pos )