Skip to content

Commit aa37683

Browse files
markuswntrstephentyrone
authored andcommitted
Remove log and pow functions
1 parent bc944e7 commit aa37683

File tree

2 files changed

+0
-169
lines changed

2 files changed

+0
-169
lines changed

Sources/QuaternionModule/ElementaryFunctions.swift

Lines changed: 0 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -202,136 +202,6 @@ extension Quaternion/*: ElementaryFunctions */ {
202202
let p = Quaternion(imaginary: â)
203203
return -p * tanh(q * p)
204204
}
205-
206-
// MARK: - log-like functions
207-
@inlinable
208-
public static func log(_ q: Quaternion) -> Quaternion {
209-
// If q is zero or infinite, the phase is undefined, so the result is
210-
// the single exceptional value.
211-
guard q.isFinite && !q.isZero else { return .infinity }
212-
213-
let argument = q.imaginary.length
214-
let axis = q.imaginary / argument
215-
216-
// We deliberatly choose log(length) over the (faster)
217-
// log(lengthSquared) / 2 which is used for complex numbers; as
218-
// the squared length of quaternions is more prone to overflows than the
219-
// squared length of complex numbers.
220-
return Quaternion(real: .log(q.length), imaginary: axis * q.halfAngle)
221-
}
222-
223-
@inlinable
224-
public static func log(onePlus q: Quaternion) -> Quaternion {
225-
// If either |r| or ||v||₁ is bounded away from the origin, we don't need
226-
// any extra precision, and can just literally compute log(1+z). Note
227-
// that this includes part of the sphere |1+q| = 1 where log(onePlus:)
228-
// vanishes (where r <= -0.5), but on this portion of the sphere 1+r
229-
// is always exact by Sterbenz' lemma, so as long as log( ) produces
230-
// a good result, log(1+q) will too.
231-
guard 2*q.real.magnitude < 1 && q.imaginary.oneNorm < 1 else {
232-
return log(.one + q)
233-
}
234-
// q is in (±0.5, ±1), so we need to evaluate more carefully.
235-
// The imaginary part is straightforward:
236-
let argument = (.one + q).halfAngle
237-
let (â,_) = q.imaginary.unitAxisAndLength
238-
let imaginary = â * argument
239-
// For the real part, we _could_ use the same approach that we do for
240-
// log( ), but we'd need an extra-precise (1+r)², which can potentially
241-
// be quite painful to calculate. Instead, we can use an approach that
242-
// NevinBR suggested on the Swift forums for complex numbers:
243-
//
244-
// Re(log 1+q) = (log 1+q + log 1+q̅)/2
245-
// = log((1+q)(1+q̅)/2
246-
// = log(1 + q + q̅ + qq̅)/2
247-
// = log1p((2+r)r + x² + y² + z²)/2
248-
//
249-
// So now we need to evaluate (2+r)r + x² + y² + z² accurately. To do this,
250-
// we employ augmented arithmetic;
251-
// (2+r)r + x² + y² + z²
252-
// --↓--
253-
let rp2 = Augmented.fastTwoSum(2, q.real) // Known that 2 > |r|
254-
var (head, δ) = Augmented.twoProdFMA(q.real, rp2.head)
255-
var tail = δ
256-
// head + x² + y² + z²
257-
// ----↓----
258-
let x² = Augmented.twoProdFMA(q.imaginary.x, q.imaginary.x)
259-
(head, δ) = Augmented.twoSum(head, x².head)
260-
tail += (δ + x².tail)
261-
// head + y² + z²
262-
// ----↓----
263-
let y² = Augmented.twoProdFMA(q.imaginary.y, q.imaginary.y)
264-
(head, δ) = Augmented.twoSum(head, y².head)
265-
tail += (δ + y².tail)
266-
// head + z²
267-
// ----↓----
268-
let z² = Augmented.twoProdFMA(q.imaginary.z, q.imaginary.z)
269-
(head, δ) = Augmented.twoSum(head, z².head)
270-
tail += (δ + z².tail)
271-
272-
let s = (head + tail).addingProduct(q.real, rp2.tail)
273-
return Quaternion(real: .log(onePlus: s)/2, imaginary: imaginary)
274-
}
275-
276-
//
277-
// MARK: - pow-like functions
278-
279-
@inlinable
280-
public static func pow(_ q: Quaternion, _ p: Quaternion) -> Quaternion {
281-
// Mathematically, this operation can be expanded in terms of the
282-
// quaternionic `exp` and `log` operations as follows:
283-
//
284-
// ```
285-
// pow(q, p) = exp(log(pow(q, p)))
286-
// = exp(p * log(q))
287-
// ```
288-
exp(p * log(q))
289-
}
290-
291-
@inlinable
292-
public static func pow(_ q: Quaternion, _ n: Int) -> Quaternion {
293-
// Mathematically, this operation can be expanded in terms of the
294-
// quaternionic `exp` and `log` operations as follows:
295-
//
296-
// ```
297-
// pow(q, n) = exp(log(pow(q, n)))
298-
// = exp(log(q) * n)
299-
// ```
300-
guard !q.isZero else { return .zero }
301-
// TODO: this implementation is not quite correct, because n may be
302-
// rounded in conversion to RealType. This only effects very extreme
303-
// cases, so we'll leave it alone for now.
304-
return exp(log(q).multiplied(by: RealType(n)))
305-
}
306-
307-
@inlinable
308-
public static func sqrt(_ q: Quaternion) -> Quaternion<RealType> {
309-
// Mathematically, this operation can be expanded in terms of the
310-
// quaternionic `exp` and `log` operations as follows:
311-
//
312-
// ```
313-
// sqrt(q) = q^(1/2) = exp(log(q^(1/2)))
314-
// = exp(log(q) * (1/2))
315-
// ```
316-
guard !q.isZero else { return .zero }
317-
return exp(log(q).divided(by: 2))
318-
}
319-
320-
@inlinable
321-
public static func root(_ q: Quaternion, _ n: Int) -> Quaternion {
322-
// Mathematically, this operation can be expanded in terms of the
323-
// quaternionic `exp` and `log` operations as follows:
324-
//
325-
// ```
326-
// root(q, n) = exp(log(root(q, n)))
327-
// = exp(log(q) / n)
328-
// ```
329-
guard !q.isZero else { return .zero }
330-
// TODO: this implementation is not quite correct, because n may be
331-
// rounded in conversion to RealType. This only effects very extreme
332-
// cases, so we'll leave it alone for now.
333-
return exp(log(q).divided(by: RealType(n)))
334-
}
335205
}
336206

337207
extension SIMD3 where Scalar: FloatingPoint {
@@ -349,18 +219,3 @@ extension SIMD3 where Scalar: FloatingPoint {
349219
return (self/length, length)
350220
}
351221
}
352-
353-
extension Augmented {
354-
355-
// TODO: Move to Augmented.swift
356-
@usableFromInline @_transparent
357-
internal static func twoSum<T:Real>(_ a: T, _ b: T) -> (head: T, tail: T) {
358-
let head = a + b
359-
let x = head - b
360-
let y = head - x
361-
let ax = a - x
362-
let by = b - y
363-
let tail = ax + by
364-
return (head, tail)
365-
}
366-
}

Tests/QuaternionTests/ElementaryFunctionTests.swift

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -234,34 +234,12 @@ final class ElementaryFunctionTests: XCTestCase {
234234
}
235235
}
236236

237-
// MARK: - log-like functions
238-
239-
func testLog<T: Real & FixedWidthFloatingPoint & SIMDScalar>(_ type: T.Type) {
240-
// log(0) = undefined/infinity
241-
XCTAssertFalse(Quaternion<T>.log(Quaternion(real: 0, imaginary: 0, 0, 0)).isFinite)
242-
XCTAssertFalse(Quaternion<T>.log(Quaternion(real:-0, imaginary: 0, 0, 0)).isFinite)
243-
XCTAssertFalse(Quaternion<T>.log(Quaternion(real:-0, imaginary:-0,-0,-0)).isFinite)
244-
XCTAssertFalse(Quaternion<T>.log(Quaternion(real: 0, imaginary:-0,-0,-0)).isFinite)
245-
246-
var g = SystemRandomNumberGenerator()
247-
let values: [Quaternion<T>] = (0..<100).map { _ in
248-
Quaternion(
249-
real: T.random(in: -1 ... 1, using: &g),
250-
imaginary: SIMD3(repeating: T.random(in: -.pi ... .pi, using: &g) / 3))
251-
}
252-
for q in values {
253-
XCTAssertTrue(q.isApproximatelyEqual(to: .log(.exp(q))))
254-
}
255-
}
256-
257237
func testFloat() {
258238
testExp(Float32.self)
259239
testExpMinusOne(Float32.self)
260240
testCosh(Float32.self)
261241
testSinh(Float32.self)
262242
testCosSin(Float32.self)
263-
264-
testLog(Float32.self)
265243
}
266244

267245
func testDouble() {
@@ -270,7 +248,5 @@ final class ElementaryFunctionTests: XCTestCase {
270248
testCosh(Float64.self)
271249
testSinh(Float64.self)
272250
testCosSin(Float64.self)
273-
274-
testLog(Float64.self)
275251
}
276252
}

0 commit comments

Comments
 (0)