context("test-vignettes")
test_that("Test non-negative least squares", {
skip_on_cran()
library(MASS)
print("LS")
## Generate problem data
s <- 1
m <- 10
n <- 300
mu <- rep(0, 9)
Sigma <- data.frame(c(1.6484, -0.2096, -0.0771, -0.4088, 0.0678, -0.6337, 0.9720, -1.2158, -1.3219),
c(-0.2096, 1.9274, 0.7059, 1.3051, 0.4479, 0.7384, -0.6342, 1.4291, -0.4723),
c(-0.0771, 0.7059, 2.5503, 0.9047, 0.9280, 0.0566, -2.5292, 0.4776, -0.4552),
c(-0.4088, 1.3051, 0.9047, 2.7638, 0.7607, 1.2465, -1.8116, 2.0076, -0.3377),
c(0.0678, 0.4479, 0.9280, 0.7607, 3.8453, -0.2098, -2.0078, -0.1715, -0.3952),
c(-0.6337, 0.7384, 0.0566, 1.2465, -0.2098, 2.0432, -1.0666, 1.7536, -0.1845),
c(0.9720, -0.6342, -2.5292, -1.8116, -2.0078, -1.0666, 4.0882, -1.3587, 0.7287),
c(-1.2158, 1.4291, 0.4776, 2.0076, -0.1715, 1.7536, -1.3587, 2.8789, 0.4094),
c(-1.3219, -0.4723, -0.4552, -0.3377, -0.3952, -0.1845, 0.7287, 0.4094, 4.8406))
X <- MASS::mvrnorm(n, mu, Sigma)
X <- cbind(rep(1, n), X)
b <- c(0, 0.8, 0, 1, 0.2, 0, 0.4, 1, 0, 0.7)
y <- X %*% b + rnorm(n, 0, s)
## Construct the OLS problem without constraints
beta <- Variable(m)
objective <- Minimize(sum((y - X %*% beta)^2))
prob <- Problem(objective)
## Solve the OLS problem for beta
system.time(result <- solve(prob))
beta_ols <- result$getValue(beta)
## Add non-negativity constraint on beta
constraints <- list(beta >= 0)
prob2 <- Problem(objective, constraints)
## Solve the NNLS problem for beta
system.time(result2 <- solve(prob2))
beta_nnls <- result2$getValue(beta)
expect_true(all(beta_nnls >= -1e-6)) ## All resulting beta should be non-negative
## Calculate the fitted y values
fit_ols <- X %*% beta_ols
fit_nnls <- X %*% beta_nnls
## Plot coefficients for OLS and NNLS
coeff <- cbind(b, beta_ols, beta_nnls)
colnames(coeff) <- c("Actual", "OLS", "NNLS")
rownames(coeff) <- paste("beta", 1:length(b)-1, sep = "")
barplot(t(coeff), ylab = "Coefficients", beside = TRUE, legend.text = TRUE)
})
test_that("Test censored regression", {
skip_on_cran()
## Problem data
n <- 30
M <- 50
K <- 200
set.seed(n*M*K)
print("CENSORED")
X <- matrix(stats::rnorm(K*n), nrow = K, ncol = n)
beta_true <- matrix(stats::rnorm(n), nrow = n, ncol = 1)
y <- X %*% beta_true + 0.3*sqrt(n)*stats::rnorm(K)
## Order variables based on y
idx <- order(y, decreasing = FALSE)
y_ordered <- y[idx]
X_ordered <- X[idx,]
## Find cutoff and censor
D <- (y_ordered[M] + y_ordered[M+1])/2
y_censored <- pmin(y_ordered, D)
plot_results <- function(beta_res, bcol = "blue", bpch = 17) {
graphics::plot(1:M, y_censored[1:M], col = "black", xlab = "Observations", ylab = "y", ylim = c(min(y), max(y)), xlim = c(1,K))
graphics::points((M+1):K, y_ordered[(M+1):K], col = "red")
graphics::points(1:K, X_ordered %*% beta_res, col = bcol, pch = bpch)
graphics::abline(a = D, b = 0, col = "black", lty = "dashed")
graphics::legend("topleft", c("Uncensored", "Censored", "Estimate"), col = c("black", "red", bcol), pch = c(1,1,bpch))
}
## Regular OLS
beta <- Variable(n)
obj <- sum((y_censored - X_ordered %*% beta)^2)
prob <- Problem(Minimize(obj))
result <- solve(prob)
expect_equal(result$status, "optimal")
beta_ols <- result$getValue(beta)
plot_results(beta_ols)
## OLS using uncensored data
obj <- sum((y_censored[1:M] - X_ordered[1:M,] %*% beta)^2)
prob <- Problem(Minimize(obj))
result <- solve(prob)
expect_equal(result$status, "optimal")
beta_unc <- result$getValue(beta)
plot_results(beta_unc)
## Censored regression
obj <- sum((y_censored[1:M] - X_ordered[1:M,] %*% beta)^2)
constr <- list(X_ordered[(M+1):K,] %*% beta >= D)
prob <- Problem(Minimize(obj), constr)
result <- solve(prob)
expect_equal(result$status, "optimal")
beta_cens <- result$getValue(beta)
plot_results(beta_cens)
})
test_that("Test Elastic-net regression", {
skip_on_cran()
set.seed(1)
print("ELASTIC")
# Problem data
n = 20
m = 1000
DENSITY = 0.25 # Fraction of non-zero beta
beta_true = matrix(rnorm(n), ncol = 1)
idxs <- sample.int(n, size = floor((1-DENSITY)*n), replace = FALSE)
beta_true[idxs] <- 0
sigma = 45
X = matrix(rnorm(m*n, sd = 5), nrow = m, ncol = n)
eps <- matrix(rnorm(m, sd = sigma), ncol = 1)
y = X %*% beta_true + eps
# Elastic-net penalty function
elastic_reg <- function(beta, lambda = 0, alpha = 0) {
ridge <- (1 - alpha) * sum(beta^2)
lasso <- alpha * p_norm(beta, 1)
lambda * (lasso + ridge)
}
TRIALS <- 10
beta_vals <- matrix(0, nrow = n, ncol = TRIALS)
lambda_vals <- 10^seq(-2, log10(50), length.out = TRIALS)
beta <- Variable(n)
loss <- sum((y - X %*% beta)^2)/(2*m)
# Elastic-net regression
alpha <- 0.75
for(i in 1:TRIALS) {
lambda <- lambda_vals[i]
obj <- loss + elastic_reg(beta, lambda, alpha)
prob <- Problem(Minimize(obj))
result <- solve(prob)
expect_equal(result$status, "optimal")
beta_vals[,i] <- result$getValue(beta)
}
# Plot coefficients against regularization
plot(0, 0, type = "n", main = "Regularization Path for Elastic-net Regression", xlab = expression(lambda), ylab = expression(beta), ylim = c(-0.75, 1.25), xlim = c(0, 50))
matlines(lambda_vals, t(beta_vals))
# Compare with glmnet results
library(glmnet)
model_net <- glmnet(X, y, family = "gaussian", alpha = alpha, lambda = lambda_vals, standardize = FALSE, intercept = FALSE)
coef_net <- coef(model_net)[-1, seq(TRIALS, 1, by = -1)] # Reverse order to match beta_vals
sum((beta_vals - coef_net)^2)/(n*TRIALS)
})
test_that("Test Huber regression", {
skip_on_cran()
n <- 1
m <- 450
M <- 1 ## Huber threshold
p <- 0.1 ## Fraction of responses with sign flipped
print("HUBER")
## Generate problem data
beta_true <- 5*matrix(stats::rnorm(n), nrow = n)
X <- matrix(stats::rnorm(m*n), nrow = m, ncol = n)
y_true <- X %*% beta_true
eps <- matrix(stats::rnorm(m), nrow = m)
## Randomly flip sign of some responses
factor <- 2*stats::rbinom(m, size = 1, prob = 1-p) - 1
y <- factor * y_true + eps
## Solve ordinary least squares problem
beta <- Variable(n)
rel_err <- norm(beta - beta_true, "F")/norm(beta_true, "F")
obj <- sum((y - X %*% beta)^2)
prob <- Problem(Minimize(obj))
result <- solve(prob)
expect_equal(result$status, "optimal")
beta_ols <- result$getValue(beta)
err_ols <- result$getValue(rel_err)
## Plot fit against measured responses
plot(X[factor == 1], y[factor == 1], col = "black", xlab = "X", ylab = "y")
points(X[factor == -1], y[factor == -1], col = "red")
lines(X, X %*% beta_ols, col = "blue")
## Solve Huber regression problem
obj <- sum(CVXR::huber(y - X %*% beta, M))
prob <- Problem(Minimize(obj))
result <- solve(prob)
expect_equal(result$status, "optimal")
beta_hub <- result$getValue(beta)
err_hub <- result$getValue(rel_err)
lines(X, X %*% beta_hub, col = "seagreen", lty = "dashed")
## Solve ordinary least squares assuming sign flips known
obj <- sum((y - factor*(X %*% beta))^2)
prob <- Problem(Minimize(obj))
result <- solve(prob)
expect_equal(result$status, "optimal")
beta_prs <- result$getValue(beta)
err_prs <- result$getValue(rel_err)
lines(X, X %*% beta_prs, col = "black")
legend("topright", c("OLS", "Huber", "Prescient"), col = c("blue", "seagreen", "black"), lty = 1)
})
test_that("Test logistic regression", {
skip_on_cran()
n <- 20
m <- 1000
offset <- 0
sigma <- 45
DENSITY <- 0.2
print("LOGISTIC")
beta_true <- stats::rnorm(n)
idxs <- sample(n, size = floor((1-DENSITY)*n), replace = FALSE)
beta_true[idxs] <- 0
X <- matrix(stats::rnorm(m*n, 0, 5), nrow = m, ncol = n)
y <- sign(X %*% beta_true + offset + stats::rnorm(m, 0, sigma))
beta <- Variable(n)
obj <- -sum(logistic(-X[y <= 0,] %*% beta)) - sum(logistic(X[y == 1,] %*% beta))
prob <- Problem(Maximize(obj))
result <- solve(prob)
expect_equal(result$status, "optimal")
log_odds <- result$getValue(X %*% beta)
beta_res <- result$getValue(beta)
y_probs <- 1/(1 + exp(-X %*% beta_res))
log(y_probs/(1 - y_probs))
})
test_that("Test saturating hinges problem", {
skip_on_cran()
library(ElemStatLearn)
print("SAT HINGES")
## Import and sort data
data(bone)
X <- bone[bone$gender == "female",]$age
y <- bone[bone$gender == "female",]$spnbmd
ord <- order(X, decreasing = FALSE)
X <- X[ord]
y <- y[ord]
## Choose knots evenly distributed along domain
k <- 10
lambdas <- c(1, 0.5, 0.01)
idx <- floor(seq(1, length(X), length.out = k))
knots <- X[idx]
## Saturating hinge
f_est <- function(x, knots, w0, w) {
hinges <- sapply(knots, function(t) { pmax(x - t, 0) })
w0 + hinges %*% w
}
## Loss function
loss_obs <- function(y, f) { (y - f)^2 }
## Form problem
w0 <- Variable(1)
w <- Variable(k)
loss <- sum(loss_obs(y, f_est(X, knots, w0, w)))
constr <- list(sum(w) == 0)
xrange <- seq(min(X), max(X), length.out = 100)
splines <- matrix(0, nrow = length(xrange), ncol = length(lambdas))
for(i in 1:length(lambdas)) {
lambda <- lambdas[i]
reg <- lambda * p_norm(w, 1)
obj <- loss + reg
prob <- Problem(Minimize(obj), constr)
## Solve problem and save spline weights
result <- solve(prob)
expect_equal(result$status, "optimal")
w0s <- result$getValue(w0)
ws <- result$getValue(w)
splines[,i] <- f_est(xrange, knots, w0s, ws)
}
## Plot saturating hinges
plot(X, y, xlab = "Age", ylab = "Change in Bone Density", col = "black", type = "p")
matlines(xrange, splines, col = "blue", lty = 1:length(lambdas), lwd = 1.5)
legend("topright", as.expression(lapply(lambdas, function(x) bquote(lambda==.(x)))), col = "blue", lty = 1:length(lambdas), bty = "n")
## Add outliers to data
set.seed(1)
nout <- 50
X_out <- runif(nout, min(X), max(X))
y_out <- runif(nout, min(y), 3*max(y)) + 0.3
X_all <- c(X, X_out)
y_all <- c(y, y_out)
## Solve with squared error loss
loss_obs <- function(y, f) { (y - f)^2 }
loss <- sum(loss_obs(y_all, f_est(X_all, knots, w0, w)))
prob <- Problem(Minimize(loss + reg), constr)
result <- solve(prob)
expect_equal(result$status, "optimal")
spline_sq <- f_est(xrange, knots, result$getValue(w0), result$getValue(w))
## Solve with Huber loss
loss_obs <- function(y, f, M) { CVXR::huber(y - f, M) }
loss <- sum(loss_obs(y, f_est(X, knots, w0, w), 0.01))
prob <- Problem(Minimize(loss + reg), constr)
result <- solve(prob)
expect_equal(result$status, "optimal")
spline_hub <- f_est(xrange, knots, result$getValue(w0), result$getValue(w))
## Compare fitted functions with squared error and Huber loss
plot(X, y, xlab = "Age", ylab = "Change in Bone Density", col = "black", type = "p", ylim = c(min(y), 1))
points(X_out, y_out, col = "red", pch = 16)
matlines(xrange, cbind(spline_hub, spline_sq), col = "blue", lty = 1:2, lwd = 1.5)
legend("topright", c("Huber Loss", "Squared Loss"), col = "blue", lty = 1:2, bty = "n")
})
test_that("Test log-concave distribution estimation", {
skip_on_cran()
set.seed(1)
print("LOG CONCAVE")
## Calculate a piecewise linear function
pwl_fun <- function(x, knots) {
n <- nrow(knots)
x0 <- sort(knots$x, decreasing = FALSE)
y0 <- knots$y[order(knots$x, decreasing = FALSE)]
slope <- diff(y0)/diff(x0)
sapply(x, function(xs) {
if(xs <= x0[1])
y0[1] + slope[1]*(xs -x0[1])
else if(xs >= x0[n])
y0[n] + slope[n-1]*(xs - x0[n])
else {
idx <- which(xs <= x0)[1]
y0[idx-1] + slope[idx-1]*(xs - x0[idx-1])
}
})
}
## Problem data
m <- 25
xrange <- 0:100
knots <- data.frame(x = c(0, 25, 65, 100), y = c(10, 30, 40, 15))
xprobs <- pwl_fun(xrange, knots)/15
xprobs <- exp(xprobs)/sum(exp(xprobs))
x <- sample(xrange, size = m, replace = TRUE, prob = xprobs)
K <- max(xrange)
xhist <- hist(x, breaks = -1:K, right = TRUE, include.lowest = FALSE)
counts <- xhist$counts
## Solve problem with log-concave constraint
u <- Variable(K+1)
obj <- t(counts) %*% u
constraints <- list(sum(exp(u)) <= 1, diff(u[1:K]) >= diff(u[2:(K+1)]))
prob <- Problem(Maximize(obj), constraints)
result <- solve(prob)
expect_equal(result$status, "optimal")
pmf <- result$getValue(exp(u))
## Plot probability mass function
cl <- rainbow(3)
plot(NA, xlim = c(0, 100), ylim = c(0, 0.025), xlab = "x", ylab = "Probability Mass Function")
lines(xrange, xprobs, lwd = 2, col = cl[1])
lines(density(x, bw = "sj"), lwd = 2, col = cl[2])
## lines(counts/sum(counts), lwd = 2, col = cl[2])
lines(xrange, pmf, lwd = 2, col = cl[3])
legend("topleft", c("True", "Empirical", "Optimal Estimate"), lty = c(1,1,1), col = cl)
## Plot cumulative distribution function
plot(NA, xlim = c(0, 100), ylim = c(0, 1), xlab = "x", ylab = "Cumulative Distribution Function")
lines(xrange, base::cumsum(xprobs), lwd = 2, col = cl[1])
lines(xrange, base::cumsum(counts)/sum(counts), lwd = 2, col = cl[2])
lines(xrange, base::cumsum(pmf), lwd = 2, col = cl[3])
legend("topleft", c("True", "Empirical", "Optimal Estimate"), lty = c(1,1,1), col = cl)
})
test_that("Test channel capacity problem", {
skip_on_cran()
## Problem data
n <- 2
m <- 2
P <- rbind(c(0.75, 0.25), ## Channel transition matrix
c(0.25, 0.75))
print("CHANNEL")
## Form problem
x <- Variable(n) ## Probability distribution of input signal x(t)
y <- P %*% x ## Probability distribution of output signal y(t)
c <- apply(P * log2(P), 2, sum)
I <- t(c) %*% x + sum(entr(y)) ## Mutual information between x and y
obj <- Maximize(I)
constraints <- list(sum(x) == 1, x >= 0)
prob <- Problem(obj, constraints)
result <- solve(prob)
expect_equal(result$status, "optimal")
## Optimal channel capacity and input signal.
result$value
result$getValue(x)
})
test_that("Test optimal allocation in a Gaussian broadcast channel", {
skip_on_cran()
## Problem data
n <- 5
alpha <- seq(10, n-1+10)/n
beta <- seq(10, n-1+10)/n
P_tot <- 0.5
W_tot <- 1.0
print("OPTIMAL ALLOCATION")
## Form problem
P <- Variable(n) ## Power
W <- Variable(n) ## Bandwidth
R <- kl_div(alpha*W, alpha*(W + beta*P)) - alpha*beta*P ## Bitrate
objective <- Minimize(sum(R))
constraints <- list(P >= 0, W >= 0, sum(P) == P_tot, sum(W) == W_tot)
prob <- Problem(objective, constraints)
result <- solve(prob)
expect_equal(result$status, "optimal")
## Optimal utility, power, and bandwidth
-result$value
result$getValue(P)
result$getValue(W)
})
test_that("Test catenary problem", {
skip_on_cran()
## Problem data
m <- 101
L <- 2
h <- L/(m-1)
print("CATENARY")
## Form objective
x <- Variable(m)
y <- Variable(m)
objective <- Minimize(sum(y))
## Form constraints
constraints <- list(x[1] == 0, y[1] == 1, x[m] == 1, y[m] == 1,
diff(x)^2 + diff(y)^2 <= h^2)
## Solve the catenary problem
prob <- Problem(objective, constraints)
system.time(result <- solve(prob))
expect_equal(result$status, "optimal")
## Plot and compare with ideal catenary
xs <- result$getValue(x)
ys <- result$getValue(y)
graphics::plot(c(0, 1), c(0, 1), type = 'n', xlab = "x", ylab = "y")
graphics::lines(xs, ys, col = "blue", lwd = 2)
graphics::grid()
ideal <- function(x) { 0.22964*cosh((x-0.5)/0.22964)-0.02603 }
expect_equal(ys, ideal(xs), tolerance = 1e-3)
## points(c(0, 1), c(1, 1))
## curve(0.22964*cosh((x-0.5)/0.22964)-0.02603, 0, 1, col = "red", add = TRUE)
## grid()
## Lower right endpoint and add staircase structure
ground <- sapply(seq(0, 1, length.out = m), function(x) {
if(x < 0.2)
return(0.6)
else if(x >= 0.2 && x < 0.4)
return(0.4)
else if(x >= 0.4 && x < 0.6)
return(0.2)
else
return(0)
})
## Solve catenary problem with ground constraint
constraints <- c(constraints, y >= ground)
constraints[[4]] <- (y[m] == 0.5)
prob <- Problem(objective, constraints)
result <- solve(prob)
expect_equal(result$status, "optimal")
## Plot catenary against ground
xs <- result$getValue(x)
ys <- result$getValue(y)
graphics::plot(c(0, 1), c(1, 0.5), type = "n", xlab = "x", ylab = "y", ylim = c(0, 1))
graphics::points(c(0, 1), c(1, 0.5))
graphics::lines(xs, ys, col = "blue", lwd = 2)
graphics::lines(xs, ground, col = "red")
graphics::grid()
})
test_that("Test direct standardization problem", {
skip_on_cran()
print("DIRECT")
skew_sample <- function(data, bias) {
if(missing(bias))
bias <- rep(1.0, ncol(data))
num <- exp(data %*% bias)
return(num / sum(num))
}
plot_cdf <- function(data, probs, color = 'k') {
if(missing(probs))
probs <- rep(1.0/length(data), length(data))
distro <- cbind(data, probs)
dsort <- distro[order(distro[,1]),]
ecdf <- base::cumsum(dsort[,2])
lines(dsort[,1], ecdf, col = color)
}
## Problem data
n <- 2
m <- 1000
msub <- 100
## Generate original distribution
sex <- stats::rbinom(m, 1, 0.5)
age <- sample(10:60, m, replace = TRUE)
mu <- 5 * sex + 0.1 * age
X <- cbind(sex, age)
y <- stats::rnorm(mu, 1.0)
b <- as.matrix(apply(X, 2, mean))
## Generate skewed subsample
skew <- skew_sample(X, c(-0.95, -0.05))
sub <- sample(1:m, msub, replace = TRUE, prob = skew)
## Construct the direct standardization problem
w <- Variable(msub)
objective <- sum(entr(w))
constraints <- list(w >= 0, sum(w) == 1, t(X[sub,]) %*% w == b)
prob <- Problem(Maximize(objective), constraints)
## Solve for the distribution weights
result <- solve(prob)
expect_equal(result$status, "optimal")
weights <- result$getValue(w)
## Plot probability density function
cl <- rainbow(3)
plot(density(y), col = cl[1], xlab = "y", ylab = NA, ylim = c(0, 0.5), zero.line = FALSE)
lines(density(y[sub]), col = cl[2])
lines(density(y[sub], weights = weights), col = cl[3])
legend("topleft", c("True", "Sample", "Estimate"), lty = c(1,1,1), col = cl)
## Plot cumulative distribution function
plot(NA, xlab = "y", ylab = NA, xlim = c(-2, 3), ylim = c(0, 1))
plot_cdf(y, color = cl[1])
plot_cdf(y[sub], color = cl[2])
plot_cdf(y[sub], weights, color = cl[3])
legend("topleft", c("True", "Sample", "Estimate"), lty = c(1,1,1), col = cl)
})
test_that("Test risk-return tradeoff in portfolio optimization", {
skip_on_cran()
print("PORTFOLIO")
## Problem data
set.seed(10)
n <- 10
SAMPLES <- 100
mu <- matrix(abs(stats::rnorm(n)), nrow = n)
Sigma <- matrix(stats::rnorm(n^2), nrow = n, ncol = n)
Sigma <- t(Sigma) %*% Sigma
## Form problem
w <- Variable(n)
ret <- t(mu) %*% w
risk <- quad_form(w, Sigma)
constraints <- list(w >= 0, sum(w) == 1)
## Risk aversion parameters
gammas <- 10^seq(-2, 3, length.out = SAMPLES)
ret_data <- rep(0, SAMPLES)
risk_data <- rep(0, SAMPLES)
w_data <- matrix(0, nrow = SAMPLES, ncol = n)
## Compute trade-off curve
for(i in 1:length(gammas)) {
gamma <- gammas[i]
objective <- ret - gamma * risk
prob <- Problem(Maximize(objective), constraints)
result <- solve(prob)
expect_equal(result$status, "optimal")
## Evaluate risk/return for current solution
risk_data[i] <- result$getValue(sqrt(risk))
ret_data[i] <- result$getValue(ret)
w_data[i,] <- result$getValue(w)
}
## Plot trade-off curve
plot(risk_data, ret_data, xlab = "Risk (Standard Deviation)", ylab = "Return", xlim = c(0, 4), ylim = c(0, 2), type = "l", lwd = 2, col = "blue")
points(sqrt(diag(Sigma)), mu, col = "red", cex = 1.5, pch = 16)
markers_on <- c(10, 20, 30, 40)
for(marker in markers_on) {
points(risk_data[marker], ret_data[marker], col = "black", cex = 1.5, pch = 15)
nstr <- sprintf("%.2f", gammas[marker])
text(risk_data[marker] + 0.2, ret_data[marker] - 0.05, bquote(paste(gamma, " = ", .(nstr))), cex = 1.5)
}
## Plot weights for a few gamma
w_plot <- t(w_data[markers_on,])
colnames(w_plot) <- sprintf("%.2f", gammas[markers_on])
barplot(w_plot, xlab = expression(paste("Risk Aversion (", gamma, ")", sep = "")), ylab = "Fraction of Budget")
})
test_that("Test Kelly gambling optimal bets", {
skip_on_cran()
print("KELLY")
set.seed(1)
n <- 20 ## Total bets
K <- 100 ## Number of possible returns
PERIODS <- 100
TRIALS <- 5
## Generate return probabilities
ps <- runif(K)
ps <- ps/sum(ps)
## Generate matrix of possible returns
rets <- runif(K*(n-1), 0.5, 1.5)
shuff <- sample(1:length(rets), size = length(rets), replace = FALSE)
rets[shuff[1:30]] <- 0 ## Set 30 returns to be relatively low
rets[shuff[31:60]] <- 5 ## Set 30 returns to be relatively high
rets <- matrix(rets, nrow = K, ncol = n-1)
rets <- cbind(rets, rep(1, K)) ## Last column represents not betting
## Solve for Kelly optimal bets
b <- Variable(n)
obj <- Maximize(t(ps) %*% log(rets %*% b))
constraints <- list(sum(b) == 1, b >= 0)
prob <- Problem(obj, constraints)
result <- solve(prob)
expect_equal(result$status, "optimal")
bets <- result$getValue(b)
## Naive betting scheme: bet in proportion to expected return
bets_cmp <- matrix(0, nrow = n)
bets_cmp[n] <- 0.15 ## Hold 15% of wealth
rets_avg <- ps %*% rets
## tidx <- order(rets_avg[-n], decreasing = TRUE)[1:9]
tidx <- 1:(n-1)
fracs <- rets_avg[tidx]/sum(rets_avg[tidx])
bets_cmp[tidx] <- fracs*(1-bets_cmp[n])
## Calculate wealth over time
wealth <- matrix(0, nrow = PERIODS, ncol = TRIALS)
wealth_cmp <- matrix(0, nrow = PERIODS, ncol = TRIALS)
for(i in 1:TRIALS) {
sidx <- sample(1:K, size = PERIODS, replace = TRUE, prob = ps)
winnings <- rets[sidx,] %*% bets
wealth[,i] <- cumprod(winnings)
winnings_cmp <- rets[sidx,] %*% bets_cmp
wealth_cmp[,i] <- cumprod(winnings_cmp)
}
## Plot Kelly optimal growth trajectories
matplot(1:PERIODS, wealth, xlab = "Time", ylab = "Wealth", log = "y", type = "l", col = "red", lty = 1, lwd = 2)
matlines(1:PERIODS, wealth_cmp, col = "blue", lty = 2, lwd = 2)
legend("topleft", c("Kelly Optimal Bets", "Naive Bets"), col = c("red", "blue"), lty = c(1, 2), lwd = 2, bty = "n")
})
test_that("Test worst-case covariance", {
skip_on_cran()
print("WORST CASE")
## Problem data
w <- matrix(c(0.1, 0.2, -0.05, 0.1))
## Constraint matrix:
## [[0.2, + , +, +-],
## [+, 0.1, -, - ],
## [+, -, 0.3, + ],
## [+-, -, +, 0.1]]
## Form problem
Sigma <- Variable(4, 4, PSD = TRUE)
obj <- Maximize(t(w) %*% Sigma %*% w)
constraints <- list(Sigma[1,1] == 0.2, Sigma[2,2] == 0.1, Sigma[3,3] == 0.3, Sigma[4,4] == 0.1,
Sigma[1,2] >= 0, Sigma[1,3] >= 0, Sigma[2,3] <= 0, Sigma[2,4] <= 0, Sigma[3,4] >= 0)
prob <- Problem(obj, constraints)
result <- solve(prob, solver = "SCS")
expect_equal(result$status, "optimal")
print(result$getValue(Sigma))
## Sigma_true <- rbind(c(0.2, 0.0978, 0, 0.0741),
## c(0.0978, 0.1, -0.101, 0),
## c(0, -0.101, 0.3, 0),
## c(0.0741, 0, 0, 0.1))
## The new SCS solver gives slightly different results from the above
## So the values below have been obtained from cvxpy 1.25.
Sigma_true <- rbind(c(0.2, 0.09674, 0, 0.0762),
c(0.09674, 0.1, -0.103, 0),
c(0, -0.103, 0.3, 0.0041),
c(0.0762, 0, .0041, 0.1))
expect_equal(sqrt(result$value), 0.1232, tolerance = 1e-3)
expect_equal(result$getValue(Sigma), Sigma_true, tolerance = 1e-3)
## Problem data
n <- 5
w <- rexp(n)
w <- w / sum(w)
mu <- abs(matrix(stats::rnorm(n), nrow = n, ncol = 1))/15
Sigma_nom <- matrix(runif(n^2, -0.15, 0.8), nrow = n, ncol = n)
Sigma_nom <- t(Sigma_nom) %*% Sigma_nom
## Known upper and lower bounds
Delta <- matrix(0.2, nrow = n, ncol = n)
diag(Delta) <- 0
U <- Sigma_nom + Delta
L <- Sigma_nom - Delta
Sigma <- Variable(n, n, PSD = TRUE)
obj <- quad_form(w, Sigma)
constr <- list(L <= Sigma, Sigma <= U, Sigma == t(Sigma))
prob <- Problem(Maximize(obj), constr)
result <- solve(prob)
expect_equal(result$status, "optimal")
print(result$getValue(Sigma))
})
test_that("Test sparse inverse covariance estimation", {
skip_on_cran()
library(Matrix)
library(expm)
print("SPARSE INV")
set.seed(1)
n <- 10 ## Dimension of matrix
m <- 1000 ## Number of samples
alphas <- c(10, 4, 1)
## Create sparse, symmetric PSD matrix S
A <- rsparsematrix(n, n, 0.15, rand.x = stats::rnorm)
Strue <- A %*% t(A) + 0.05 * diag(rep(1, n)) ## Force matrix to be strictly positive definite
image(Strue != 0, main = "Inverse of true covariance matrix")
## Create covariance matrix associated with S
R <- base::solve(Strue)
## Sample from distribution with covariance R
## If Y ~ N(0, I), then R^{1/2} * Y ~ N(0, R) since R is symmetric
x_sample <- matrix(stats::rnorm(n*m), nrow = m, ncol = n) %*% t(expm::sqrtm(R))
Q <- cov(x_sample) ## Sample covariance matrix
S <- Variable(n, n, PSD = TRUE) ## Variable constrained to positive semidefinite cone
obj <- Maximize(log_det(S) - matrix_trace(S %*% Q))
for(alpha in alphas) {
constraints <- list(sum(abs(S)) <= alpha)
## Form and solve optimization problem
prob <- Problem(obj, constraints)
result <- solve(prob)
expect_equal(result$status, "optimal")
## Create covariance matrix
R_hat <- base::solve(result$getValue(S))
Sres <- result$getValue(S)
Sres[abs(Sres) <= 1e-4] <- 0
title <- bquote(bold(paste("Estimated inv. cov matrix (", alpha, " = ", .(alpha), ")")))
image(Sres != 0, main = title)
}
})
test_that("Test fastest mixing Markov chain (FMMC)", {
skip_on_cran()
print("MCMC")
## Boyd, Diaconis, and Xiao. SIAM Rev. 46 (2004) pgs. 667-689 at pg. 672
## Form the complementary graph
antiadjacency <- function(g) {
n <- max(as.numeric(names(g))) ## Assumes names are integers starting from 1
a <- lapply(1:n, function(i) c())
names(a) <- 1:n
for(x in names(g)) {
for(y in 1:n) {
if(!(y %in% g[[x]]))
a[[x]] <- c(a[[x]], y)
}
}
a
}
## Fastest mixing Markov chain on graph g
FMMC <- function(g, verbose = FALSE) {
a <- antiadjacency(g)
n <- length(names(a))
P <- Variable(n, n)
o <- rep(1, n)
objective <- Minimize(norm(P - 1.0/n, "2"))
constraints <- list(P %*% o == o, t(P) == P, P >= 0)
for(i in names(a)) {
for(j in a[[i]]) { ## (i-j) is a not-edge of g!
idx <- as.numeric(i)
if(idx != j)
constraints <- c(constraints, P[idx,j] == 0)
}
}
prob <- Problem(objective, constraints)
result <- solve(prob)
expect_equal(result$status, "optimal")
if(verbose)
cat("Status: ", result$status, ", Optimal Value = ", result$value)
list(status = result$status, value = result$value, P = result$getValue(P))
}
disp_result <- function(states, P, tol = 1e-3) {
rownames(P) <- states
colnames(P) <- states
print(P)
}
## SIAM Rev. 46 examples pg. 674: Figure 1 and Table 1
## a) line graph L(4)
g <- list("1" = 2, "2" = c(1,3), "3" = c(2,4), "4" = 3)
result <- FMMC(g, verbose = TRUE)
disp_result(names(g), result$P)
## b) triangle + one edge
g <- list("1" = 2, "2" = c(1,3,4), "3" = c(2,4), "4" = c(2,3))
result <- FMMC(g, verbose = TRUE)
disp_result(names(g), result$P)
## c) bipartite 2 + 3
g <- list("1" = c(2,4,5), "2" = c(1,3), "3" = c(2,4,5), "4" = c(1,3), "5" = c(1,3))
result <- FMMC(g, verbose = TRUE)
disp_result(names(g), result$P)
## d) square + central point
g <- list("1" = c(2,3,5), "2" = c(1,4,5), "3" = c(1,4,5), "4" = c(2,3,5), "5" = c(1,2,3,4,5))
result <- FMMC(g, verbose = TRUE)
disp_result(names(g), result$P)
})
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.