Vector Dot Product (dotprod)

This module provides interfaces for computing a vector dot product between two equally-sized vectors. Dot products are commonly used in digital signal processing for communications, particularly in filtering and matrix operations. Given two vectors of equal length \(\vec{x} = \left[x(0),x(1),\ldots,x(N-1)\right]^T\) and \(\vec{v} = \left[v(0),v(1),\ldots,v(N-1)\right]^T\), the vector dot product between them is computed as

\[\vec{x} \cdot \vec{v} = \vec{x}^T \vec{v} = \sum_{k=0}^{N-1}{ x(k) v(k) }\]

A number of other liquid modules rely on dotprod, such as filtering and equalization.

Specific machine architectures

The vector dot product has a complexity of \(\mathcal{O}(N)\) multiply-and-accumulate operations. Because of its prevalence in multimedia applications, a considerable amount of research has been put into computing the vector dot product as efficiently as possible. Software-defined radio is no exception as basic profiling will likely demonstrate that a considerable portion of the processor is spent computing it. Certain machine architectures have specific instructions for computing vector dot products, particularly those which use a single instruction for multiple data (SIMD) such as MMX, SSE, AVX, AltiVec, and Neon.

Interface

There are effectively two ways to use the dotprod module. In the first and most general case, a vector dot product is computed on two input vectors \(\vec{x}\) and \(\vec{v}\) whose values are not known a priori. In the second case, a dotprod object is created around vector \(\vec{v}\) which does not change (or rarely changes) throughout its life cycle. This is the more convenient method for filtering objects which don’t usually have time-dependent coefficients. Listed below is a simple interface example to the dotprod module object:

#include <liquid/liquid.h>

int main() {
    // create input arrays
    float x[] = { 1.0f,  2.0f,  3.0f,  4.0f,  5.0f};
    float v[] = { 0.1f, -0.2f,  1.0f, -0.2f,  0.1f};
    float y;

    // run the basic vector dot product, store in 'y'
    dotprod_rrrf_run(x,v,5,&y);

    // create dotprod object and execute, store in 'y'
    dotprod_rrrf q = dotprod_rrrf_create(v,5);
    dotprod_rrrf_execute(q,x,&y);
    dotprod_rrrf_destroy(q);
}

In both cases the dotprod can be easily integrated with the window object ([section-buffer-window]) for managing input data and alignment. There are three types of dot product objects and are listed in [tab-dotprod-objects].

Table 1 Data types for dotprod family of objects

Precision

Input/Output

Coefficients

Interface

float

real

real

dotprod_rrrf

float

complex

complex

dotprod_cccf

float

complex

real

dotprod_crcf

Listed below is the full interface to the dotprod family of objects.

run()

Run dot product without creating object. This is less efficient than creating the object as it is an unoptimized portable implementation that doesn’t take advantage of processor extensions. It is meant to provide a baseline for performance comparison and a convenient way to invoke a dot product operation when fast operation is not necessary.

int = dotprod_rrrf_run(float * _v, float * _x, unsigned int _n, float * _y);
  • float * _v: coefficients array

  • float * _x: input array

  • unsigned int _n: dotprod length

  • float * _y: output sample pointer

  • returns standard error code

int = dotprod_cccf_run(float complex * _v, float complex * _x, unsigned int _n, float complex * _y);
  • float complex * _v: coefficients array

  • float complex * _x: input array

  • unsigned int _n: dotprod length

  • float complex * _y: output sample pointer

  • returns standard error code

int = dotprod_crcf_run(float * _v, float complex * _x, unsigned int _n, float complex * _y);
  • float * _v: coefficients array

  • float complex * _x: input array

  • unsigned int _n: dotprod length

  • float complex * _y: output sample pointer

  • returns standard error code

run4()

This provides the same unoptimized operation as the ‘run()’ method above, but with the loop unrolled by a factor of 4. It is marginally faster than ‘run()’ without unrolling the loop.

int = dotprod_rrrf_run4(float * _v, float * _x, unsigned int _n, float * _y);
  • float * _v: coefficients array

  • float * _x: input array

  • unsigned int _n: dotprod length

  • float * _y: output sample pointer

  • returns standard error code

int = dotprod_cccf_run4(float complex * _v, float complex * _x, unsigned int _n, float complex * _y);
  • float complex * _v: coefficients array

  • float complex * _x: input array

  • unsigned int _n: dotprod length

  • float complex * _y: output sample pointer

  • returns standard error code

int = dotprod_crcf_run4(float * _v, float complex * _x, unsigned int _n, float complex * _y);
  • float * _v: coefficients array

  • float complex * _x: input array

  • unsigned int _n: dotprod length

  • float complex * _y: output sample pointer

  • returns standard error code

create()

Create vector dot product object

dotprod_rrrf = dotprod_rrrf_create(float * _v, unsigned int _n);
  • float * _v: coefficients array

  • unsigned int _n: dotprod length

  • returns new dotprod_rrrf object

dotprod_cccf = dotprod_cccf_create(float complex * _v, unsigned int _n);
  • float complex * _v: coefficients array

  • unsigned int _n: dotprod length

  • returns new dotprod_cccf object

dotprod_crcf = dotprod_crcf_create(float * _v, unsigned int _n);
  • float * _v: coefficients array

  • unsigned int _n: dotprod length

  • returns new dotprod_crcf object

create_rev()

Create vector dot product object with time-reversed coefficients

dotprod_rrrf = dotprod_rrrf_create_rev(float * _v, unsigned int _n);
  • float * _v: time-reversed coefficients array

  • unsigned int _n: dotprod length

  • returns new dotprod_rrrf object

dotprod_cccf = dotprod_cccf_create_rev(float complex * _v, unsigned int _n);
  • float complex * _v: time-reversed coefficients array

  • unsigned int _n: dotprod length

  • returns new dotprod_cccf object

dotprod_crcf = dotprod_crcf_create_rev(float * _v, unsigned int _n);
  • float * _v: time-reversed coefficients array

  • unsigned int _n: dotprod length

  • returns new dotprod_crcf object

recreate()

Re-create dot product object of potentially a different length with different coefficients. If the length of the dot product object does not change, no memory reallocation is invoked.

dotprod_rrrf = dotprod_rrrf_recreate(dotprod_rrrf _q, float * _v, unsigned int _n);
  • dotprod_rrrf _q: old dotprod object

  • float * _v: coefficients array

  • unsigned int _n: dotprod length

  • returns new dotprod_rrrf object

dotprod_cccf = dotprod_cccf_recreate(dotprod_cccf _q, float complex * _v, unsigned int _n);
  • dotprod_cccf _q: old dotprod object

  • float complex * _v: coefficients array

  • unsigned int _n: dotprod length

  • returns new dotprod_cccf object

dotprod_crcf = dotprod_crcf_recreate(dotprod_crcf _q, float * _v, unsigned int _n);
  • dotprod_crcf _q: old dotprod object

  • float * _v: coefficients array

  • unsigned int _n: dotprod length

  • returns new dotprod_crcf object

recreate_rev()

Re-create dot product object of potentially a different length with different coefficients. If the length of the dot product object does not change, no memory reallocation is invoked. Filter coefficients are stored in reverse order.

dotprod_rrrf = dotprod_rrrf_recreate_rev(dotprod_rrrf _q, float * _v, unsigned int _n);
  • dotprod_rrrf _q: old dotprod object

  • float * _v: time-reversed coefficients array

  • unsigned int _n: dotprod length

  • returns new dotprod_rrrf object

dotprod_cccf = dotprod_cccf_recreate_rev(dotprod_cccf _q, float complex * _v, unsigned int _n);
  • dotprod_cccf _q: old dotprod object

  • float complex * _v: time-reversed coefficients array

  • unsigned int _n: dotprod length

  • returns new dotprod_cccf object

dotprod_crcf = dotprod_crcf_recreate_rev(dotprod_crcf _q, float * _v, unsigned int _n);
  • dotprod_crcf _q: old dotprod object

  • float * _v: time-reversed coefficients array

  • unsigned int _n: dotprod length

  • returns new dotprod_crcf object

copy()

Copy object including all internal objects and state

dotprod_rrrf = dotprod_rrrf_copy(dotprod_rrrf _q);
  • dotprod_rrrf _q:

  • returns new dotprod_rrrf object

dotprod_cccf = dotprod_cccf_copy(dotprod_cccf _q);
  • dotprod_cccf _q:

  • returns new dotprod_cccf object

dotprod_crcf = dotprod_crcf_copy(dotprod_crcf _q);
  • dotprod_crcf _q:

  • returns new dotprod_crcf object

destroy()

Destroy dotprod object, freeing all internal memory

int = dotprod_rrrf_destroy(dotprod_rrrf _q);
  • dotprod_rrrf _q:

  • returns standard error code

int = dotprod_cccf_destroy(dotprod_cccf _q);
  • dotprod_cccf _q:

  • returns standard error code

int = dotprod_crcf_destroy(dotprod_crcf _q);
  • dotprod_crcf _q:

  • returns standard error code

print()

Print dotprod object internals to standard output

int = dotprod_rrrf_print(dotprod_rrrf _q);
  • dotprod_rrrf _q:

  • returns standard error code

int = dotprod_cccf_print(dotprod_cccf _q);
  • dotprod_cccf _q:

  • returns standard error code

int = dotprod_crcf_print(dotprod_crcf _q);
  • dotprod_crcf _q:

  • returns standard error code

execute()

Execute dot product on an input array

int = dotprod_rrrf_execute(dotprod_rrrf _q, float * _x, float * _y);
  • dotprod_rrrf _q: dotprod object

  • float * _x: input array

  • float * _y: output sample pointer

  • returns standard error code

int = dotprod_cccf_execute(dotprod_cccf _q, float complex * _x, float complex * _y);
  • dotprod_cccf _q: dotprod object

  • float complex * _x: input array

  • float complex * _y: output sample pointer

  • returns standard error code

int = dotprod_crcf_execute(dotprod_crcf _q, float complex * _x, float complex * _y);
  • dotprod_crcf _q: dotprod object

  • float complex * _x: input array

  • float complex * _y: output sample pointer

  • returns standard error code