<commit_before>/** @file vlad.c
 ** @brief VLAD - Declaration
 ** @author <NAME>
 ** @author <NAME>
 **/

/*
Copyright (C) 2013 <NAME> and <NAME>.
All rights reserved.

This file is part of the VLFeat library and is made available under
the terms of the BSD license (see the COPYING file).
*/

/**
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
@page vlad Vector of Locally Aggregated Descriptors (VLAD) encoding
@author <NAME>
@author <NAME>
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->

@ref vlad.h implements the *Vector of Linearly Aggregated Descriptors*
(VLAD) image representation @cite{jegou10vlad}
@cite{arandjelovic13all-about}.

@ref vlad-starting demonstrates ho to use VLFeat to compute the VLAD
encoding of an image. For further details refere to:

- @subpage vlad-fundamentals

@section vlad-starting Getting started

To get the VLAD vector siply run the function ::vl_vlad_encode
with correct parameters. The function can be applied to both
@c float or @c double data types.

@page vlad-fundamentals Fundamentals
@tableofcontents

VLAD can be seen as a *feature encoding and pooling* method, similar
to @ref fisher "Fisher vectors". VLAD encodes a set of local feature
descriptors $I=(\bx_1,\dots,\bx_n)$ extracted from an image using a
dictionary built using a clustering method such as @ref gmm or @ref
kmeans. Let $q_{ik}$ be the strength of the association of data vector
$\bx_i$ to cluster $\mu_k$, such that $q_{ik} \geq 0$ and
$\sum_{k=1}^K q_{ik} = 1$. The association may be either soft
(e.g. obtained as the posterior probabilities of the GMM clusters) or
hard (e.g. obtained by vector quantization with K-means).

$\mu_k$ are the cluster *means*, vectors of the same dimension as the
data $\bx_i$. VLAD encodes feature $\bx_$ by considering the *residuals*
\[
 \bv_k = \sum_{i=1}^{N} q_{ik} (\bx_{i} - \mu_k).
\]
The residulas are stacked together to obtain the vector
\[
\hat\Phi(I) =
\begin{bmatrix}
\vdots \\
\bv_k \\
\vdots
\end{bmatrix}
\]

Before the VLAD encoding is used it is usually globally $L^2$ normalized:
\[
 \Phi(I) = \hat\Phi(I) / \|\hat\Phi(I)\|_2.
\]
In this manner, the Euclidean distance and inner product between VLAD
vectors becomre more meaningful.

@subsection vlad-component-normalization

However the size of each cluster could have a negative imapact on the
appearance of the vector @f$ V @f$ and so, the normalization of each
aggregate of vectors could be used:

@f[
  v_{j} = { \sum_{i=1}^{N} {q_{i,j} x_{i}}  \over { \sum_{i=1}^{N} {q_{i,j}} } } - \mu_{j}
@f]

This normalization is controlled by the last argument of ::vl_vlad_encode.


*/

#include "vlad.h"
#include "mathop.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#if defined(_OPENMP)
#include <omp.h>
#endif

/* ================================================================ */
#ifdef VL_VLAD_INSTANTIATING

static void
VL_XCAT(_vl_vlad_encode_, SFX)
(TYPE * enc,
 vl_size numData,
 TYPE const * means,
 vl_size dimension,
 vl_size numClusters,
 TYPE const * data,
 TYPE const * assignments,
 int flags)
{
  vl_uindex dim ;
  vl_index i_cl, i_d ;

  memset(enc, 0, sizeof(TYPE) * dimension * numClusters) ;

#if defined(_OPENMP)
#pragma omp parallel for default(shared) private(i_cl,i_d,dim) num_threads(vl_get_max_threads())
#endif
  for (i_cl = 0; i_cl < (signed)numClusters; i_cl++) {
    double clusterMass = 0 ;
    for (i_d = 0; i_d < (signed)numData; i_d++) {
      if (assignments[i_d*numClusters + i_cl] > 0) {
        double q = assignments[i_d*numClusters+i_cl] ;
        clusterMass +=  q ;
        for(dim = 0; dim < dimension; dim++) {
          enc [i_cl * dimension + dim] += q * data [i_d  * dimension + dim] ;
        }
      }
    }
    
    if (clusterMass > 0) {
      if (flags & VL_VLAD_FLAG_NORMALIZE_MASS) {
        for(dim = 0; dim < dimension; dim++) {
          enc[i_cl*dimension + dim] /= clusterMass ;
          enc[i_cl*dimension + dim] -= means[i_cl*dimension+dim];
        }
      } else {
        for(dim = 0; dim < dimension; dim++) {
          enc[i_cl*dimension + dim] -= clusterMass * means[i_cl*dimension+dim];
        }
      }
    }
    
    if (flags & VL_VLAD_FLAG_SQUARE_ROOT) {
      for(dim = 0; dim < dimension; dim++) {
        TYPE z = enc[i_cl*dimension + dim] ;
        if (z >= 0) {
          enc[i_cl*dimension + dim] = VL_XCAT(vl_sqrt_, SFX)(z) ;
        } else {
          enc[i_cl*dimension + dim] = - VL_XCAT(vl_sqrt_, SFX)(- z) ;
        }
      }
    }
    
    if (flags & VL_VLAD_FLAG_NORMALIZE_COMPONENTS) {
      TYPE n = 0 ;
      dim = 0 ;
      for(dim = 0; dim < dimension; dim++) {
        TYPE z = enc[i_cl*dimension + dim] ;
        n += z * z ;
      }
      n = VL_XCAT(vl_sqrt_, SFX)(n) ;
      for(dim = 0; dim < dimension; dim++) {
        enc[i_cl*dimension + dim] /= n ;
      }
    }
  }
  
  if (! (flags & VL_VLAD_FLAG_UNNORMALIZED)) {
    TYPE n = 0 ;
    for(dim = 0 ; dim < dimension * numClusters ; dim++) {
      TYPE z = enc [dim] ;
      n += z * z ;
    }
    n = VL_XCAT(vl_sqrt_, SFX)(n) ;
    for(dim = 0 ; dim < dimension * numClusters ; dim++) {
      enc[dim] /= n ;
    }
  }
}

/* VL_FISHER_INSTANTIATING */
#else

#ifndef __DOXYGEN__
#define FLT VL_TYPE_FLOAT
#define TYPE float
#define SFX f
#define VL_VLAD_INSTANTIATING
#include "vlad.c"

#define FLT VL_TYPE_DOUBLE
#define TYPE double
#define SFX d
#define VL_VLAD_INSTANTIATING
#include "vlad.c"
#endif

/* VL_VLAD_INSTANTIATING */
#endif

/* ================================================================ */
#ifndef VL_VLAD_INSTANTIATING

/** @brief Calculates the VLAD encoding of a set of vectors.
 ** @param enc output VLAD encoding (out).
 ** @param dataType the type of the input data (::VL_TYPE_DOUBLE or ::VL_TYPE_FLOAT).
 ** @param numData number of data vectors to encode.
 ** @param means cluster means.
 ** @param numClusters number of clusters.
 ** @param data the data vectors to encode.
 ** @param dimension dimensionality of the data.
 ** @param assignments data to cluster soft assignments.
 ** @param flags further options.
 **
 ** @a enc is the encoded output, a vector of size @a numClusters by
 ** @a dimension. @a means is a matrix with @a numClusters columns and
 ** @a dimension rows. @a data is the matrix of vectors to be encoded,
 ** with @a dimension rows and @a numData columns. @a assignments is a
 ** matrix with @a numClusters rows and @a numData columns.
 **
 ** @sa @ref vlad
 **/

void
vl_vlad_encode (void * enc,
                vl_type dataType,
                vl_size numData,
                void const * means,
                vl_size dimension,
                vl_size numClusters,
                void const * data,
                void const * assignments,
                int flags)
{
  switch(dataType) {
    case VL_TYPE_FLOAT:
      _vl_vlad_encode_f ((float *) enc, numData,
                         (float const *) means, dimension, numClusters,
                         (float const *) data,
                         (float const *) assignments, flags) ;
      break;
    case VL_TYPE_DOUBLE:
      _vl_vlad_encode_d ((double *) enc, numData,
                         (double const *) means, dimension, numClusters,
                         (double const *) data,
                         (double const *) assignments, flags) ;
      break;
    default:
      abort();
  }
}

/* ! VL_VLAD_INSTANTIATING */
#endif

#undef SFX
#undef TYPE
#undef FLT
#undef VL_VLAD_INSTANTIATING
<commit_msg>vlad: avoid NaNs on empty components<commit_after>/** @file vlad.c
 ** @brief VLAD - Declaration
 ** @author <NAME>
 ** @author <NAME>
 **/

/*
Copyright (C) 2013 <NAME> and <NAME>.
All rights reserved.

This file is part of the VLFeat library and is made available under
the terms of the BSD license (see the COPYING file).
*/

/**
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
@page vlad Vector of Locally Aggregated Descriptors (VLAD) encoding
@author <NAME>
@author <NAME>
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->

@ref vlad.h implements the *Vector of Linearly Aggregated Descriptors*
(VLAD) image representation @cite{jegou10vlad}
@cite{arandjelovic13all-about}.

@ref vlad-starting demonstrates ho to use VLFeat to compute the VLAD
encoding of an image. For further details refere to:

- @subpage vlad-fundamentals

@section vlad-starting Getting started

To get the VLAD vector siply run the function ::vl_vlad_encode
with correct parameters. The function can be applied to both
@c float or @c double data types.

@page vlad-fundamentals Fundamentals
@tableofcontents

VLAD can be seen as a *feature encoding and pooling* method, similar
to @ref fisher "Fisher vectors". VLAD encodes a set of local feature
descriptors $I=(\bx_1,\dots,\bx_n)$ extracted from an image using a
dictionary built using a clustering method such as @ref gmm or @ref
kmeans. Let $q_{ik}$ be the strength of the association of data vector
$\bx_i$ to cluster $\mu_k$, such that $q_{ik} \geq 0$ and
$\sum_{k=1}^K q_{ik} = 1$. The association may be either soft
(e.g. obtained as the posterior probabilities of the GMM clusters) or
hard (e.g. obtained by vector quantization with K-means).

$\mu_k$ are the cluster *means*, vectors of the same dimension as the
data $\bx_i$. VLAD encodes feature $\bx_$ by considering the *residuals*
\[
 \bv_k = \sum_{i=1}^{N} q_{ik} (\bx_{i} - \mu_k).
\]
The residulas are stacked together to obtain the vector
\[
\hat\Phi(I) =
\begin{bmatrix}
\vdots \\
\bv_k \\
\vdots
\end{bmatrix}
\]

Before the VLAD encoding is used it is usually globally $L^2$ normalized:
\[
 \Phi(I) = \hat\Phi(I) / \|\hat\Phi(I)\|_2.
\]
In this manner, the Euclidean distance and inner product between VLAD
vectors becomre more meaningful.

@subsection vlad-component-normalization

However the size of each cluster could have a negative imapact on the
appearance of the vector @f$ V @f$ and so, the normalization of each
aggregate of vectors could be used:

@f[
  v_{j} = { \sum_{i=1}^{N} {q_{i,j} x_{i}}  \over { \sum_{i=1}^{N} {q_{i,j}} } } - \mu_{j}
@f]

This normalization is controlled by the last argument of ::vl_vlad_encode.


*/

#include "vlad.h"
#include "mathop.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#if defined(_OPENMP)
#include <omp.h>
#endif

/* ================================================================ */
#ifdef VL_VLAD_INSTANTIATING

static void
VL_XCAT(_vl_vlad_encode_, SFX)
(TYPE * enc,
 vl_size numData,
 TYPE const * means,
 vl_size dimension,
 vl_size numClusters,
 TYPE const * data,
 TYPE const * assignments,
 int flags)
{
  vl_uindex dim ;
  vl_index i_cl, i_d ;

  memset(enc, 0, sizeof(TYPE) * dimension * numClusters) ;

#if defined(_OPENMP)
#pragma omp parallel for default(shared) private(i_cl,i_d,dim) num_threads(vl_get_max_threads())
#endif
  for (i_cl = 0; i_cl < (signed)numClusters; i_cl++) {
    double clusterMass = 0 ;
    for (i_d = 0; i_d < (signed)numData; i_d++) {
      if (assignments[i_d*numClusters + i_cl] > 0) {
        double q = assignments[i_d*numClusters+i_cl] ;
        clusterMass +=  q ;
        for(dim = 0; dim < dimension; dim++) {
          enc [i_cl * dimension + dim] += q * data [i_d  * dimension + dim] ;
        }
      }
    }

    if (clusterMass > 0) {
      if (flags & VL_VLAD_FLAG_NORMALIZE_MASS) {
        for(dim = 0; dim < dimension; dim++) {
          enc[i_cl*dimension + dim] /= clusterMass ;
          enc[i_cl*dimension + dim] -= means[i_cl*dimension+dim];
        }
      } else {
        for(dim = 0; dim < dimension; dim++) {
          enc[i_cl*dimension + dim] -= clusterMass * means[i_cl*dimension+dim];
        }
      }
    }

    if (flags & VL_VLAD_FLAG_SQUARE_ROOT) {
      for(dim = 0; dim < dimension; dim++) {
        TYPE z = enc[i_cl*dimension + dim] ;
        if (z >= 0) {
          enc[i_cl*dimension + dim] = VL_XCAT(vl_sqrt_, SFX)(z) ;
        } else {
          enc[i_cl*dimension + dim] = - VL_XCAT(vl_sqrt_, SFX)(- z) ;
        }
      }
    }

    if (flags & VL_VLAD_FLAG_NORMALIZE_COMPONENTS) {
      TYPE n = 0 ;
      dim = 0 ;
      for(dim = 0; dim < dimension; dim++) {
        TYPE z = enc[i_cl*dimension + dim] ;
        n += z * z ;
      }
      n = VL_XCAT(vl_sqrt_, SFX)(n) ;
      n = VL_MAX(n, 1e-12) ;
      for(dim = 0; dim < dimension; dim++) {
        enc[i_cl*dimension + dim] /= n ;
      }
    }
  }

  if (! (flags & VL_VLAD_FLAG_UNNORMALIZED)) {
    TYPE n = 0 ;
    for(dim = 0 ; dim < dimension * numClusters ; dim++) {
      TYPE z = enc [dim] ;
      n += z * z ;
    }
    n = VL_XCAT(vl_sqrt_, SFX)(n) ;
    n = VL_MAX(n, 1e-12) ;
    for(dim = 0 ; dim < dimension * numClusters ; dim++) {
      enc[dim] /= n ;
    }
  }
}

/* VL_FISHER_INSTANTIATING */
#else

#ifndef __DOXYGEN__
#define FLT VL_TYPE_FLOAT
#define TYPE float
#define SFX f
#define VL_VLAD_INSTANTIATING
#include "vlad.c"

#define FLT VL_TYPE_DOUBLE
#define TYPE double
#define SFX d
#define VL_VLAD_INSTANTIATING
#include "vlad.c"
#endif

/* VL_VLAD_INSTANTIATING */
#endif

/* ================================================================ */
#ifndef VL_VLAD_INSTANTIATING

/** @brief Calculates the VLAD encoding of a set of vectors.
 ** @param enc output VLAD encoding (out).
 ** @param dataType the type of the input data (::VL_TYPE_DOUBLE or ::VL_TYPE_FLOAT).
 ** @param numData number of data vectors to encode.
 ** @param means cluster means.
 ** @param numClusters number of clusters.
 ** @param data the data vectors to encode.
 ** @param dimension dimensionality of the data.
 ** @param assignments data to cluster soft assignments.
 ** @param flags further options.
 **
 ** @a enc is the encoded output, a vector of size @a numClusters by
 ** @a dimension. @a means is a matrix with @a numClusters columns and
 ** @a dimension rows. @a data is the matrix of vectors to be encoded,
 ** with @a dimension rows and @a numData columns. @a assignments is a
 ** matrix with @a numClusters rows and @a numData columns.
 **
 ** @sa @ref vlad
 **/

void
vl_vlad_encode (void * enc,
                vl_type dataType,
                vl_size numData,
                void const * means,
                vl_size dimension,
                vl_size numClusters,
                void const * data,
                void const * assignments,
                int flags)
{
  switch(dataType) {
    case VL_TYPE_FLOAT:
      _vl_vlad_encode_f ((float *) enc, numData,
                         (float const *) means, dimension, numClusters,
                         (float const *) data,
                         (float const *) assignments, flags) ;
      break;
    case VL_TYPE_DOUBLE:
      _vl_vlad_encode_d ((double *) enc, numData,
                         (double const *) means, dimension, numClusters,
                         (double const *) data,
                         (double const *) assignments, flags) ;
      break;
    default:
      abort();
  }
}

/* ! VL_VLAD_INSTANTIATING */
#endif

#undef SFX
#undef TYPE
#undef FLT
#undef VL_VLAD_INSTANTIATING

{'fraction_non_alphanumeric': 0.1491117173464635, 'fraction_numerical': 0.0050568900126422255, 'mean_word_length': 2.891765924391507, 'pattern_counts': {'":': 0, '<': 47, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5639879', 'n_tokens_mistral': 5215, 'n_tokens_neox': 4916, 'n_words': 1603}