~enricoschumann/neighbours

59c40e7c7a353521616f420a5119abc50eec523e — Enrico Schumann 2 years ago 1d3dbda
Update vignette
2 files changed, 30 insertions(+), 25 deletions(-)

M DESCRIPTION
M vignettes/neighbours.Rnw
M DESCRIPTION => DESCRIPTION +1 -1
@@ 2,7 2,7 @@ Package: neighbours
Type: Package
Title: Neighbourhood Functions for Local-Search Algorithms
Version: 0.1-0
Date: 2022-08-11
Date: 2022-08-15
Maintainer: Enrico Schumann <es@enricoschumann.net>
Authors@R: person(given = "Enrico", family = "Schumann",
                  role  = c("aut", "cre"),

M vignettes/neighbours.Rnw => vignettes/neighbours.Rnw +29 -24
@@ 7,6 7,7 @@
\usepackage[T1]{fontenc}
\usepackage[scaled=0.9]{inconsolata}
\renewcommand*\familydefault{\sfdefault}
\usepackage{sfmath}
\usepackage{hyperref}
\usepackage{natbib}
\usepackage{xcolor}


@@ 83,14 84,14 @@ LSopt <- if (requireNamespace("NMOF")) {

\section*{Example: Selecting elements of a list}

\noindent We are given a numeric vector~$y$, and also a
\noindent We are given a numeric vector~$y$ and also a
matrix~$X$, which has as many rows as~$y$ has elements.
The aim now is to find a subset of columns of~$X$ whose
average is as lowly correlated with $y$ as possible.
Let us create random data.

<<data>>=
ny <- 50     ## length of y
ny <- 50     ## length of y, number of rows of X
nx <- 500    ## number of columns of X
y <- runif(ny)
X <- array(runif(ny * nx), dim = c(ny, nx))


@@ 99,7 100,7 @@ X <- array(runif(ny * nx), dim = c(ny, nx))
\noindent We'll try a (stochastic) Local Search to compute a
solution.  There may be other, perhaps better
heuristics for the job.  But a Local Search will
compute a good solution (as we will see), and it is
compute a good solution, as we will see; and it is
simple, which is a good idea for an example.  See
\citet[Chapter~13]{Gilli2019} for a tutorial on Local
Search.


@@ 144,9 145,9 @@ nb <- neighbourfun(type = "logical", kmin = 10, kmax = 20)
\noindent It remains to run the Local Search.
<<LSopt-run>>=
sol.ls <- LSopt(column_cor,
                list(x0 = x0,
                     neighbour = nb,
                     nI = 3000,
                list(neighbour = nb,
                     x0 = x0,      ## initial solution
                     nI = 3000,    ## number of iterations
                     printBar = FALSE),
                X = X, y = y)
@


@@ 160,7 161,7 @@ And we check the constraints: how many columns are in the solution?
sum(sol.ls$xbest)
@

We can also visualise the initial and the final
We can visualise the initial and the final
solution.  The negative correlation is clearly visible.

<<fig=true, width = 5, height = 3.2>>=


@@ 237,17 238,17 @@ do.call(compare_vectors, xs)

\section*{Another example: minimising portfolio risk}

Suppose we are given a matrix $R$ and aim to
find a vector $x$ such that the variance of the
elements in the product~$Rx$ is minimised.  This is
common problem in finance, in which $R$ could be a
matrix of asset-price returns, with each column holding
the returns of one asset, and $x$ a vector of portfolio
Suppose we are given a matrix $R$ and aim to find a
vector $x$ such that the variance of the elements in
the product~$Rx$ is minimised.  This is a common
problem in finance, in which $R$ could be a matrix of
asset-price returns, with each column holding the
returns of one asset, and $x$ a vector of portfolio
weights.

We'll solve this problem, as in the previous example,
with Local Search.  Our solution now is a numeric
vector~$x$; and again we need two functions -- the objective
with Local Search.  The solution now is a numeric
vector~$x$.  Again we need two functions -- the objective
function and the neighbourhood function -- to find the
optimal (or at least a good) vector.



@@ 301,7 302,8 @@ elements remains unchanged.  (That is a typical
restriction in portfolio-selection models.)

<<nb>>=
nb <- neighbourfun(type = "numeric", min = 0, max = 0.2,
nb <- neighbourfun(type = "numeric",
                   min = 0, max = 0.2,
                   stepsize = 0.005)
@



@@ 344,7 346,7 @@ example is the so-called semi-variance, defined as

\begin{equation}
  \label{eq:sv}
  \frac{1}{N}\sum_{i=1}^N \min(R_ix - m, 0)^2
  \frac{1}{m}\sum_{i=1}^m \min(R_ix - \frac{1}{m}\iota'(Rx), 0)^2
\end{equation}

All we have to do now is to exchange objective functions,


@@ 372,11 374,11 @@ can update the product and save computing time (the longer
$x$ is, the more time we can save).

Let $x^{\mathrm{c}}$ denote the current solution and
$x^{\mathrm{n}}$ the neighbour solution. This latter
solution is produced by adding element-wise the vector
$x^{\Delta}$ to $x^{\mathrm{c}}$. If only few elements
change, the n $x^{\Delta}$ will be relatively sparse,
i.e. many of its elements are zero.
$x^{\mathrm{n}}$ the neighbour solution, produced by
adding element-wise the vector $x^{\Delta}$ to
$x^{\mathrm{c}}$. If only few elements change, then
$x^{\Delta}$ will be relatively sparse, i.e. many of
its elements are zero.

\begin{equation*}
   x^{\mathrm{n}} = \phantom{A(}x^{\mathrm{c}} + x^{\Delta}\,.


@@ 386,8 388,11 @@ Then we have:
  Ax^{\mathrm{n}} = A(x^{\mathrm{c}} + x^{\Delta}) =
  \underbrace{\ \ Ax^{\mathrm{c}}\ \ }_{\text{known}} + Ax^{\Delta}\,.
\end{equation*}
So we only need to compute $Ax^{\Delta}$ in every iteration,
not the full $Ax$.

So we only need to compute $Ax^{\Delta}$ in every
iteration, in which many columns of $A$ are ignored
because the corresponding elements of~ $x^{\Delta}$
are zero.

Let us solve the risk-minimisation model one more time.
First, we add the initial product $Ax$ as an attribute to