Skip to content

propensive/mosquito

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

96 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GitHub Workflow

Mosquito

Typesafe vector algebra

Euclidean vectors, in contrast to scalars and arbitrary collections, represent values in a fixed multiple number of dimensions. Mosquito provides a representation of vectors, Euclidean, whose generic type encapsulates both its element type and size. In some sense, a Euclidean can be considered a hybrid of a Tuple (whose size in known) and a collection (whose elements are homogeneous). Mosquito supports the use case of generic programming with Euclidean vectors, but facilitates linear algebra operations, including working with matrices and scalar and vector products.

Features

  • representation of Euclidean vectors and matrices
  • provides many common linear algebraic operations
  • vectors and matrices are generically-typed
  • linear algebraic operations abstract over arithmetic operations
  • results are dependently-typed on inputs

Availability

Getting Started

All terms and types are defined in the mosquito package, so it's sufficient to import,

import mosquito.*

Constructing vectors

A Euclidean vector, primarily to distinguish it from Scala's Vector collection type, is called Euclidean. It represents an ordered, generic collection of elements whose length is known, and furthermore encoded in its type. So a three-dimensional vector of Double values would have the type, Euclidean[Double, 3].

But since both the element type and the size of a Euclidean vector can be inferred from its values, we can create such a vector with just,

val vector = Euclidean(2.7, -0.3, 0.8)

and it's type will be known to be Euclidean[Double, 3].

Constructing matrices

A matrix has the type Matrix, and like a Euclidean, contains homogeneous elements, and encodes its size in its type. However, a Matrix's size is determined by a number of rows and a number of columns. So a 3×4 matrix of Ints would have the type, Matrix[Int, 3, 4]. Conventionally for matrices, the number of rows is given before the number of columns. This is in contrast to the more general convention that a width precedes height. So we avoid the terminology of width and height when describing a matrix, and talk instead of rows and columns.

When constructing a new Matrix, its rows and columns should be specified as type parameters (since they cannot be inferred at present), and organised into equal-length tuples for each row of the matrix. The number and length of the tuples must, of course, match the rows and columns specified during construction, and a mismatch will result in a compile error. Here is an example of constructing a 2×3 Int matrix:

val matrix = Matrix[2, 3](
  (1, -2, 4),
  (3, 8, -1)
)

Multiplication operations

Vectors may be multiplied in several different ways:

  • multiplication of a vector by a scalar, yielding a new vector
  • a dot product between two equal-dimension vectors
  • a cross product between two 3- or 7-dimensional vectors
  • matrix multiplication
  • left- and right-multiplication of a vector by a matrix

Scalar multiplication

A vector may be multiplied by a scalar intuitively with the *. The result will be a new vector, of the same dimension, and elements of the type resulting from multiplying the original element types by the scalar value.

Often, as for Doubles, this would be the same type: the product of a Euclidean[Double, 3] and a Double will be another Euclidean[Double, 3]. But this will not always be the case.

If using Quantitative with Mosquito, a vector of values in metres per second, multiplied by a time value, would yield a vector distance. Specifically, a Euclidean[Quantity[Metres[1] & Seconds[-1]]] multiplied by a Quantity[Seconds[1]] would produce a Euclidean[Quantity[Metres[1]]].

Dot Products

The extension method dot is available for combining two Euclidean instances of equal dimension, and whose scalar elements have a * operation defined, whose result type has a + operation that returns the same type. Although slightly complex, these criteria correspond closely to the low-level operations needed to carry out a dot product.

The result type of a dot product will therefore be determined by the element types of each vector. Using another Quantitative example, the dot product of two vectors of distance Quantity values in metres would be a single scalar value in square metres.

Cross Products

The cross product is only defined for vectors of dimension 3 or 7. Currently, Mosquito only provides an implementation for 3-dimensional Euclidean values, but cross products between 7-dimensional vectors will be provided at a later date.

Matrix multiplication

A pair of matrices may be multiplied with the * operator, if they share the same inner dimension, that is, the number of columns of the left matrix is the same as the number of rows of the right matrix. The result will be a new square matrix, whose elements' type will be determined by the result of * and + operations on their elements.

Multiplication of a vector by a matrix

Left- and right-multiplication by a matrix is not yet implemented, but is a logical and simple inclusion, since the operation is equivalent to matrix multiplication if the vector is interpreted as a single-row or single-column matrix.

Github issue #3 is tracking the implementation of multiplication of a vector by a matrix.

Other operations

Vectors and matrices of equal dimension may also be added and subtracted. Usually, that will result in a vector or matrix of the same element type, but in some cases, result types may be different. Quantitative would, for example, produce a value in metres per second when adding units in feet per second and metres per minute.

Status

Mosquito is classified as fledgling. For reference, Soundness projects are categorized into one of the following five stability levels:

  • embryonic: for experimental or demonstrative purposes only, without any guarantees of longevity
  • fledgling: of proven utility, seeking contributions, but liable to significant redesigns
  • maturescent: major design decisions broady settled, seeking probatory adoption and refinement
  • dependable: production-ready, subject to controlled ongoing maintenance and enhancement; tagged as version 1.0.0 or later
  • adamantine: proven, reliable and production-ready, with no further breaking changes ever anticipated

Projects at any stability level, even embryonic projects, can still be used, as long as caution is taken to avoid a mismatch between the project's stability level and the required stability and maintainability of your own project.

Mosquito is designed to be small. Its entire source code currently consists of 247 lines of code.

Building

Mosquito will ultimately be built by Fury, when it is published. In the meantime, two possibilities are offered, however they are acknowledged to be fragile, inadequately tested, and unsuitable for anything more than experimentation. They are provided only for the necessity of providing some answer to the question, "how can I try Mosquito?".

  1. Copy the sources into your own project

    Read the fury file in the repository root to understand Mosquito's build structure, dependencies and source location; the file format should be short and quite intuitive. Copy the sources into a source directory in your own project, then repeat (recursively) for each of the dependencies.

    The sources are compiled against the latest nightly release of Scala 3. There should be no problem to compile the project together with all of its dependencies in a single compilation.

  2. Build with Wrath

    Wrath is a bootstrapping script for building Mosquito and other projects in the absence of a fully-featured build tool. It is designed to read the fury file in the project directory, and produce a collection of JAR files which can be added to a classpath, by compiling the project and all of its dependencies, including the Scala compiler itself.

    Download the latest version of wrath, make it executable, and add it to your path, for example by copying it to /usr/local/bin/.

    Clone this repository inside an empty directory, so that the build can safely make clones of repositories it depends on as peers of mosquito. Run wrath -F in the repository root. This will download and compile the latest version of Scala, as well as all of Mosquito's dependencies.

    If the build was successful, the compiled JAR files can be found in the .wrath/dist directory.

Contributing

Contributors to Mosquito are welcome and encouraged. New contributors may like to look for issues marked beginner.

We suggest that all contributors read the Contributing Guide to make the process of contributing to Mosquito easier.

Please do not contact project maintainers privately with questions unless there is a good reason to keep them private. While it can be tempting to repsond to such questions, private answers cannot be shared with a wider audience, and it can result in duplication of effort.

Author

Mosquito was designed and developed by Jon Pretty, and commercial support and training on all aspects of Scala 3 is available from Propensive OÜ.

Name

A mosquito is a typical example of a vector: an animal that transmits a pathogen or disease.

In general, Soundness project names are always chosen with some rationale, however it is usually frivolous. Each name is chosen for more for its uniqueness and intrigue than its concision or catchiness, and there is no bias towards names with positive or "nice" meanings—since many of the libraries perform some quite unpleasant tasks.

Names should be English words, though many are obscure or archaic, and it should be noted how willingly English adopts foreign words. Names are generally of Greek or Latin origin, and have often arrived in English via a romance language.

Logo

The logo represents the x, y and z axes of a vector space.

License

Mosquito is copyright © 2024 Jon Pretty & Propensive OÜ, and is made available under the Apache 2.0 License.