## ----setup, include=FALSE-----------------------------------------------------
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
library(rareflow)

## -----------------------------------------------------------------------------
Qobs <- c(0.05, 0.90, 0.05)
px <- function(z) c(0.3, 0.4, 0.3)

## -----------------------------------------------------------------------------
flow <- makeflow("planar", list(u = 0.1, w = 0.2, b = 0))
fit <- fitflowvariational(Qobs, pxgivenz = px, nmc = 500)
fit$elbo

## -----------------------------------------------------------------------------
b <- function(x) -x
dt <- 0.01
T <- 1000
set.seed(1)
Winc <- rnorm(T, sd = sqrt(dt))

## -----------------------------------------------------------------------------
theta <- rep(0.5, T)
girsanov_logratio(theta, Winc, dt)


## -----------------------------------------------------------------------------
set.seed(123)
dt  <- 0.01
T   <- 1000
Winc <- rnorm(T, sd = sqrt(dt))
theta_path <- rep(0, length(Winc))
fit_gir <- fitflow_girsanov(
  observed      = Qobs,
  base_pxgivenz = px,
  theta_path    = theta_path,
  Winc          = Winc,
  dt            = dt
)


## -----------------------------------------------------------------------------
V <- function(x) x^4/4 - x^2/2
xs <- seq(-2, 2, length.out = 400)

plot(xs, V(xs), type = "l", lwd = 2,
     main = "Double-Well Potential",
     ylab = "V(x)", xlab = "x")
abline(v = c(-1, 1), lty = 3, col = "gray")


## -----------------------------------------------------------------------------
b<- function(x) {
  v<- as.numeric(x)
  return(c(v - v^3)) #double-well drift
}
qp<- FW_quasipotential(-1, 1, drift = b, T = 200, dt = 0.01)
qp$action

## -----------------------------------------------------------------------------
plot(qp$path, type = "l", main = "Minimum-Action Path (Freidlin–Wentzell)")


## -----------------------------------------------------------------------------
V2 <- function(x, y) 0.25 * (x^2 + y^2 - 1)^2

xs <- seq(-2, 2, length.out = 200)
ys <- seq(-2, 2, length.out = 200)
grid <- expand.grid(x = xs, y = ys)
Z <- matrix(V2(grid$x, grid$y), nrow = length(xs))

contour(xs, ys, Z,
        nlevels = 20,
        main = "2D Radial Double-Well Potential",
        xlab = "x", ylab = "y")


## -----------------------------------------------------------------------------
dt <- 0.01
T <- 5000
x <- matrix(0, nrow = T, ncol = 2)

b2 <- function(v) {
  r2 <- sum(v^2)
  -(r2 - 1) * v
}

for (t in 1:(T-1)) {
  drift <- b2(x[t, ])
  noise <- sqrt(dt) * rnorm(2)
  x[t+1, ] <- x[t, ] + drift * dt + noise
}

plot(x[,1], x[,2], type = "l",
     main = "2D Diffusion in a Radial Double-Well",
     xlab = "x", ylab = "y")


## -----------------------------------------------------------------------------
# Potential and drift
V2 <- function(x, y) 0.25 * (x^2 + y^2 - 1)^2
b2 <- function(v) {
  r2 <- sum(v^2)
  -(r2 - 1) * v
}

# String method parameters
N <- 80          # number of points in the string
steps <- 200     # number of iterations
dt_sm <- 0.01    # step size

# Initial straight-line path
path <- cbind(seq(-1, 1, length.out = N), rep(0, N))

# String method iterations
for (k in 1:steps) {
  # Update each interior point
  for (i in 2:(N-1)) {
    drift <- b2(path[i, ])
    path[i, ] <- path[i, ] + dt_sm * drift
  }
  
  # Reparametrize to keep points evenly spaced
  d <- sqrt(rowSums(diff(path)^2))
  s <- c(0, cumsum(d))
  s <- s / max(s)
  path <- cbind(
    approx(s, path[,1], xout = seq(0,1,length.out=N))$y,
    approx(s, path[,2], xout = seq(0,1,length.out=N))$y
  )
}

plot(path[,1], path[,2], type="l", lwd=2,
     main="2D Minimum Action Path (String Method)",
     xlab="x", ylab="y")
points(c(-1,1), c(0,0), pch=19, col="red")


## -----------------------------------------------------------------------------
library(ggplot2)
library(gganimate)


## ----eval = FALSE-------------------------------------------------------------
# # Simulate a 2D trajectory
# dt <- 0.01
# T <- 2000
# x <- matrix(0, nrow = T, ncol = 2)
# 
# for (t in 1:(T-1)) {
#   drift <- b2(x[t, ])
#   noise <- sqrt(dt) * rnorm(2)
#   x[t+1, ] <- x[t, ] + drift * dt + noise
# }
# 
# df <- data.frame(
#   t = 1:T,
#   x = x[,1],
#   y = x[,2]
# )
# 
# p <- ggplot(df, aes(x, y)) +
#   geom_path(alpha = 0.4) +
#   geom_point(aes(frame = t), color = "red", size = 2) +
#   coord_equal() +
#   labs(title = "2D Diffusion Trajectory", x = "x", y = "y")
# 
# animate(p, nframes = 200, fps = 20)
# 

## -----------------------------------------------------------------------------
b <- function(x) x - x^3
dt <- 0.01
T <- 2000
x <- numeric(T)
for (t in 1:(T-1)) {
  x[t+1] <- x[t] + b(x[t])*dt + sqrt(dt)*rnorm(1)
}


## -----------------------------------------------------------------------------
rare_event <- mean(x > 1.5)
rare_event


## -----------------------------------------------------------------------------
Qobs <- c(mean(x < -0.5), mean(abs(x) <= 0.5), mean(x > 0.5))
px <- function(z) c(0.3, 0.4, 0.3)


## -----------------------------------------------------------------------------
fit <- fitflowvariational(Qobs, pxgivenz = px)
fit$elbo


