Spire
Spire — это числовая библиотека для Scala, которая должна быть универсальной, быстрой и точной.
Используя такие функции, как специализация, макросы, классы типов и неявные выражения, Spire прилагает все усилия, чтобы бросить вызов общепринятым представлениям о компромиссах между производительностью и точностью. Основная цель — позволить разработчикам писать эффективный числовой код без необходимости "встраивать" определенные числовые представления. В большинстве случаев универсальные реализации, использующие специализированные классы типов Spire, работают идентично соответствующим прямым реализациям.
Пример использования библиотеки Spire:
import spire.*
import spire.implicits.*
import spire.math.*
Complex(3.0,5.0).sin
// val res0: spire.math.Complex[Double] = (10.472508533940392 + -73.46062169567367i)
Числовые типы
Помимо поддержки всех встроенных числовых типов Scala,
Spire представляет несколько новых, все из которых можно найти в spire.math
:
Natural
- беззнаковое, неизменяемое целое число произвольной точностиRational
- дроби целых чисел с идеальной точностьюAlgebraic
- лениво вычисляемые алгебраические числа произвольной точностиReal
- реализация вычислимых действительных чиселComplex[A]
- комплексные числа, точки на комплексной плоскостиJet[A]
- N-мерные двойные числа для автоматического дифференцированияQuaternion[A]
- расширение комплексных чисел в четырехмерное пространство- от
UByte
доULong
- классы значений, поддерживающие беззнаковые операции SafeLong
- быстрый целочисленный тип с защитой от переполненияNumber
- запечатанный тип с поддержкой традиционной числовой опорыInterval[A]
- арифметика на открытых, закрытых и несвязанных интервалахPolynomial[A]
- одномерные (с одной переменной) полиномиальные выраженияTrilean
- класс значений, поддерживающий трехзначную логикуFixedPoint
- дроби сLong
числителем и неявным знаменателем (в доп.)
Классы типов
Spire предоставляет классы типов для поддержки широкого спектра унарных и двоичных операций с числами. Классы типов являются специализированными, не выполняют упаковку и используют неявные выражения для обеспечения удобного инфиксного синтаксиса.
Классы типов общего назначения можно найти в пакете spire.math
:
Numeric[A]
- все типы чисел, прилагает все усилия для поддержки операторовFractional[A]
- типы дробных чисел, где/
- честное делениеIntegral[A]
- типы целых чисел, где/
- деление по уровням
Некоторые классы типов общего назначения построены на основе набора более фундаментальных классов типов,
определенных в spire.algebra
.
Многие из них соответствуют понятиям абстрактной алгебры:
Eq[A]
- типы, которые можно сравнивать на равенствоOrder[A]
- типы, которые можно сравнивать и упорядочиватьPartialOrder[A]
- типы, которые можно сравнивать на равенство и для которых упорядочены определенные парыSemigroup[A]
- типы с ассоциативным бинарным оператором|+|
Monoid[A]
- полугруппы, имеющие единичный элементGroup[A]
- моноиды, имеющие обратный оператор(Left/Right/)Action[P, G]
- действия влево/вправо для полугрупп/моноидов/группSemiring[A]
- типы, образующие полугруппы под+
и*
Rng[A]
- типы, которые образуют группу+
и полугруппу под*
Rig[A]
- типы, образующие моноиды под+
и*
Ring[A]
- типы, образующие группу под+
и моноид под*
EuclideanRing[A]
- кольца с частными и остатками (евклидово деление)Field[A]
- евклидовы кольца с мультипликативными инверсиями (взаимными)Signed[A]
- типы, имеющие знак (отрицательный, ноль, положительный)NRoot[A]
- типы, поддерживающие k-корни, журналы и дробные степениModule[V,R]
- типы, образующие левый R-модульVectorSpace[V,F]
- типы, образующие векторное пространствоNormedVectorSpace[V,F]
- типы с соответствующей нормойInnerProductSpace[V,F]
- типы с внутренним произведениемMetricSpace[V,R]
- типы со связанной метрикойTrig[A]
- типы, поддерживающие тригонометрические функцииBool[A]
- типы, образующие булеву алгебруHeyting[A]
- типы, образующие алгебру Гейтинга
Варианты Semigroup
/Monoid
/Group
/Action
с частичными операциями определены в подпакете spire.algebra.partial
.
Помимо самих классов типов, spire.implicits
определяет множество неявных функций,
которые предоставляют унарные и инфиксные операторы для классов типов.
Самый простой способ использовать их — импортировать файлы spire.implicits.*
.
С чего начать?
Spire содержит множество типов, а также другие механизмы, обеспечивающие удобство использования. Самый простой способ использовать Spire — импортировать зависимости:
import spire.algebra.* // предоставляет алгебраические классы типов
import spire.implicits.* // обеспечивает инфиксные операторы, инстансы и конверсию
import spire.math.* // обеспечивает функции, типа и классы типов
Операции, отсортированные по классам типов
Ниже приводится более подробное описание классов типов, предоставляемых Spire, а также операторов, которые они используют. Хотя Spire по возможности избегает введения новых операторов, в некоторых случаях это было неизбежно.
- Eq, Order и PartialOrder
- Semigroup, Monoid и Group
- Ring и компания
- Vector и компания
- Алгебра Гейтинга и Bool
- Тригонометрия
Числа
Эти классы типов высокого уровня будут включать все соответствующие классы алгебраических типов.
Integral
: целочисленные типы (например,Int
,BigInt
)Fractional
: дробные/десятичные типы (например,Double
,Rational
)Numeric
: тип произвольного числа, прилагающий "все усилия" для поддержки операций
Класс типов Numeric
уникален тем, что обеспечивает ту же функциональность, что Fractional
и все числовые типы.
Каждый тип будет пытаться "поступить правильно", насколько это возможно, в противном случае выдавать ошибки.
Пользователям, которые настороженно относятся к такому поведению, рекомендуется использовать более точные классы типов.
Синтаксис
Используя строковую интерполяцию и макросы, Spire обеспечивает удобный синтаксис для числовых типов. Эти макросы оцениваются во время компиляции, и любые ошибки, с которыми они сталкиваются, возникают во время компиляции.
Например:
object LiteralsDemo:
import spire.syntax.literals.*
// byte-ы и short-ы
val x = b"100" // без аннотации типа!
val y = h"999"
val mask = b"255" // беззнаковая константа сконвертированная в знаковую (-1)
// дроби
val n1 = r"1/3"
val n2 = r"1599/115866" // упроститься при компиляции до 13/942
object SIDemo:
// SI нотация для больших чисел
import spire.syntax.literals.si.* // также доступны `.us` и `.eu`
val w = i"1 944 234 123" // Int
val x = j"89 234 614 123 234 772" // Long
val y = big"123 234 435 456 567 678 234 123 112 234 345" // BigInt
val z = dec"1 234 456 789.123456789098765" // BigDecimal
Spire также предоставляет макрос цикла cfor
, синтаксис которого немного похож на традиционный цикл for
из C или Java.
Этот макрос расширяется до хвостовой рекурсивной функции, которая встраивает литеральные аргументы функции.
Макрос может быть вложен сам в себя и выгодно отличается от других конструкций цикла в Scala, таких как for
и while
:
import spire.syntax.cfor.*
// печатает числа от 0 до 9
cfor(0)(_ < 10, _ + 1): i =>
println(i)
// простой алгоритм сортировки
def selectionSort(ns: Array[Int]): Unit =
val limit = ns.length - 1
cfor(0)(_ < limit, _ + 1): i =>
var k = i
val n = ns(i)
cfor(i + 1)(_ <= limit, _ + 1): j =>
if ns(j) < ns(k) then k = j
ns(i) = ns(k)
ns(k) = n
Остальные операции
Разное
Кроме того, Spire предоставляет множество других методов, которые "отсутствуют" в java.Math
(и scala.math
),
например:
log(BigDecimal): BigDecimal
exp(BigDecimal): BigDecimal
pow(BigDecimal): BigDecimal
pow(Long): Long
gcd(Long, Long): Long
- и так далее...
Тесты
Помимо модульных тестов, Spire поставляется с относительно детальным набором микротестов JMH.
Чтобы запустить тесты из SBT, перейдите к подпроекту benchmark
и затем запустите Jmh / run -l
, чтобы просмотреть список тестов:
$ sbt
> project benchmark
> Jmh / run -l
[info] Benchmarks:
[info] spire.benchmark.AddBenchmarks.addComplexDoubleStateDirect
[info] spire.benchmark.AddBenchmarks.addComplexDoubleStateGeneric
[info] spire.benchmark.AddBenchmarks.addComplexFloatStateDirect
...
Чтобы запустить все доступные тесты:
> Jmh / run
Чтобы запустить определенный метод тестирования:
> Jmh / run spire.benchmark.AddBenchmarks.addComplexDoubleStateDirect
Чтобы запустить все тесты в определенном классе:
> Jmh / run spire.benchmark.AddBenchmarks
Чтобы просмотреть все доступные варианты использования JMH:
> Jmh / run -h
Если вы планируете внести свой вклад в Spire, обязательно запустите соответствующие тесты, чтобы убедиться, что ваши изменения не повлияют на производительность. Тесты обычно включают сравнение с эквивалентными классами Scala или Java, чтобы попытаться измерить как относительную, так и абсолютную производительность.
Ссылки: