概要
Math.NETのベクトルVector<T>
と行列Matrix<T>
を高速なシリアライザであるで扱う方法についてまとめました。
動作条件と注意点
本記事は.NET Standard2.1版のMemoryPackが使用される.NET 5および.NET 6での動作確認をしています。
.NET 7ではCS8987によりコンパイルエラーが出ます。
コード
に則りVector<T>
をT[]
として(つまり密ベクトルとして)シリアライズできるようにラッパー、フォーマッターを実装します。
using MathNet.Numerics.LinearAlgebra;
using MemoryPack;
// シリアライズ前にFormatterを登録します(Vector<double>をシリアライズ可能にする)
MemoryPackFormatterProvider.Register(new VectorFormatter<double>());
var v = Vector<double>.Build.Random(5);
var s = new Sample(v);
var bin = MemoryPackSerializer.Serialize(s);
var val = MemoryPackSerializer.Deserialize<Sample>(bin);
Console.WriteLine(s);
Console.WriteLine(val);
Console.ReadLine();
// Vector<T>を含むシリアライズ対象クラス
[MemoryPackable]
public partial class Sample
{
[MemoryPackAllowSerialize]
public Vector<double> X { get; set; }
[MemoryPackConstructor]
public Sample(Vector<double> x)
{
X = x;
}
public override string ToString()
{
return X.ToVectorString();
}
}
// Vector<T>をシリアライズするためのWrapper
[MemoryPackable]
public partial class SerializableVector<T> where T : struct, IEquatable<T>, IFormattable
{
[MemoryPackIgnore]
public readonly Vector<T> Data;
[MemoryPackInclude]
T[] source => Data.ToArray();
[MemoryPackConstructor]
SerializableVector(T[] source)
{
Data = Vector<T>.Build.DenseOfArray(source);
}
public SerializableVector(Vector<T> data)
{
Data = data;
}
}
// カスタムフォーマッター
public class VectorFormatter<T> : MemoryPackFormatter<Vector<T>> where T : struct, IEquatable<T>, IFormattable
{
public override void Deserialize(ref MemoryPackReader reader, ref Vector<T>? value)
{
if (reader.PeekIsNull())
{
value = null;
return;
}
var wrapped = reader.ReadPackable<SerializableVector<T>>();
value = wrapped?.Data;
}
public override void Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter> writer, ref Vector<T>? value)
{
if (value == null)
{
writer.WriteNullObjectHeader();
return;
}
writer.WritePackable(new SerializableVector<T>(value));
}
}
行列は?
ベクトルの時と同様にMatrix<T>
をT[,]
とみなしてラッパーとフォーマッターを作成すればシリアライズが可能です。疎行列の場合には別途工夫が必要になると思います。