Skip to content

Commit

Permalink
Merge pull request #127 from mipt-npm/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
altavir authored Aug 11, 2020
2 parents b9f3ddd + c4a2489 commit eed1663
Show file tree
Hide file tree
Showing 149 changed files with 5,332 additions and 1,460 deletions.
32 changes: 32 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# KMath

## [Unreleased]

### Added
- Functional Expressions API
- Mathematical Syntax Tree, its interpreter and API
- String to MST parser (https://github.com/mipt-npm/kmath/pull/120)
- MST to JVM bytecode translator (https://github.com/mipt-npm/kmath/pull/94)
- FloatBuffer (specialized MutableBuffer over FloatArray)
- FlaggedBuffer to associate primitive numbers buffer with flags (to mark values infinite or missing, etc.)
- Specialized builder functions for all primitive buffers like `IntBuffer(25) { it + 1 }` (https://github.com/mipt-npm/kmath/pull/125)
- Interface `NumericAlgebra` where `number` operation is available to convert numbers to algebraic elements
- Inverse trigonometric functions support in ExtendedField (`asin`, `acos`, `atan`) (https://github.com/mipt-npm/kmath/pull/114)
- New space extensions: `average` and `averageWith`
- Local coding conventions
- Geometric Domains API in `kmath-core`
- Blocking chains in `kmath-coroutines`

### Changed
- BigInteger and BigDecimal algebra: JBigDecimalField has companion object with default math context; minor optimizations
- `power(T, Int)` extension function has preconditions and supports `Field<T>`
- Memory objects have more preconditions (overflow checking)
- `tg` function is renamed to `tan` (https://github.com/mipt-npm/kmath/pull/114)
- Gradle version: 6.3 -> 6.5.1
- Moved probability distributions to commons-rng and to `kmath-prob`.

### Fixed
- Missing copy method in Memory implementation on JS (https://github.com/mipt-npm/kmath/pull/106)
- D3.dim value in `kmath-dimensions`
- Multiplication in integer rings in `kmath-core` (https://github.com/mipt-npm/kmath/pull/101)
- Commons RNG compatibility (https://github.com/mipt-npm/kmath/issues/93)
5 changes: 3 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
plugins {
id("scientifik.publish") version "0.4.2" apply false
id("scientifik.publish") apply false
}

val kmathVersion by extra("0.1.4-dev-4")
val kmathVersion by extra("0.1.4-dev-8")

val bintrayRepo by extra("scientifik")
val githubProject by extra("kmath")
Expand All @@ -11,6 +11,7 @@ allprojects {
repositories {
jcenter()
maven("https://dl.bintray.com/kotlin/kotlinx")
maven("https://dl.bintray.com/hotkeytlt/maven")
}

group = "scientifik"
Expand Down
124 changes: 69 additions & 55 deletions doc/algebra.md
Original file line number Diff line number Diff line change
@@ -1,110 +1,124 @@
# Algebra and algebra elements
# Algebraic Structures and Algebraic Elements

The mathematical operations in `kmath` are generally separated from mathematical objects.
This means that in order to perform an operation, say `+`, one needs two objects of a type `T` and
and algebra context which defines appropriate operation, say `Space<T>`. Next one needs to run actual operation
in the context:
The mathematical operations in KMath are generally separated from mathematical objects. This means that to perform an
operation, say `+`, one needs two objects of a type `T` and an algebra context, which draws appropriate operation up,
say `Space<T>`. Next one needs to run the actual operation in the context:

```kotlin
val a: T
val b: T
val space: Space<T>
import scientifik.kmath.operations.*

val c = space.run{a + b}
val a: T = ...
val b: T = ...
val space: Space<T> = ...

val c = space { a + b }
```

From the first glance, this distinction seems to be a needless complication, but in fact one needs
to remember that in mathematics, one could define different operations on the same objects. For example,
one could use different types of geometry for vectors.
At first glance, this distinction seems to be a needless complication, but in fact one needs to remember that in
mathematics, one could draw up different operations on same objects. For example, one could use different types of
geometry for vectors.

## Algebra hierarchy
## Algebraic Structures

Mathematical contexts have the following hierarchy:

**Space** <- **Ring** <- **Field**
**Algebra****Space****Ring****Field**

These interfaces follow real algebraic structures:

All classes follow abstract mathematical constructs.
[Space](http://mathworld.wolfram.com/Space.html) defines `zero` element, addition operation and multiplication by constant,
[Ring](http://mathworld.wolfram.com/Ring.html) adds multiplication and unit `one` element,
[Field](http://mathworld.wolfram.com/Field.html) adds division operation.
- [Space](https://mathworld.wolfram.com/VectorSpace.html) defines addition, its neutral element (i.e. 0) and scalar
multiplication;
- [Ring](http://mathworld.wolfram.com/Ring.html) adds multiplication and its neutral element (i.e. 1);
- [Field](http://mathworld.wolfram.com/Field.html) adds division operation.

Typical case of `Field` is the `RealField` which works on doubles. And typical case of `Space` is a `VectorSpace`.
A typical implementation of `Field<T>` is the `RealField` which works on doubles, and `VectorSpace` for `Space<T>`.

In some cases algebra context could hold additional operation like `exp` or `sin`, in this case it inherits appropriate
interface. Also a context could have an operation which produces an element outside of its context. For example
`Matrix` `dot` operation produces a matrix with new dimensions which can be incompatible with initial matrix in
terms of linear operations.
In some cases algebra context can hold additional operations like `exp` or `sin`, and then it inherits appropriate
interface. Also, contexts may have operations, which produce elements outside of the context. For example, `Matrix.dot`
operation produces a matrix with new dimensions, which can be incompatible with initial matrix in terms of linear
operations.

## Algebra element
## Algebraic Element

In order to achieve more familiar behavior (where you apply operations directly to mathematical objects), without involving contexts
`kmath` introduces special type objects called `MathElement`. A `MathElement` is basically some object coupled to
To achieve more familiar behavior (where you apply operations directly to mathematical objects), without involving
contexts KMath submits special type objects called `MathElement`. A `MathElement` is basically some object coupled to
a mathematical context. For example `Complex` is the pair of real numbers representing real and imaginary parts,
but it also holds reference to the `ComplexField` singleton which allows to perform direct operations on `Complex`
but it also holds reference to the `ComplexField` singleton, which allows performing direct operations on `Complex`
numbers without explicit involving the context like:

```kotlin
val c1 = Complex(1.0, 1.0)
val c2 = Complex(1.0, -1.0)
val c3 = c1 + c2 + 3.0.toComplex()
//or with field notation:
val c4 = ComplexField.run{c1 + i - 2.0}
import scientifik.kmath.operations.*

// Using elements
val c1 = Complex(1.0, 1.0)
val c2 = Complex(1.0, -1.0)
val c3 = c1 + c2 + 3.0.toComplex()

// Using context
val c4 = ComplexField { c1 + i - 2.0 }
```

Both notations have their pros and cons.

The hierarchy for algebra elements follows the hierarchy for the corresponding algebra.
The hierarchy for algebraic elements follows the hierarchy for the corresponding algebraic structures.

**MathElement** <- **SpaceElement** <- **RingElement** <- **FieldElement**
**MathElement** **SpaceElement** **RingElement** **FieldElement**

**MathElement** is the generic common ancestor of the class with context.
`MathElement<C>` is the generic common ancestor of the class with context.

One important distinction between algebra elements and algebra contexts is that algebra element has three type parameters:
One major distinction between algebraic elements and algebraic contexts is that elements have three type
parameters:

1. The type of elements, field operates on.
2. The self-type of the element returned from operation (must be algebra element).
1. The type of elements, the field operates on.
2. The self-type of the element returned from operation (which has to be an algebraic element).
3. The type of the algebra over first type-parameter.

The middle type is needed in case algebra members do not store context. For example, it is not possible to add
a context to regular `Double`. The element performs automatic conversions from context types and back.
One should used context operations in all important places. The performance of element operations is not guaranteed.
The middle type is needed for of algebra members do not store context. For example, it is impossible to add a context
to regular `Double`. The element performs automatic conversions from context types and back. One should use context
operations in all performance-critical places. The performance of element operations is not guaranteed.

## Spaces and fields
## Spaces and Fields

An obvious first choice of mathematical objects to implement in a context-oriented style are algebraic elements like spaces,
rings and fields. Those are located in the `scientifik.kmath.operations.Algebra.kt` file. Alongside common contexts, the file includes definitions for algebra elements like `FieldElement`. A `FieldElement` object
stores a reference to the `Field` which contains additive and multiplicative operations, meaning
it has one fixed context attached and does not require explicit external context. So those `MathElements` can be operated without context:
KMath submits both contexts and elements for builtin algebraic structures:

```kotlin
import scientifik.kmath.operations.*

val c1 = Complex(1.0, 2.0)
val c2 = ComplexField.i

val c3 = c1 + c2
// or
val c3 = ComplexField { c1 + c2 }
```

`ComplexField` also features special operations to mix complex and real numbers, for example:
Also, `ComplexField` features special operations to mix complex and real numbers, for example:

```kotlin
import scientifik.kmath.operations.*

val c1 = Complex(1.0, 2.0)
val c2 = ComplexField.run{ c1 - 1.0} // Returns: [re:0.0, im: 2.0]
val c3 = ComplexField.run{ c1 - i*2.0}
val c2 = ComplexField { c1 - 1.0 } // Returns: Complex(re=0.0, im=2.0)
val c3 = ComplexField { c1 - i * 2.0 }
```

**Note**: In theory it is possible to add behaviors directly to the context, but currently kotlin syntax does not support
that. Watch [KT-10468](https://youtrack.jetbrains.com/issue/KT-10468) and [KEEP-176](https://github.com/Kotlin/KEEP/pull/176) for updates.
**Note**: In theory it is possible to add behaviors directly to the context, but as for now Kotlin does not support
that. Watch [KT-10468](https://youtrack.jetbrains.com/issue/KT-10468) and
[KEEP-176](https://github.com/Kotlin/KEEP/pull/176) for updates.

## Nested fields

Contexts allow one to build more complex structures. For example, it is possible to create a `Matrix` from complex elements like so:
Contexts allow one to build more complex structures. For example, it is possible to create a `Matrix` from complex
elements like so:

```kotlin
val element = NDElement.complex(shape = intArrayOf(2,2)){ index: IntArray ->
val element = NDElement.complex(shape = intArrayOf(2, 2)) { index: IntArray ->
Complex(index[0].toDouble() - index[1].toDouble(), index[0].toDouble() + index[1].toDouble())
}
```

The `element` in this example is a member of the `Field` of 2-d structures, each element of which is a member of its own
`ComplexField`. The important thing is one does not need to create a special n-d class to hold complex
The `element` in this example is a member of the `Field` of 2D structures, each element of which is a member of its own
`ComplexField`. It is important one does not need to create a special n-d class to hold complex
numbers and implement operations on it, one just needs to provide a field for its elements.

**Note**: Fields themselves do not solve the problem of JVM boxing, but it is possible to solve with special contexts like
Expand Down
6 changes: 4 additions & 2 deletions doc/buffers.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Buffers

Buffer is one of main building blocks of kmath. It is a basic interface allowing random-access read and write (with `MutableBuffer`).
There are different types of buffers:

* Primitive buffers wrapping like `DoubleBuffer` which are wrapping primitive arrays.
* Primitive buffers wrapping like `RealBuffer` which are wrapping primitive arrays.
* Boxing `ListBuffer` wrapping a list
* Functionally defined `VirtualBuffer` which does not hold a state itself, but provides a function to calculate value
* `MemoryBuffer` allows direct allocation of objects in continuous memory block.
Expand All @@ -12,4 +13,5 @@ Some kmath features require a `BufferFactory` class to operate properly. A gener
buffer for given reified type (for types with custom memory buffer it still better to use their own `MemoryBuffer.create()` factory).

## Buffer performance
One should avoid using default boxing buffer wherever it is possible. Try to use primitive buffers or memory buffers instead

One should avoid using default boxing buffer wherever it is possible. Try to use primitive buffers or memory buffers instead
34 changes: 34 additions & 0 deletions doc/codestyle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Coding Conventions

KMath code follows general [Kotlin conventions](https://kotlinlang.org/docs/reference/coding-conventions.html), but
with a number of small changes and clarifications.

## Utility Class Naming

Filename should coincide with a name of one of the classes contained in the file or start with small letter and
describe its contents.

The code convention [here](https://kotlinlang.org/docs/reference/coding-conventions.html#source-file-names) says that
file names should start with a capital letter even if file does not contain classes. Yet starting utility classes and
aggregators with a small letter seems to be a good way to visually separate those files.

This convention could be changed in future in a non-breaking way.

## Private Variable Naming

Private variables' names may start with underscore `_` for of the private mutable variable is shadowed by the public
read-only value with the same meaning.

This rule does not permit underscores in names, but it is sometimes useful to "underscore" the fact that public and
private versions draw up the same entity. It is allowed only for private variables.

This convention could be changed in future in a non-breaking way.

## Functions and Properties One-liners

Use one-liners when they occupy single code window line both for functions and properties with getters like
`val b: String get() = "fff"`. The same should be performed with multiline expressions when they could be
cleanly separated.

There is no universal consensus whenever use `fun a() = ...` or `fun a() { return ... }`. Yet from reader outlook
one-lines seem to better show that the property or function is easily calculated.
2 changes: 1 addition & 1 deletion doc/linear.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## Basic linear algebra layout

Kmath support for linear algebra organized in a context-oriented way. Meaning that operations are in most cases declared
KMath support for linear algebra organized in a context-oriented way. Meaning that operations are in most cases declared
in context classes, and are not the members of classes that store data. This allows more flexible approach to maintain multiple
back-ends. The new operations added as extensions to contexts instead of being member functions of data structures.

Expand Down
2 changes: 1 addition & 1 deletion doc/nd-structure.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Nd-structure generation and operations
# ND-structure generation and operations

**TODO**

Expand Down
12 changes: 7 additions & 5 deletions examples/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
java
kotlin("jvm")
kotlin("plugin.allopen") version "1.3.71"
id("kotlinx.benchmark") version "0.2.0-dev-7"
kotlin("plugin.allopen") version "1.3.72"
id("kotlinx.benchmark") version "0.2.0-dev-8"
}

configure<AllOpenExtension> {
Expand All @@ -24,16 +24,18 @@ sourceSets {
}

dependencies {
implementation(project(":kmath-ast"))
implementation(project(":kmath-core"))
implementation(project(":kmath-coroutines"))
implementation(project(":kmath-commons"))
implementation(project(":kmath-prob"))
implementation(project(":kmath-koma"))
implementation(project(":kmath-viktor"))
implementation(project(":kmath-dimensions"))
implementation("com.kyonifer:koma-core-ejml:0.12")
implementation("org.jetbrains.kotlinx:kotlinx-io-jvm:0.2.0-npm-dev-6")
implementation("org.jetbrains.kotlinx:kotlinx.benchmark.runtime:0.2.0-dev-7")
"benchmarksCompile"(sourceSets.main.get().compileClasspath)
implementation("org.jetbrains.kotlinx:kotlinx.benchmark.runtime:0.2.0-dev-8")
"benchmarksCompile"(sourceSets.main.get().output + sourceSets.main.get().compileClasspath) //sourceSets.main.output + sourceSets.main.runtimeClasspath
}

// Configure benchmark
Expand All @@ -57,6 +59,6 @@ benchmark {

tasks.withType<KotlinCompile> {
kotlinOptions {
jvmTarget = Scientifik.JVM_VERSION
jvmTarget = Scientifik.JVM_TARGET.toString()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import scientifik.kmath.operations.complex
class BufferBenchmark {

@Benchmark
fun genericDoubleBufferReadWrite() {
val buffer = DoubleBuffer(size){it.toDouble()}
fun genericRealBufferReadWrite() {
val buffer = RealBuffer(size){it.toDouble()}

(0 until size).forEach {
buffer[it]
Expand Down
Loading

0 comments on commit eed1663

Please sign in to comment.