[Swift] 複素数型 Complex

C#の複素数型は、↓こちらを参照ください。

https://qiita.com/hextomino/items/13d74872625e52a1329a

:::note info
Swift の記事ですが、今回の動作検証は Windows PC で行いました。

Intel N97 Processor, LPDDR5 RAM 12GB
Windows11 Pro 64bit 24H2 （26100.2161）
C# Compiler version 4.11.0-3.24460.3 (5649376e)
Swift version 5.9.2 (swift-5.9.2-RELEASE)
:::


当初の Swift には`Complex`型 はありませんでしたが、2019年12月にサポートされました。ただし、`Complex`型を使う場合は、[`Swift Numerics`](https://github.com/apple/swift-numerics)パッケージを追加する必要があります。

```swift:Package.swift（例）
import PackageDescription

let package = Package(
    name: "complex",
    dependencies: [
        .package(url: "https://github.com/apple/swift-numerics", from: "1.0.2")
    ],
    targets: [
        .executableTarget(
            name: "complex",
            dependencies: [
              .product(name: "Numerics", package: "swift-numerics")
            ]),
    ]
)
```

# 精度　C# vs Swift

冒頭の記事のコードを流用させていただきました。

```c#:C#
using System.Numerics;

Complex c = new Complex(0, 1);

Console.WriteLine(Complex.Multiply(c, c));
Console.WriteLine(Complex.Pow(c, 2));
Console.WriteLine(Complex.Sqrt(c));
Console.WriteLine(Complex.Pow(c, 0.5));
Console.WriteLine(Complex.Pow(c, c));
```

```swift:Swift
import Numerics

let c = Complex(0, 1)

print(c * c)
print(Complex.pow(c, 2))
print(Complex.sqrt(c))
print(Complex.pow(c, Complex(0.5, 0)))
print(Complex.pow(c, c))
```

```text:C#結果
<-1; 0>
<-1; 1.2246467991473532E-16>
<0.7071067811865476; 0.7071067811865475>
<0.7071067811865476; 0.7071067811865476>
<0.20787957635076193; 0>
```

```text:Swift結果
(-1.0, 0.0)
(-1.0, 1.2246467991473532e-16)
(0.7071067811865476, 0.7071067811865475)
(0.7071067811865476, 0.7071067811865475)
(0.20787957635076193, 0.0)
```

計算精度は、イーブンでした。


# 性能　C# vs Swift

冒頭の記事の[`マンデルブロ集合`](https://en.wikipedia.org/wiki/Mandelbrot_set)のコードを流用させていただきました。
_10回描画の平均値（コンソール出力も含めた処理時間）_

```c#:C#
using System.Numerics;
using System.Diagnostics;

long MandelbrotSet()
{
    Stopwatch sw = Stopwatch.StartNew();

    for (double y = -1.25d; y <= 1.25d; y += 2.5d / 24.0d)
    {
        for (double x = -2.0d; x <= 0.5d; x += 2.5d / 64.0d)
        {
            Complex z = new Complex(0, 0);
            Complex c = new Complex(x, y);
            for (int n = 0; n < 256; n++)
            {
                z = Complex.Pow(z, 2);
                z = Complex.Add(z, c);

                if (z.Magnitude > 2)
                {
                    Console.Write(".");
                    break;
                }
                if (n == 255)
                {
                    Console.Write("*");
                }
            }
        }
        Console.WriteLine("");
    }

    sw.Stop();
    return sw.ElapsedMilliseconds;
}

long total = 0;
List<long> results = new List<long>();
for (int n = 0; n < 10; n++)
{
    long result = MandelbrotSet();
    total += result;
    results.Add(result);
}

results.Sort();
foreach (var r in results)
{
    Console.WriteLine(r);
}
Console.WriteLine((double)total / (double)results.Count);
```

```swift:Swift
import Foundation
import Numerics

func MandelbrotSet() -> Double {
    let sw = Stopwatch.startNow()

    for y in stride(from: -1.25, through: 1.25, by: 2.5 / 24.0) {
        for x in stride(from: -2.0, through: 0.5, by: 2.5 / 64.0) {
            var z = Complex(0, 0)
            let c = Complex(x, y)
            for n in 0 ..< 256 {
                z = Complex.pow(z, 2)
                z += c
                
                if (z.magnitude > 2) {
                    print(".", terminator: "")
                    break
                }
                if (n == 255) {
                    print("*", terminator: "")
                }
            }
        }
        print()
    }

    sw.stop()
    return sw.elapsedMilliseconds
}


var total: Double = 0;
var results: [Double] = []
for _ in 0 ..< 10 {
    let result = MandelbrotSet()
    total += result
    results.append(result)
}

results.sort()
for r in results {
    print(r)
}
print(total / Double(results.count))
```

```text:マンデルブロ集合
.................................................................
.................................................................
.................................................................
.................................................................
................................................*................
..............................................*****..............
..........................................*.*********.*..........
.......................................*********************.....
...................................*************************.....
..................................****************************...
......................********...*****************************...
....................************.****************************....
......*........*******************************************.......
....................************.****************************....
......................********...*****************************...
..................................****************************...
...................................*************************.....
.......................................*********************.....
..........................................*.*********.*..........
..............................................*****..............
................................................*................
.................................................................
.................................................................
.................................................................
.................................................................
```

```text:C#結果
57
57
58
58
58
59
59
63
64
66
59.9
```

```text:Swift結果
49.999237060546875
55.00030517578125
59.00001525878906
59.99946594238281
59.99946594238281
59.99946594238281
62.999725341796875
62.999725341796875
63.00163269042969
63.00163269042969
59.600067138671875
```

10回の平均値は 59.9ms と 59.6ms で、こちらもほぼイーブンです。（実行する度に、10〜20ミリ秒のブレがある）

_どちらの結果も`ミリ秒`単位ですが、C#は long型 、Swiftは Double型と違いがあります。そもそも正しいベンチマークになっていないのかも？_

<br><br>Swiftで使用した Stopwatch型は[`こちら`](https://qiita.com/nak435/items/f8b0cd88b51ae328731f#stopwatchクラス)を参照。


# 参考

実は、Complex型の何が便利なのか理解できていません。勉強不足ですみません。

古い記事ですが、ネットで見つけたサイトを紹介します。

- 2016/7/13	（SwiftにまだComplex型が無かった頃）

https://blog.nowhere.co.jp/archives/20160713-15610.html


- 2019/11/12	（SwiftにComplex型がサポートされた後）

https://www.infoq.com/news/2019/11/swift-numerics-library/

- 日本語サイト

https://www.infoq.com/jp/news/2019/12/swift-numerics-library/


<br><br><br>

**以上**