-
Notifications
You must be signed in to change notification settings - Fork 58
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Karatsuba added, 2 bugs are fixed #328
Conversation
Incorrect equals fix test Incorrect overflow handling support
(Slack discussion) |
I recently learned that E.A. Karatsuba has a fast method for evaluating transcendentals, which can be useful for working with certain probability distributions. It might be worth looking into at some point: https://en.wikipedia.org/wiki/FEE_method |
@breandan However, the Karatsuba's algorithm that is subject of the PR is fast multiplication. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The changes in the core are not appropriate at the moment, they should be discussed in a separate issue first. Otherwise looks fine. It would be nice to have your benchmarking results in docs and a separate example in examples.
else -> powWithoutOptimization(exponent) | ||
} | ||
|
||
private fun T.powWithoutOptimization(exponent: ULong): T = when (exponent) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The implementation details should not be present In generic algebra. Probably better to use extensions instead. Like Ring<T>.pow(T, exponent)
. This change needs additional discussion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I completely agree with @altavir.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, it should be placed there (as an extension). Thank you! I'll fix it.
Furthermore, it may be better to create a function in BigInt
that would make it possible to use power for bigints without BigIntField { }
. What do you think, @altavir @CommanderTvis? After inventing multiple receivers that should be replaced though.
@@ -250,6 +250,22 @@ public interface Ring<T> : Group<T>, RingOperations<T> { | |||
* neutral operation for multiplication | |||
*/ | |||
public val one: T | |||
|
|||
public fun T.pow(exponent: ULong): T = when { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are there any real use-cases for Long
power?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean negative exponent usage? If so, no. It is impossible to implement.
Or do you mean big range? If so, yes for some non-bigint
rings. As we don't know what the rings are, we cannot say anything about the usage of exponents of UInt.MAX_VALUE.toULong().inc()..ULog.MAX_VALUE
else -> powWithoutOptimization(exponent) | ||
} | ||
|
||
private fun T.powWithoutOptimization(exponent: ULong): T = when (exponent) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I completely agree with @altavir.
|
||
val z0 = x0 * y0 | ||
val z2 = x1 * y1 | ||
val z1 = (x0 + x1) * (y1 + y0) - z0 - z2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will it be more optimal to use the other formula for z1, namely
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed it.
I have submitted what you suggested. Also, I fixed bugs, listed in Slack. I decreased the overhead of parsing of BigInt. |
|
I found that the addition of large integers is slower with KMath BigInt than with JVM BigInteger: I added benchmarks for that. |
…igIntField usage removed
*/ | ||
public val one: T | ||
} | ||
|
||
@UnstableKMathAPI | ||
public fun <T> Ring<T>.pow(base: T, exponent: ULong): T = when { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add KDoc
} | ||
|
||
@UnstableKMathAPI | ||
public fun <T> Ring<T>.pow(base: T, exponent: UInt): T = pow(base, exponent.toULong()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add KDoc
@UnstableKMathAPI | ||
public fun <T> Ring<T>.pow(base: T, exponent: UInt): T = pow(base, exponent.toULong()) | ||
|
||
private fun <T> Ring<T>.powWithoutOptimization(base: T, exponent: ULong): T = when (exponent) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move pow operation to algebraExtensions.kt
. It actually already have those functions: https://github.com/mipt-npm/kmath/blob/ca02f5406d06d18d07909f3d48508e1a99701fa5/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/algebraExtensions.kt#L107, so you shoud probably just replace the implementation and use UInt instead of Int.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not ULong? Because of being consistent with Field<T>.power
which has a stable API?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, I see a bug with Field.power:
public fun <T> Field<T>.power(arg: T, power: Int): T {
require(power != 0 || arg != zero) { "The $zero raised to $power is not defined." }
if (power == 0) return one
if (power < 0) return one / (this as Ring<T>).power(arg, -power)
return (this as Ring<T>).power(arg, power)
}
If [power] is Int.MIN_VALUE, this will fail
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The implementation could be wrong in corner cases. It is not properly tested. As for ULong, the major bulk of operations on JVM and in kotlin use Int. I can't see a case where people would use more than Int for a power (please show them if you know them) and using Ulong would force users to do an additional unnecessary conversion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am absolutely sure that there is no need to power numbers by ULong ranged numbers, however, this may be ok for some other Rings (albeit I don't know such ones).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, 0^0 is not defined here while it is 1 in java's BigInt. Which convention to choose?
public fun <T> Ring<T>.power(arg: T, power: Int): T {
require(power >= 0) { "The power can't be negative." }
require(power != 0 || arg != zero) { "The $zero raised to $power is not defined." }
if (power == 0) return one
var res = arg
repeat(power - 1) { res *= arg }
return res
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's stick to UInt for now because I still can't imagine a case for longs. Since we are using extensions, it is easy to add another one if somebody needs it. As for 0^0, please fix it. I don't remember where this method comes from, but it was quite obviously forgotten.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean that 0^0 is expected to be 1?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I published the changes.
else | ||
-this * kotlin.math.abs(other).toUInt() | ||
@UnstableKMathAPI | ||
public fun pow(other: ULong): BigInt = BigIntField { pow(this@BigInt, other) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BigIntField.pow(this, other) should be more concise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The was a mistake in the added code It was discussed in Slack. It was fixed in the further commits to the repo.
public fun <T> Field<T>.power(arg: T, power: Int): T = when { | ||
power < 0 -> one / (this as Ring<T>).power(arg, if (power == Int.MIN_VALUE) Int.MAX_VALUE.toUInt().inc() else (-power).toUInt()) | ||
else -> (this as Ring<T>).power(arg, power.toUInt()) | ||
public fun <T> Field<T>.power(arg: T, power: UInt): T = when { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@altavir You are wrong here: Field can have negative power as Field has division. That was even in the previous implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is reverted.
abs(Int.MIN_VALUE) == Int.MIN_VALUE
was fixed.equals
function now behaves with instances of other types as all other objects do.