demo/proto.R

########################################################################
## DEMO: ecological model applications have common properties,
##       that belong together:
##       - equations
##       - constant parameters
##       - initial values of the state variables
##       - time steps
##       - an adequate solver, and
##       - utility functions
########################################################################

if (dev.cur() <= 1) get(getOption("device"))()

opar <- par(ask = interactive() &&
            (.Device %in% c("X11", "GTK", "gnome", "windows","quartz")))

if (require(odesolve)) {

	library(odesolve)
	library(proto)

	## object creation from scratch (ex-nihilo)
	lv <- proto(expr = {
	    equations <- function(t, x, p) {
	    dx1.dt <-   p["k1"] * x[1] - p["k2"] * x[1] * x[2]
	    dx2.dt <- - p["k3"] * x[2] + p["k2"] * x[1] * x[2]
	    list(c(dx1.dt, dx2.dt))
	    }
	    # vectors of parameters, timesteps and initial values
	    parms  <- c(k1=0.2, k2=0.2, k3=0.2)
	    times  <- 1:100
	    init   <- c(prey=0.5, predator=1)
	    # two methods
	    solve  <- function(.) {
		  # must use .$with(equations) instead of .$equations
		  # as latter can only be used in a call
		  equations <- .$with(equations)
		  res <- as.data.frame(rk4(.$init, .$times, equations, .$parms))
		}
	    plot <- function(.) {
	      res <- .$solve()
	      
	      graphics::plot(res$time, res$predator, col = "red", 
		 type = "b", pch = 20)
	      graphics::plot(res$time, res$prey, col = "green",
		 type = "b", pch = 20)
	    }
	})    

	## the created object is fully functional
	par(mfrow=c(2,1))
	res <- lv$solve() 
	plot(res)
	lv$plot()  

	## derive a child object with a different ODE solver
	lv.lsoda <- lv$proto(
		      solve <- function(.) {
			equations <- .$with(equations)
			res <- lsoda(.$init, .$times, equations, .$parms)
			as.data.frame(res)
		      }
		    )

	## test this child object
	lv.lsoda$plot()

	### derive two scenarios

	## scenario 1 with initial values, that are in equilibrium
	sc1 <- lv.lsoda$proto(init   <- c(prey=1, predator=1))

	## scenario 2, similar to scenario 1, but with different parameters
	sc2 <- sc1$proto(parms  <- c(k1=0.3, k2=0.2, k3=0.2))

	## solve, plot, compare these scenarios
	par(mfrow=c(2,2))
	sc1$plot()
	sc2$plot()
			      
	## and as a last example, we modify parameters of the parent
	lv$times <- seq(0, 100, 0.1)

	sc1$main <- "Scenario 1"
	sc2$main <- "Scenario 2"
	lv$plot <- function(.) {
	      res <- .$solve()
	      
	      graphics::plot(res$time, res$predator, col = "red", 
		 type = "l", pch = 20, main=.$main)
	      graphics::plot(res$time, res$prey, col = "green",
		 type = "l", pch = 20, main=.$main)
	    }

	par(mfrow=c(2,2))

	sc1$plot()
	sc2$plot()

	## fragile base object: the folowing would give an error
	# lv$plot()

	## but it works if we add the required main title
	lv$main <- "Reference"
	lv$plot()

	## show the object relationships
	g <- graph.proto()
	plot(g)

	## Conclusion:
	##   - the responsibility is due to the user, but
	##   - prototypes allow oop without overhead

}

par(opar)
ggrothendieck/proto documentation built on May 17, 2019, 4:17 a.m.