tests/testthat/test-funform.R

# 
# These tests are there to make sure chouca can approximate functions up to degree 4 
# 


trans_exact <- transition(from = "0", to = "+", ~ 1 + exp(- p["+"] * q["+"] ))

exp_approx <- function(x, d) { 
  if ( length(x) > 1 ) { 
    return(sapply(exp_approx, x, d = d))
  }
  
  sum(sapply(seq(0, d), function(k) { 
    x^k / factorial(k)
  }))
}

mkmod <- function(tr) { 
  camodel(tr, 
          wrap = TRUE, 
          neighbors = 4, 
          check_model = "full")
}


err <- function(deg) { 
  
  m <- mkmod(transition(from = "0", to = "+", 
                        ~ 1 + exp_approx( - p["+"] * q["+"], deg)))
  
  data.frame(err = m[["max_error"]], 
             relerr = m[["max_rel_error"]])
}

allerrs <- plyr::ldply(seq.int(0, 9), function(d) { 
  plyr::ldply(seq.int(5), function(n) { 
    data.frame(nrep = n, deg = d, err(d)) 
  })
}, .progress = "none")

# library(ggplot2)
# 
# ggplot(allerrs, aes(x = deg, y = relerr)) + 
#   geom_point() + 
#   scale_y_continuous(trans = "log10")
# 





m <- mkmod(trans_exact)
m <- mkmod(transition(from = "0", to = "+", ~ 1 + exp_approx(-p["+"]*q["+"], 1)))
m <- mkmod(transition(from = "0", to = "+", ~ 1 + exp_approx(-p["+"]*q["+"], 2)))
m <- mkmod(transition(from = "0", to = "+", ~ 1 + exp_approx(-p["+"]*q["+"], 3)))
m <- mkmod(transition(from = "0", to = "+", ~ 1 + exp_approx(-p["+"]*q["+"], 4)))

options(chouca.degmax = 10)
m <- mkmod(transition(from = "0", to = "+", ~ 1 + exp_approx(-p["+"]*q["+"], 5)))
m <- mkmod(transition(from = "0", to = "+", ~ 1 + exp_approx(-p["+"]*q["+"], 6)))
m <- mkmod(transition(from = "0", to = "+", ~ 1 + exp_approx(-p["+"]*q["+"], 7)))
m <- mkmod(transition(from = "0", to = "+", ~ 1 + exp_approx(-p["+"]*q["+"], 8)))
m <- mkmod(transition(from = "0", to = "+", ~ 1 + exp_approx(-p["+"]*q["+"], 9)))
options(chouca.degmax = NULL) # unset


# Expect warning if not enough degrees to work with 
options(chouca.degmax = 1)
expect_warning({ 
  m <- mkmod(transition(from = "0", to = "+", ~ 1 + exp_approx(-p["+"]*q["+"], 5)))
})
options(chouca.degmax = NULL)



# Check the reconstruction of coefs
m <- mkmod(
  transition(from = "0", to = "+", 
             ~ 1 - 0.1*p["+"]*p["0"] + 0.2*(p["+"]*p["0"])^2 + 0.03*(p["+"]*p["0"])^3 +
                0.1*(p["+"]*p["0"])^4)
)

expect_true({ 
  abs(m[["beta_0"]][ ,"coef"] - 1) < 1e-8
})

expect_true({ 
  all(abs(m[["beta_pp"]][ ,"coef"] - c(-0.1, 0.2, 0.03, 0.10)) < 1e-8)
})




m <- mkmod(
  transition(from = "0", to = "+", 
             ~ 1 - 0.1*p["+"]*q["0"] + 0.2*(p["+"]*q["0"])^2 + 0.03*(p["+"]*q["0"])^3 +
                0.1*(p["+"]*q["0"])^4)
)

expect_true({ 
  abs(m[["beta_0"]][ ,"coef"] - 1) < 1e-8
})

expect_true({ 
  all(abs(m[["beta_pq"]][ ,"coef"] - c(-0.1, 0.2, 0.03, 0.10)) < 1e-8)
})








sin_approx <- function(x, d) { 
  
  if ( length(x) > 1 ) { 
    return( sapply(x, sin_approx, d = d) )
  }
  
  sum(sapply(seq(0, d), function(k) { 
    (-1)^k / factorial(2 * k + 1) * x^(2 * k + 1)
  }))
}

err <- function(deg) { 
  
  # We suppress warnings because we can get something saying it goes below zero 
  suppressWarnings({ 
    m <- mkmod(transition(from = "0", to = "+", 
                          ~ 1.01 + sin_approx( - p["+"] * q["+"] * pi / 1.2, deg)))
  })
  
#   m <- mkmod(transition(from = "0", to = "+", 
#                         ~ 1.01 + sin_approx( - p["+"] * q["+"] * pi / 1.2, deg)))
  
  xy <- expand.grid(x = seq(0, 1, l = 32), 
                    y = seq(0, 1, l = 32))
  xy[ ,"z"] <- with(xy, sin_approx(- x * y * pi / 1.2, 2))
  xy[ ,"pr"] <- sapply(seq.int(nrow(xy)), function(i) { 
    with(as.data.frame(m$beta_pq), { 
      sum( coef * xy[i,"x"]^expo_1 * xy[i,"y"]^expo_2 )
    })
  })
#   
#   ggplot(xy, aes(x = x, y = y, fill = z)) + 
#     geom_raster() + 
#     geom_contour(aes(z = z)) 
#   
#   ggplot(xy, aes(x = x, y = z, group = y, color = y)) + 
#     geom_line() + 
#     geom_line(aes(y = pr), col = "red")
#   
  data.frame(err = m[["max_error"]], 
             relerr = m[["max_rel_error"]])
}

x <- seq(0, 1, l = 12)
# plot(x, sin(x*pi))
# lines(x, sin_approx(x*pi, 0))


allerrs <- plyr::ldply(seq.int(0, 2), function(d) { 
  plyr::ldply(seq.int(5), function(n) { 
    data.frame(nrep = n, deg = d, err(d)) 
  })
}, .progress = "none")

# We expect to be able to reconstruct sinus mclaurin series up to degree 2 (which means 
#  polynomial of degree 5 total)
expect_true({
  all( allerrs[ ,"relerr"] < 1e-3 ) 
})

# 
# ggplot(allerrs, aes(x = deg, y = relerr)) + 
#   geom_point() + 
#   scale_y_continuous(trans = "log10")
# 


# m <- mkmod(transition(from = "0",to = "+",
#                       ~ 1.1 + sin(-p["+"]*q["+"] * pi / 1.2)))

# m <- mkmod(transition(from = "0",to = "+",
#                       ~ 1.1 + sin(-p["+"]*p["0"] * pi / 1.2)))

# m <- mkmod(transition(from = "0",to = "+",
#                       ~ 1.1 + sin(-q["+"]*q["0"] * pi / 1.2)))


# 1.1 - pq[+] * pi / 1.2 + (pi/1.2)^3/(3*2) - (pi/1.2)^5/(5*4*3*2)
#  = 1.1 + p[+]q[+] (- 2.6 + 2.99 - 1.02 )
expect_warning({ 
  m <- mkmod(transition(from = "0",to = "+", 
                        ~ 1.1 + sin_approx(-p["0"]*q["+"] * pi / 1.2, 0)))
}, regexp = "Transition probabilities may go below zero")


# We expect to recover the first two terms of the Mac Laurin series for sin
m <- mkmod(transition(from = "0",to = "+", 
                      ~ 1.1 + sin_approx(-p["+"]*q["0"] * pi / 1.2, 1)))
expect_true({ 
  all( abs(m[["beta_pq"]][["coef"]] - c(-2.617994, 2.990575)) < 1e-5 )
})

# Here things get more complicated because some non-zero coefficients become very 
# small but creep in nevertheless
# m <- mkmod(transition(from = "0",to = "+",
#                       ~ 1.1 + sin_approx(-p["+"]*q["+"] * pi / 1.2, 2)))

Try the chouca package in your browser

Any scripts or data that you put into this service are public.

chouca documentation built on May 29, 2024, 6:12 a.m.