def _sample_colored_noise(self, n): """Generate colored noise increments at specified times from zero.""" check_positive_integer(n) n = n + 1 if self._n != n: self._n = n self._half = (n + 1) // 2 self._frequencies = np.fft.fftfreq(n, self.t) self._scale = [ np.sqrt(0.5 * (1 / w) ** self.beta) for w in self._frequencies[1 : self._half] ] gn_real = np.random.normal(size=self._half - 1) gn_imag = np.random.normal(size=self._half - 1) fft = self._scale * (gn_real + 1j * gn_imag) if n % 2 == 0: f = np.concatenate( ( [0], fft, [ np.sqrt(0.5 * (1 / -self._frequencies[self._half]) ** self.beta) * np.random.normal() ], np.conj(fft)[::-1], ) ) else: f = np.concatenate(([0], fft, np.conj(fft)[::-1])) return np.fft.ifft(f).real / np.std(f)
def _sample_poisson_process(self, n=None, length=None): """Generate a realization of a Poisson process. Generate a poisson process sample up to count of length if time=False, otherwise generate a sample up to time t=length if time=True """ if n is not None: check_positive_integer(n) exponentials = self.rng.exponential(scale=1.0 / self.rate, size=n) s = np.array([0] + list(np.cumsum(exponentials))) return s elif length is not None: check_positive_number(length, "Sample length") t = 0 times = [0] exp_rate = 1.0 / self.rate while t < length: t += self.rng.exponential(scale=exp_rate) times.append(t) return np.array(times) else: raise ValueError("Must provide either argument n or length.")
def _sample_squared_bessel_process(self, n): """Generate a realization of a squared Bessel process.""" check_positive_integer(n) samples = [self._sample_brownian_motion(n) for _ in range(self.dim)] return np.array([sum(map(lambda x: x ** 2, coord)) for coord in zip(*samples)])
def _sample_chinese_restaurant(self, n, partition=False): """Generate a Chinese restaurant process with n customers.""" check_positive_integer(n) c = [[1]] s = [0] num_tables = 1 table_range = [0, 1] for k in range(2, n + 1): p = [ 1.0 * (len(c[t]) - self.discount) / (k - 1 + self.strength) for t in table_range[:-1] ] p.append(1.0 * (self.strength + num_tables * self.discount) / (k - 1 + self.strength)) table = self.rng.choice(table_range, p=p) if table == num_tables: num_tables += 1 table_range.append(num_tables) c.append([]) c[table].append(k - 1) s.append(table) if partition: return np.array([np.array(t) for t in c], dtype=object) else: return np.array(s)
def _daviesharte(self, n): """Generate a fractional Gaussian noise using davies-harte method. Uses Davies and Harte method (exact method) from: Davies, Robert B., and D. S. Harte. "Tests for Hurst effect." Biometrika 74, no. 1 (1987): 95-101. """ check_positive_integer(n) # For scaling to interval [0, T] increment = self.t / n scale = increment**self.hurst # If H = 0.5 then just generate a standard Brownian motion, otherwise # proceed with the Davies Harte method if self.hurst == 0.5: return self.rng.normal(scale=scale, size=n) else: # Generate some more fGns to use power-of-two FFTs for speed. m = 2**(n - 2).bit_length() + 1 sqrt_eigenvals = self._dh_sqrt_eigenvals(self.hurst, m) # irfft results will be normalized by (2(m-1))**(3/2) but we only # want to normalize by 2(m-1)**(1/2). scale *= 2**(1 / 2) * (m - 1) w = self.rng.normal(scale=scale, size=2 * m).view(complex) w[0] = w[0].real * 2**(1 / 2) w[-1] = w[-1].real * 2**(1 / 2) # Resulting z is fft of sequence w. return np.fft.irfft(sqrt_eigenvals * w)[:n]
def _sample_cauchy_process(self, n): """Generate a realization of a Cauchy process.""" check_positive_integer(n) delta_t = 1.0 * self.t / n times = np.cumsum(levy.rvs(loc=0, scale=delta_t**2 / 2, size=n)) times = np.insert(times, 0, [0]) return self._sample_brownian_motion_at(times)
def test_check_positive_integer(increments_fixture): if not isinstance(increments_fixture, int): with pytest.raises(TypeError): check_positive_integer(increments_fixture) elif increments_fixture <= 0: with pytest.raises(ValueError): check_positive_integer(increments_fixture) else: assert check_positive_integer(increments_fixture) is None
def _sample_gamma_process(self, n): """Sample a Gamma process.""" check_positive_integer(n) delta_t = 1.0 * self.t / n shape = 1.0 * self.mean**2 * delta_t / self.variance scale = 1.0 * self.variance / self.mean samples = np.cumsum(self.rng.gamma(shape=shape, scale=scale, size=n)) return np.concatenate(([0], samples))
def _sample_gaussian_noise(self, n): """Generate a realization of Gaussian noise. Generate a Gaussian noise realization with n increments. """ check_positive_integer(n) delta_t = 1.0 * self.t / n noise = self.rng.normal(scale=np.sqrt(delta_t), size=n) return noise
def _sample_geometric_brownian_motion(self, n, initial=1.0): """Generate a realization of geometric Brownian motion.""" check_positive_integer(n) check_positive_number(initial, "Initial") # Opt for repeated use if self._n != n: self._n = n self._line = generate_times(self.drift - self.volatility**2 / 2.0, n) noise = self.volatility * self._brownian_motion.sample(n) return initial * np.exp(self._line + noise)
def sample(self, n): """Generate a realization of the Markov chain. :param int n: the number of steps of the Markov chain to generate. """ check_positive_integer(n) states = range(self.num_states) markov_chain = [self.rng.choice(states, p=self.initial)] for _ in range(n - 1): markov_chain.append( self.rng.choice(states, p=self.transition[markov_chain[-1]])) return np.array(markov_chain)
def _sample_variance_gamma_process(self, n): """Generate a realization of a variance gamma process.""" check_positive_integer(n) delta_t = 1.0 * self.t / n shape = delta_t / self.variance scale = self.variance gammas = self.rng.gamma(shape=shape, scale=scale, size=n) gn = self.gn.sample(n) increments = self.drift * gammas + self.scale * np.sqrt(gammas) * gn samples = np.cumsum(increments) return np.concatenate(([0], samples))
def _sample(self, n, initial=1.0): """Generate a realization of a diffusion process using Euler-Maruyama.""" check_positive_integer(n) check_numeric(initial, "Initial") delta_t = 1.0 * self.t / n gns = self._sample_gaussian_noise(n) s = [initial] t = 0 for k in range(n): t += delta_t initial += (self._speed(t) * (self._mean(t) - initial) * delta_t + self._vol(t) * initial**self._volexp(initial) * gns[k]) s.append(initial) return np.array(s)
def _sample_random_walk_increments(self, n): """Generate a sample of random walk increments.""" check_positive_integer(n) return self.rng.choice(self.steps, p=self.p, size=n)
def _sample_bessel_process(self, n): """Generate a realization of a Bessel process.""" check_positive_integer(n) samples = [self._sample_brownian_motion(n) for _ in range(self.dim)] return np.array([np.linalg.norm(coord) for coord in zip(*samples)])
def _set_times(self, n): if self._n != n: check_positive_integer(n) self._n = n self._times = generate_times(self.t, n)
def _sample_bernoulli(self, n): """Generate a Bernoulli process realization.""" check_positive_integer(n) return np.array( [1 if trial < self.p else 0 for trial in self.rng.uniform(size=n)])