Improved trigonometry operations #99
22
docs/codestyle.md
Normal file
22
docs/codestyle.md
Normal file
@ -0,0 +1,22 @@
|
||||
# Local coding conventions
|
||||
|
||||
Kmath and other `scientifik` projects use general [kotlin code conventions](https://kotlinlang.org/docs/reference/coding-conventions.html), but with a number of small changes and clarifications.
|
||||
|
||||
## Utility class names
|
||||
File name 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 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 clearly visually separate those files.
|
||||
|
||||
This convention could be changed in future in a non-breaking way.
|
||||
|
||||
## Private variable names
|
||||
Private variable names could start with underscore `_` in case the private mutable variable is shadowed by the public read-only value with the same meaning.
|
||||
|
||||
Code convention do not permit underscores in names, but is is sometimes useful to "underscore" the fact that public and private versions define 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 done with multiline expressions when they could be cleanly separated.
|
||||
|
||||
There is not general consensus whenever use `fun a() = {}` or `fun a(){return}`. Yet from reader perspective one-lines seem to better show that the property or function is easily calculated.
|
@ -59,8 +59,10 @@ class DerivativeStructureField(
|
||||
override fun divide(a: DerivativeStructure, b: DerivativeStructure): DerivativeStructure = a.divide(b)
|
||||
|
||||
override fun sin(arg: DerivativeStructure): DerivativeStructure = arg.sin()
|
||||
|
||||
override fun cos(arg: DerivativeStructure): DerivativeStructure = arg.cos()
|
||||
override fun asin(arg: DerivativeStructure): DerivativeStructure = arg.asin()
|
||||
override fun acos(arg: DerivativeStructure): DerivativeStructure = arg.acos()
|
||||
override fun atan(arg: DerivativeStructure): DerivativeStructure = arg.atan()
|
||||
|
||||
override fun power(arg: DerivativeStructure, pow: Number): DerivativeStructure = when (pow) {
|
||||
is Double -> arg.pow(pow)
|
||||
@ -136,6 +138,3 @@ object DiffExpressionAlgebra : ExpressionAlgebra<Double, DiffExpression>, Field<
|
||||
override fun divide(a: DiffExpression, b: DiffExpression) =
|
||||
DiffExpression { a.function(this) / b.function(this) }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -8,6 +8,8 @@ import scientifik.memory.MemorySpec
|
||||
import scientifik.memory.MemoryWriter
|
||||
import kotlin.math.*
|
||||
|
||||
private val PI_DIV_2 = Complex(PI / 2, 0)
|
||||
|
||||
/**
|
||||
* A field for complex numbers
|
||||
*/
|
||||
@ -30,9 +32,11 @@ object ComplexField : ExtendedField<Complex> {
|
||||
return Complex((a.re * b.re + a.im * b.im) / norm, (a.re * b.im - a.im * b.re) / norm)
|
||||
}
|
||||
|
||||
override fun sin(arg: Complex): Complex = i / 2 * (exp(-i * arg) - exp(i * arg))
|
||||
|
||||
override fun sin(arg: Complex): Complex = i * (exp(-i * arg) - exp(i * arg)) / 2
|
||||
override fun cos(arg: Complex): Complex = (exp(-i * arg) + exp(i * arg)) / 2
|
||||
override fun asin(arg: Complex): Complex = -i * ln(sqrt(one - arg pow 2) + i * arg)
|
||||
override fun acos(arg: Complex): Complex = PI_DIV_2 + i * ln(sqrt(one - arg pow 2) + i * arg)
|
||||
override fun atan(arg: Complex): Complex = i * (ln(one - i * arg) - ln(one + i * arg)) / 2
|
||||
|
||||
override fun power(arg: Complex, pow: Number): Complex =
|
||||
arg.r.pow(pow.toDouble()) * (cos(pow.toDouble() * arg.theta) + i * sin(pow.toDouble() * arg.theta))
|
||||
|
@ -8,7 +8,7 @@ import kotlin.math.pow as kpow
|
||||
*/
|
||||
interface ExtendedFieldOperations<T> :
|
||||
FieldOperations<T>,
|
||||
TrigonometricOperations<T>,
|
||||
InverseTrigonometricOperations<T>,
|
||||
PowerOperations<T>,
|
||||
ExponentialOperations<T> {
|
||||
|
||||
@ -20,18 +20,15 @@ interface ExtendedFieldOperations<T> :
|
||||
PowerOperations.SQRT_OPERATION -> sqrt(arg)
|
||||
ExponentialOperations.EXP_OPERATION -> exp(arg)
|
||||
ExponentialOperations.LN_OPERATION -> ln(arg)
|
||||
else -> super.unaryOperation(operation, arg)
|
||||
else -> super<InverseTrigonometricOperations>.unaryOperation(operation, arg)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface ExtendedField<T> : ExtendedFieldOperations<T>, Field<T> {
|
||||
override fun rightSideNumberOperation(operation: String, left: T, right: Number): T {
|
||||
return when (operation) {
|
||||
PowerOperations.POW_OPERATION -> power(left, right)
|
||||
else -> super.rightSideNumberOperation(operation, left, right)
|
||||
}
|
||||
|
||||
override fun rightSideNumberOperation(operation: String, left: T, right: Number): T = when (operation) {
|
||||
PowerOperations.POW_OPERATION -> power(left, right)
|
||||
else -> super.rightSideNumberOperation(operation, left, right)
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,7 +62,10 @@ object RealField : ExtendedField<Double>, Norm<Double, Double> {
|
||||
|
||||
override inline fun sin(arg: Double) = kotlin.math.sin(arg)
|
||||
override inline fun cos(arg: Double) = kotlin.math.cos(arg)
|
||||
override inline fun tan(arg: Double) = kotlin.math.tan(arg)
|
||||
override inline fun tan(arg: Double): Double = kotlin.math.tan(arg)
|
||||
override inline fun acos(arg: Double): Double = kotlin.math.acos(arg)
|
||||
override inline fun asin(arg: Double): Double = kotlin.math.asin(arg)
|
||||
override inline fun atan(arg: Double): Double = kotlin.math.atan(arg)
|
||||
|
||||
override inline fun power(arg: Double, pow: Number) = arg.kpow(pow.toDouble())
|
||||
|
||||
@ -97,8 +97,10 @@ object FloatField : ExtendedField<Float>, Norm<Float, Float> {
|
||||
|
||||
override inline fun sin(arg: Float) = kotlin.math.sin(arg)
|
||||
override inline fun cos(arg: Float) = kotlin.math.cos(arg)
|
||||
|
||||
override inline fun tan(arg: Float): Float = kotlin.math.tan(arg)
|
||||
override inline fun tan(arg: Float) = kotlin.math.tan(arg)
|
||||
override inline fun acos(arg: Float) = kotlin.math.acos(arg)
|
||||
override inline fun asin(arg: Float) = kotlin.math.asin(arg)
|
||||
override inline fun atan(arg: Float) = kotlin.math.atan(arg)
|
||||
|
||||
override inline fun power(arg: Float, pow: Number) = arg.pow(pow.toFloat())
|
||||
|
||||
|
@ -10,28 +10,43 @@ package scientifik.kmath.operations
|
||||
* It also allows to override behavior for optional operations
|
||||
*
|
||||
*/
|
||||
interface TrigonometricOperations<T> {
|
||||
interface TrigonometricOperations<T> : FieldOperations<T> {
|
||||
fun sin(arg: T): T
|
||||
fun cos(arg: T): T
|
||||
|
||||
fun tan(arg: T): T
|
||||
fun tan(arg: T): T = sin(arg) / cos(arg)
|
||||
|
||||
companion object {
|
||||
const val SIN_OPERATION = "sin"
|
||||
const val COS_OPERATION = "cos"
|
||||
const val TAN_OPERATION = "tan"
|
||||
}
|
||||
}
|
||||
|
||||
interface InverseTrigonometricOperations<T> : TrigonometricOperations<T> {
|
||||
fun asin(arg: T): T
|
||||
fun acos(arg: T): T
|
||||
fun atan(arg: T): T
|
||||
|
||||
companion object {
|
||||
const val ASIN_OPERATION = "asin"
|
||||
const val ACOS_OPERATION = "acos"
|
||||
const val ATAN_OPERATION = "atan"
|
||||
}
|
||||
}
|
||||
|
||||
fun <T : MathElement<out TrigonometricOperations<T>>> sin(arg: T): T = arg.context.sin(arg)
|
||||
fun <T : MathElement<out TrigonometricOperations<T>>> cos(arg: T): T = arg.context.cos(arg)
|
||||
fun <T : MathElement<out TrigonometricOperations<T>>> tan(arg: T): T = arg.context.tan(arg)
|
||||
fun <T : MathElement<out InverseTrigonometricOperations<T>>> asin(arg: T): T = arg.context.asin(arg)
|
||||
fun <T : MathElement<out InverseTrigonometricOperations<T>>> acos(arg: T): T = arg.context.acos(arg)
|
||||
fun <T : MathElement<out InverseTrigonometricOperations<T>>> atan(arg: T): T = arg.context.atan(arg)
|
||||
|
||||
/* Power and roots */
|
||||
|
||||
/**
|
||||
* A context extension to include power operations like square roots, etc
|
||||
*/
|
||||
interface PowerOperations<T> {
|
||||
interface PowerOperations<T> : Algebra<T> {
|
||||
fun power(arg: T, pow: Number): T
|
||||
fun sqrt(arg: T) = power(arg, 0.5)
|
||||
|
||||
@ -49,7 +64,7 @@ fun <T : MathElement<out PowerOperations<T>>> sqr(arg: T): T = arg pow 2.0
|
||||
|
||||
/* Exponential */
|
||||
|
||||
interface ExponentialOperations<T> {
|
||||
interface ExponentialOperations<T>: Algebra<T> {
|
||||
fun exp(arg: T): T
|
||||
fun ln(arg: T): T
|
||||
|
||||
|
@ -79,6 +79,13 @@ class ComplexNDField(override val shape: IntArray) :
|
||||
|
||||
override fun cos(arg: NDBuffer<Complex>) = map(arg) { cos(it) }
|
||||
|
||||
override fun tan(arg: NDBuffer<Complex>): NDBuffer<Complex> = map(arg) { tan(it) }
|
||||
|
||||
override fun asin(arg: NDBuffer<Complex>): NDBuffer<Complex> = map(arg) { asin(it) }
|
||||
|
||||
override fun acos(arg: NDBuffer<Complex>): NDBuffer<Complex> = map(arg) {acos(it)}
|
||||
|
||||
override fun atan(arg: NDBuffer<Complex>): NDBuffer<Complex> = map(arg) {atan(it)}
|
||||
}
|
||||
|
||||
|
||||
|
@ -9,8 +9,10 @@ import kotlin.math.*
|
||||
* A simple field over linear buffers of [Double]
|
||||
*/
|
||||
object RealBufferFieldOperations : ExtendedFieldOperations<Buffer<Double>> {
|
||||
|
||||
override fun add(a: Buffer<Double>, b: Buffer<Double>): DoubleBuffer {
|
||||
require(b.size == a.size) { "The size of the first buffer ${a.size} should be the same as for second one: ${b.size} " }
|
||||
|
||||
return if (a is DoubleBuffer && b is DoubleBuffer) {
|
||||
val aArray = a.array
|
||||
val bArray = b.array
|
||||
@ -22,6 +24,7 @@ object RealBufferFieldOperations : ExtendedFieldOperations<Buffer<Double>> {
|
||||
|
||||
override fun multiply(a: Buffer<Double>, k: Number): DoubleBuffer {
|
||||
val kValue = k.toDouble()
|
||||
|
||||
return if (a is DoubleBuffer) {
|
||||
val aArray = a.array
|
||||
DoubleBuffer(DoubleArray(a.size) { aArray[it] * kValue })
|
||||
@ -32,6 +35,7 @@ object RealBufferFieldOperations : ExtendedFieldOperations<Buffer<Double>> {
|
||||
|
||||
override fun multiply(a: Buffer<Double>, b: Buffer<Double>): DoubleBuffer {
|
||||
require(b.size == a.size) { "The size of the first buffer ${a.size} should be the same as for second one: ${b.size} " }
|
||||
|
||||
return if (a is DoubleBuffer && b is DoubleBuffer) {
|
||||
val aArray = a.array
|
||||
val bArray = b.array
|
||||
@ -43,6 +47,7 @@ object RealBufferFieldOperations : ExtendedFieldOperations<Buffer<Double>> {
|
||||
|
||||
override fun divide(a: Buffer<Double>, b: Buffer<Double>): DoubleBuffer {
|
||||
require(b.size == a.size) { "The size of the first buffer ${a.size} should be the same as for second one: ${b.size} " }
|
||||
|
||||
return if (a is DoubleBuffer && b is DoubleBuffer) {
|
||||
val aArray = a.array
|
||||
val bArray = b.array
|
||||
@ -52,49 +57,67 @@ object RealBufferFieldOperations : ExtendedFieldOperations<Buffer<Double>> {
|
||||
}
|
||||
}
|
||||
|
||||
override fun sin(arg: Buffer<Double>): DoubleBuffer {
|
||||
return if (arg is DoubleBuffer) {
|
||||
val array = arg.array
|
||||
DoubleBuffer(DoubleArray(arg.size) { sin(array[it]) })
|
||||
} else {
|
||||
DoubleBuffer(DoubleArray(arg.size) { sin(arg[it]) })
|
||||
}
|
||||
override fun sin(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
|
||||
val array = arg.array
|
||||
DoubleBuffer(DoubleArray(arg.size) { sin(array[it]) })
|
||||
} else {
|
||||
DoubleBuffer(DoubleArray(arg.size) { sin(arg[it]) })
|
||||
}
|
||||
|
||||
override fun cos(arg: Buffer<Double>): DoubleBuffer {
|
||||
return if (arg is DoubleBuffer) {
|
||||
val array = arg.array
|
||||
DoubleBuffer(DoubleArray(arg.size) { cos(array[it]) })
|
||||
} else {
|
||||
DoubleBuffer(DoubleArray(arg.size) { cos(arg[it]) })
|
||||
}
|
||||
override fun cos(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
|
||||
val array = arg.array
|
||||
DoubleBuffer(DoubleArray(arg.size) { cos(array[it]) })
|
||||
} else {
|
||||
DoubleBuffer(DoubleArray(arg.size) { cos(arg[it]) })
|
||||
}
|
||||
|
||||
override fun power(arg: Buffer<Double>, pow: Number): DoubleBuffer {
|
||||
return if (arg is DoubleBuffer) {
|
||||
val array = arg.array
|
||||
DoubleBuffer(DoubleArray(arg.size) { array[it].pow(pow.toDouble()) })
|
||||
} else {
|
||||
DoubleBuffer(DoubleArray(arg.size) { arg[it].pow(pow.toDouble()) })
|
||||
}
|
||||
override fun tan(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
|
||||
val array = arg.array
|
||||
DoubleBuffer(DoubleArray(arg.size) { tan(array[it]) })
|
||||
} else {
|
||||
DoubleBuffer(DoubleArray(arg.size) { tan(arg[it]) })
|
||||
}
|
||||
|
||||
override fun exp(arg: Buffer<Double>): DoubleBuffer {
|
||||
return if (arg is DoubleBuffer) {
|
||||
val array = arg.array
|
||||
DoubleBuffer(DoubleArray(arg.size) { exp(array[it]) })
|
||||
} else {
|
||||
DoubleBuffer(DoubleArray(arg.size) { exp(arg[it]) })
|
||||
}
|
||||
override fun asin(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
|
||||
val array = arg.array
|
||||
DoubleBuffer(DoubleArray(arg.size) { asin(array[it]) })
|
||||
} else {
|
||||
DoubleBuffer(DoubleArray(arg.size) { asin(arg[it]) })
|
||||
}
|
||||
|
||||
override fun ln(arg: Buffer<Double>): DoubleBuffer {
|
||||
return if (arg is DoubleBuffer) {
|
||||
val array = arg.array
|
||||
DoubleBuffer(DoubleArray(arg.size) { ln(array[it]) })
|
||||
} else {
|
||||
DoubleBuffer(DoubleArray(arg.size) { ln(arg[it]) })
|
||||
}
|
||||
override fun acos(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
|
||||
val array = arg.array
|
||||
DoubleBuffer(DoubleArray(arg.size) { acos(array[it]) })
|
||||
} else {
|
||||
DoubleBuffer(DoubleArray(arg.size) { acos(arg[it]) })
|
||||
}
|
||||
|
||||
override fun atan(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
|
||||
val array = arg.array
|
||||
DoubleBuffer(DoubleArray(arg.size) { atan(array[it]) })
|
||||
} else {
|
||||
DoubleBuffer(DoubleArray(arg.size) { atan(arg[it]) })
|
||||
}
|
||||
|
||||
override fun power(arg: Buffer<Double>, pow: Number): DoubleBuffer = if (arg is DoubleBuffer) {
|
||||
val array = arg.array
|
||||
DoubleBuffer(DoubleArray(arg.size) { array[it].pow(pow.toDouble()) })
|
||||
} else {
|
||||
DoubleBuffer(DoubleArray(arg.size) { arg[it].pow(pow.toDouble()) })
|
||||
}
|
||||
|
||||
override fun exp(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
|
||||
val array = arg.array
|
||||
DoubleBuffer(DoubleArray(arg.size) { exp(array[it]) })
|
||||
} else {
|
||||
DoubleBuffer(DoubleArray(arg.size) { exp(arg[it]) })
|
||||
}
|
||||
|
||||
override fun ln(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
|
||||
val array = arg.array
|
||||
DoubleBuffer(DoubleArray(arg.size) { ln(array[it]) })
|
||||
} else {
|
||||
DoubleBuffer(DoubleArray(arg.size) { ln(arg[it]) })
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,7 +142,6 @@ class RealBufferField(val size: Int) : ExtendedField<Buffer<Double>> {
|
||||
return RealBufferFieldOperations.multiply(a, b)
|
||||
}
|
||||
|
||||
|
||||
override fun divide(a: Buffer<Double>, b: Buffer<Double>): DoubleBuffer {
|
||||
require(a.size == size) { "The buffer size ${a.size} does not match context size $size" }
|
||||
return RealBufferFieldOperations.divide(a, b)
|
||||
@ -135,6 +157,26 @@ class RealBufferField(val size: Int) : ExtendedField<Buffer<Double>> {
|
||||
return RealBufferFieldOperations.cos(arg)
|
||||
}
|
||||
|
||||
override fun tan(arg: Buffer<Double>): DoubleBuffer {
|
||||
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
|
||||
return RealBufferFieldOperations.tan(arg)
|
||||
}
|
||||
|
||||
override fun asin(arg: Buffer<Double>): DoubleBuffer {
|
||||
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
|
||||
return RealBufferFieldOperations.asin(arg)
|
||||
}
|
||||
|
||||
override fun acos(arg: Buffer<Double>): DoubleBuffer {
|
||||
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
|
||||
return RealBufferFieldOperations.acos(arg)
|
||||
}
|
||||
|
||||
override fun atan(arg: Buffer<Double>): DoubleBuffer {
|
||||
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
|
||||
return RealBufferFieldOperations.atan(arg)
|
||||
}
|
||||
|
||||
override fun power(arg: Buffer<Double>, pow: Number): DoubleBuffer {
|
||||
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
|
||||
return RealBufferFieldOperations.power(arg, pow)
|
||||
@ -149,5 +191,4 @@ class RealBufferField(val size: Int) : ExtendedField<Buffer<Double>> {
|
||||
require(arg.size == size) { "The buffer size ${arg.size} does not match context size $size" }
|
||||
return RealBufferFieldOperations.ln(arg)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -74,6 +74,13 @@ class RealNDField(override val shape: IntArray) :
|
||||
|
||||
override fun cos(arg: NDBuffer<Double>) = map(arg) { cos(it) }
|
||||
|
||||
override fun tan(arg: NDBuffer<Double>): NDBuffer<Double> = map(arg) { tan(it) }
|
||||
|
||||
override fun asin(arg: NDBuffer<Double>): NDBuffer<Double> = map(arg) { asin(it) }
|
||||
|
||||
override fun acos(arg: NDBuffer<Double>): NDBuffer<Double> = map(arg) { acos(it) }
|
||||
|
||||
override fun atan(arg: NDBuffer<Double>): NDBuffer<Double> = map(arg) { atan(it) }
|
||||
}
|
||||
|
||||
|
||||
|
@ -11,18 +11,16 @@ class DataViewMemory(val view: DataView) : Memory {
|
||||
override fun view(offset: Int, length: Int): Memory {
|
||||
require(offset >= 0) { "offset shouldn't be negative: $offset" }
|
||||
require(length >= 0) { "length shouldn't be negative: $length" }
|
||||
if (offset + length > size) {
|
||||
|
||||
if (offset + length > size)
|
||||
throw IndexOutOfBoundsException("offset + length > size: $offset + $length > $size")
|
||||
}
|
||||
|
||||
return DataViewMemory(DataView(view.buffer, view.byteOffset + offset, length))
|
||||
}
|
||||
|
||||
override fun copy(): Memory = DataViewMemory(DataView(view.buffer.slice(0)))
|
||||
|
||||
override fun copy(): Memory {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
private val reader = object : MemoryReader {
|
||||
private val reader: MemoryReader = object : MemoryReader {
|
||||
override val memory: Memory get() = this@DataViewMemory
|
||||
|
||||
override fun readDouble(offset: Int): Double = view.getFloat64(offset, false)
|
||||
@ -45,7 +43,7 @@ class DataViewMemory(val view: DataView) : Memory {
|
||||
|
||||
override fun reader(): MemoryReader = reader
|
||||
|
||||
private val writer = object : MemoryWriter {
|
||||
private val writer: MemoryWriter = object : MemoryWriter {
|
||||
override val memory: Memory get() = this@DataViewMemory
|
||||
|
||||
override fun writeDouble(offset: Int, value: Double) {
|
||||
@ -76,7 +74,6 @@ class DataViewMemory(val view: DataView) : Memory {
|
||||
override fun release() {
|
||||
//does nothing on JS
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun writer(): MemoryWriter = writer
|
||||
@ -94,4 +91,4 @@ actual fun Memory.Companion.allocate(length: Int): Memory {
|
||||
actual fun Memory.Companion.wrap(array: ByteArray): Memory {
|
||||
@Suppress("CAST_NEVER_SUCCEEDS") val int8Array = array as Int8Array
|
||||
return DataViewMemory(DataView(int8Array.buffer, int8Array.byteOffset, int8Array.length))
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user