packages feed

linear-massiv-0.1.0.0: src/Numeric/LinearAlgebra/Massiv/BLAS/Level1.hs

{-# LANGUAGE AllowAmbiguousTypes #-}

-- |
-- Module      : Numeric.LinearAlgebra.Massiv.BLAS.Level1
-- Copyright   : (c) Nadia Chambers 2026
-- License     : BSD-3-Clause
-- Maintainer  : nadia.chambers@iohk.io
-- Stability   : experimental
--
-- = BLAS Level 1: Vector–Vector Operations
--
-- This module provides type-safe, dimension-indexed wrappers around the
-- standard BLAS Level 1 kernels for vector–vector operations.  Every
-- function carries the vector length @n@ as a phantom type-level natural,
-- so dimension mismatches are caught at compile time rather than at run
-- time.
--
-- The algorithms implemented here correspond to the elementary building
-- blocks described in:
--
-- * Golub, G. H. & Van Loan, C. F. (2013). /Matrix Computations/,
--   4th edition (GVL4). Johns Hopkins University Press.
--   __Chapter 1, Section 1.1__, pp. 4–8.
--
-- Specifically:
--
-- * __Algorithm 1.1.1__ (p. 4) — Inner product (dot product).
--   Given vectors \(x, y \in \mathbb{R}^{n}\), compute
--   \(c = x^{T} y = \sum_{i=1}^{n} x_i y_i\).
--
-- * __Algorithm 1.1.2 (Saxpy)__ (p. 4) — Scalar \(\alpha\) times
--   vector \(x\) plus vector \(y\):
--   \(y \leftarrow \alpha x + y\).
--   This is the fundamental vector-update operation upon which the
--   higher-level BLAS Level 2 and Level 3 routines are built.
--
-- Additionally the module exposes the common vector norms
-- (\(\lVert \cdot \rVert_1\) and \(\lVert \cdot \rVert_2\)) and
-- scalar–vector scaling, which together form the complete Level 1
-- BLAS surface.
--
-- == Complexity
--
-- All operations in this module are \(O(n)\) in the vector length.
module Numeric.LinearAlgebra.Massiv.BLAS.Level1
  ( -- * Dot product (Algorithm 1.1.1, GVL4 p. 4)
    dot
  , dotP
    -- * Scalar–vector operations (Algorithm 1.1.2, GVL4 p. 4)
  , scal
  , axpy
    -- * Vector norms (GVL4 Section 1.1, pp. 4–8)
  , nrm2
  , asum
  ) where

import qualified Data.Massiv.Array as M
import Data.Massiv.Array (unwrapByteArray, unwrapByteArrayOffset)
import GHC.TypeNats (KnownNat)

import Numeric.LinearAlgebra.Massiv.Types
import Numeric.LinearAlgebra.Massiv.Internal
import Numeric.LinearAlgebra.Massiv.Internal.Kernel (rawDot)

-- | Inner (dot) product of two vectors.
--
-- __GVL4 Reference:__ Algorithm 1.1.1, p. 4.
--
-- Given \(x, y \in \mathbb{R}^{n}\), computes the scalar
--
-- \[
--   c \;=\; x^{T} y \;=\; \sum_{i=1}^{n} x_i \, y_i
-- \]
--
-- ==== Type-safety guarantees
--
-- Both vectors carry the same compile-time dimension @n@, so a
-- length mismatch is a type error.
--
-- ==== Complexity
--
-- \(O(n)\) — exactly \(n\) multiplications and \(n\) additions
-- (or \(n - 1\) additions, depending on the fold seed).
dot :: (KnownNat n, M.Manifest r e, Num e)
    => Vector n r e -> Vector n r e -> e
dot (MkVector x) (MkVector y) =
  M.foldlS (+) 0 $ M.zipWith (*) x y
{-# NOINLINE [1] dot #-}

-- | Specialised raw-array dot product for P Double.
dotP :: forall n. KnownNat n => Vector n M.P Double -> Vector n M.P Double -> Double
dotP (MkVector x) (MkVector y) =
  rawDot (unwrapByteArray x) (unwrapByteArrayOffset x)
         (unwrapByteArray y) (unwrapByteArrayOffset y)
         (dimVal @n)
{-# NOINLINE dotP #-}

{-# RULES "dot/P/Double" forall (x :: Vector n M.P Double)
                                (y :: Vector n M.P Double).
    dot x y = dotP x y #-}

-- | Scale every element of a vector by a scalar.
--
-- __GVL4 Reference:__ Section 1.1, pp. 4–8 (scalar–vector operations).
--
-- Computes
--
-- \[
--   x \;\leftarrow\; \alpha \, x
-- \]
--
-- i.e., each component \(x_i\) is replaced by \(\alpha \, x_i\).
--
-- ==== Type-safety guarantees
--
-- The output vector retains the same compile-time dimension @n@ as the
-- input.
--
-- ==== Complexity
--
-- \(O(n)\) — one multiplication per element.
scal :: (KnownNat n, M.Manifest r e, Num e)
     => e -> Vector n r e -> Vector n r e
scal alpha (MkVector x) = MkVector $ M.compute $ M.map (* alpha) x

-- | Saxpy (Scalar Alpha X Plus Y) — the fundamental vector-update operation.
--
-- __GVL4 Reference:__ Algorithm 1.1.1 (Saxpy), p. 4.
--
-- Given a scalar \(\alpha\) and vectors \(x, y \in \mathbb{R}^{n}\),
-- computes
--
-- \[
--   y \;\leftarrow\; \alpha \, x + y
-- \]
--
-- The Saxpy kernel is the innermost building block of the BLAS hierarchy.
-- Every Gaxpy (Level 2) and matrix–matrix (Level 3) operation can be
-- expressed as a sequence of Saxpy calls (GVL4, Section 1.1, p. 4).
--
-- ==== Type-safety guarantees
--
-- Both input vectors and the result share the same compile-time
-- dimension @n@.
--
-- ==== Complexity
--
-- \(O(n)\) — one fused multiply-add per element.
axpy :: (KnownNat n, M.Manifest r e, Num e)
     => e -> Vector n r e -> Vector n r e -> Vector n r e
axpy alpha (MkVector x) (MkVector y) =
  MkVector $ M.compute $ M.zipWith (\xi yi -> alpha * xi + yi) x y

-- | Euclidean (2-) norm of a vector.
--
-- __GVL4 Reference:__ Section 1.1, pp. 4–8 (vector norms).
--
-- Computes
--
-- \[
--   \lVert x \rVert_2 \;=\; \sqrt{\sum_{i=1}^{n} x_i^{2}}
-- \]
--
-- ==== Type-safety guarantees
--
-- The input vector carries its length @n@ at the type level; the result
-- is a scalar of the same element type.
--
-- ==== Complexity
--
-- \(O(n)\) — one multiply-accumulate per element, plus a single square
-- root.
--
-- /Note:/ This implementation does not perform the scaling trick
-- described in GVL4 (p. 5) to avoid overflow\/underflow for
-- extreme element magnitudes.  For production use on
-- floating-point data with very large or very small entries,
-- consider a two-pass scaled variant.
nrm2 :: (KnownNat n, M.Manifest r e, Floating e)
     => Vector n r e -> e
nrm2 (MkVector x) = sqrt $ M.foldlS (\acc xi -> acc + xi * xi) 0 x

-- | Sum of absolute values — the 1-norm (Manhattan norm) of a vector.
--
-- __GVL4 Reference:__ Section 1.1, pp. 4–8 (vector norms).
--
-- Computes
--
-- \[
--   \lVert x \rVert_1 \;=\; \sum_{i=1}^{n} \lvert x_i \rvert
-- \]
--
-- ==== Type-safety guarantees
--
-- The input vector carries its length @n@ at the type level; the result
-- is a scalar of the same element type.
--
-- ==== Complexity
--
-- \(O(n)\) — one absolute value and one addition per element.
asum :: (KnownNat n, M.Manifest r e, Num e, Ord e)
     => Vector n r e -> e
asum (MkVector x) = M.foldlS (\acc xi -> acc + abs xi) 0 x