forked from tsai/mathnet-numerics
66 changed files with 1805 additions and 2670 deletions
@ -0,0 +1,333 @@ |
|||||
|
using System; |
||||
|
using MathNet.Numerics.Properties; |
||||
|
|
||||
|
namespace MathNet.Numerics.LinearAlgebra.Storage |
||||
|
{ |
||||
|
[Serializable] |
||||
|
public class SparseVectorStorage<T> : VectorStorage<T> |
||||
|
where T : struct, IEquatable<T>, IFormattable |
||||
|
{ |
||||
|
// [ruegg] public fields are OK here
|
||||
|
|
||||
|
readonly T _zero; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Array that contains the indices of the non-zero values.
|
||||
|
/// </summary>
|
||||
|
public int[] Indices; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Array that contains the non-zero elements of the vector.
|
||||
|
/// </summary>
|
||||
|
public T[] Values; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the number of non-zero elements in the vector.
|
||||
|
/// </summary>
|
||||
|
public int ValueCount; |
||||
|
|
||||
|
internal SparseVectorStorage(int length, T zero = default(T)) |
||||
|
: base(length) |
||||
|
{ |
||||
|
_zero = zero; |
||||
|
Indices = new int[0]; |
||||
|
Values = new T[0]; |
||||
|
ValueCount = 0; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Retrieves the requested element without range checking.
|
||||
|
/// </summary>
|
||||
|
public override T At(int index) |
||||
|
{ |
||||
|
// Search if item idex exists in NonZeroIndices array in range "0 - nonzero values count"
|
||||
|
var itemIndex = Array.BinarySearch(Indices, 0, ValueCount, index); |
||||
|
return itemIndex >= 0 ? Values[itemIndex] : _zero; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Sets the element without range checking.
|
||||
|
/// </summary>
|
||||
|
public override void At(int index, T value) |
||||
|
{ |
||||
|
// Search if "index" already exists in range "0 - nonzero values count"
|
||||
|
var itemIndex = Array.BinarySearch(Indices, 0, ValueCount, index); |
||||
|
if (itemIndex >= 0) |
||||
|
{ |
||||
|
// Non-zero item found in matrix
|
||||
|
if (_zero.Equals(value)) |
||||
|
{ |
||||
|
// Delete existing item
|
||||
|
RemoveAtIndexUnchecked(itemIndex); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
// Update item
|
||||
|
Values[itemIndex] = value; |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
// Item not found. Add new value
|
||||
|
if (!_zero.Equals(value)) |
||||
|
{ |
||||
|
InsertAtIndexUnchecked(~itemIndex, index, value); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void InsertAtIndexUnchecked(int itemIndex, int index, T value) |
||||
|
{ |
||||
|
// Check if the storage needs to be increased
|
||||
|
if ((ValueCount == Values.Length) && (ValueCount < Length)) |
||||
|
{ |
||||
|
// Value and Indices arrays are completely full so we increase the size
|
||||
|
var size = Math.Min(Values.Length + GrowthSize(), Length); |
||||
|
Array.Resize(ref Values, size); |
||||
|
Array.Resize(ref Indices, size); |
||||
|
} |
||||
|
|
||||
|
// Move all values (with a position larger than index) in the value array to the next position
|
||||
|
// Move all values (with a position larger than index) in the columIndices array to the next position
|
||||
|
Array.Copy(Values, itemIndex, Values, itemIndex + 1, ValueCount - itemIndex); |
||||
|
Array.Copy(Indices, itemIndex, Indices, itemIndex + 1, ValueCount - itemIndex); |
||||
|
|
||||
|
// Add the value and the column index
|
||||
|
Values[itemIndex] = value; |
||||
|
Indices[itemIndex] = index; |
||||
|
|
||||
|
// increase the number of non-zero numbers by one
|
||||
|
ValueCount += 1; |
||||
|
} |
||||
|
|
||||
|
void RemoveAtIndexUnchecked(int itemIndex) |
||||
|
{ |
||||
|
// Value is zero. Let's delete it from Values and Indices array
|
||||
|
Array.Copy(Values, itemIndex + 1, Values, itemIndex, ValueCount - itemIndex - 1); |
||||
|
Array.Copy(Indices, itemIndex + 1, Indices, itemIndex, ValueCount - itemIndex - 1); |
||||
|
|
||||
|
ValueCount -= 1; |
||||
|
|
||||
|
// Check whether we need to shrink the arrays. This is reasonable to do if
|
||||
|
// there are a lot of non-zero elements and storage is two times bigger
|
||||
|
if ((ValueCount > 1024) && (ValueCount < Indices.Length / 2)) |
||||
|
{ |
||||
|
Array.Resize(ref Values, ValueCount); |
||||
|
Array.Resize(ref Indices, ValueCount); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Calculates the amount with which to grow the storage array's if they need to be
|
||||
|
/// increased in size.
|
||||
|
/// </summary>
|
||||
|
/// <returns>The amount grown.</returns>
|
||||
|
int GrowthSize() |
||||
|
{ |
||||
|
int delta; |
||||
|
if (Values.Length > 1024) |
||||
|
{ |
||||
|
delta = Values.Length / 4; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
if (Values.Length > 256) |
||||
|
{ |
||||
|
delta = 512; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
delta = Values.Length > 64 ? 128 : 32; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return delta; |
||||
|
} |
||||
|
|
||||
|
public override void Clear() |
||||
|
{ |
||||
|
ValueCount = 0; |
||||
|
} |
||||
|
|
||||
|
public override void Clear(int index, int count) |
||||
|
{ |
||||
|
if (index == 0 && count == Length) |
||||
|
{ |
||||
|
Clear(); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var first = Array.BinarySearch(Indices, 0, ValueCount, index); |
||||
|
var last = Array.BinarySearch(Indices, 0, ValueCount, index + count - 1); |
||||
|
if (first < 0) first = ~first; |
||||
|
if (last < 0) last = ~last - 1; |
||||
|
int itemCount = last - first + 1; |
||||
|
|
||||
|
if (itemCount > 0) |
||||
|
{ |
||||
|
Array.Copy(Values, first + count, Values, first, ValueCount - first - count); |
||||
|
Array.Copy(Indices, first + count, Indices, first, ValueCount - first - count); |
||||
|
|
||||
|
ValueCount -= count; |
||||
|
} |
||||
|
|
||||
|
// Check whether we need to shrink the arrays. This is reasonable to do if
|
||||
|
// there are a lot of non-zero elements and storage is two times bigger
|
||||
|
if ((ValueCount > 1024) && (ValueCount < Indices.Length / 2)) |
||||
|
{ |
||||
|
Array.Resize(ref Values, ValueCount); |
||||
|
Array.Resize(ref Indices, ValueCount); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <remarks>Parameters assumed to be validated already.</remarks>
|
||||
|
public override void CopyTo(VectorStorage<T> target, bool skipClearing = false) |
||||
|
{ |
||||
|
var sparseTarget = target as SparseVectorStorage<T>; |
||||
|
if (sparseTarget != null) |
||||
|
{ |
||||
|
CopyTo(sparseTarget); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// FALL BACK
|
||||
|
|
||||
|
if (!skipClearing) |
||||
|
{ |
||||
|
target.Clear(); |
||||
|
} |
||||
|
|
||||
|
if (ValueCount != 0) |
||||
|
{ |
||||
|
for (int i = 0; i < ValueCount; i++) |
||||
|
{ |
||||
|
target.At(Indices[i], Values[i]); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void CopyTo(SparseVectorStorage<T> target) |
||||
|
{ |
||||
|
if (ReferenceEquals(this, target)) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (Length != target.Length) |
||||
|
{ |
||||
|
var message = string.Format(Resources.ArgumentMatrixDimensions2, Length, target.Length); |
||||
|
throw new ArgumentException(message, "target"); |
||||
|
} |
||||
|
|
||||
|
target.ValueCount = ValueCount; |
||||
|
target.Values = new T[ValueCount]; |
||||
|
target.Indices = new int[ValueCount]; |
||||
|
|
||||
|
if (ValueCount != 0) |
||||
|
{ |
||||
|
Array.Copy(Values, target.Values, ValueCount); |
||||
|
Buffer.BlockCopy(Indices, 0, target.Indices, 0, ValueCount * Constants.SizeOfInt); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public override void CopySubVectorTo(VectorStorage<T> target, |
||||
|
int sourceIndex, int targetIndex, int count, |
||||
|
bool skipClearing = false) |
||||
|
{ |
||||
|
ValidateSubVectorRange(target, sourceIndex, targetIndex, count); |
||||
|
|
||||
|
var sparseTarget = target as SparseVectorStorage<T>; |
||||
|
if (sparseTarget != null) |
||||
|
{ |
||||
|
CopySubVectorTo(sparseTarget, sourceIndex, targetIndex, count, skipClearing); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// FALL BACK
|
||||
|
|
||||
|
var offset = targetIndex - sourceIndex; |
||||
|
|
||||
|
var sourceFirst = Array.BinarySearch(Indices, 0, ValueCount, sourceIndex); |
||||
|
var sourceLast = Array.BinarySearch(Indices, 0, ValueCount, sourceIndex + count - 1); |
||||
|
if (sourceFirst < 0) sourceFirst = ~sourceFirst; |
||||
|
if (sourceLast < 0) sourceLast = ~sourceLast - 1; |
||||
|
|
||||
|
if (!skipClearing) |
||||
|
{ |
||||
|
target.Clear(targetIndex, count); |
||||
|
} |
||||
|
|
||||
|
for (int i = sourceFirst; i <= sourceLast; i++) |
||||
|
{ |
||||
|
target.At(Indices[i] + offset, Values[i]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void CopySubVectorTo(SparseVectorStorage<T> target, |
||||
|
int sourceIndex, int targetIndex, int count, |
||||
|
bool skipClearing) |
||||
|
{ |
||||
|
var offset = targetIndex - sourceIndex; |
||||
|
|
||||
|
var sourceFirst = Array.BinarySearch(Indices, 0, ValueCount, sourceIndex); |
||||
|
var sourceLast = Array.BinarySearch(Indices, 0, ValueCount, sourceIndex + count - 1); |
||||
|
if (sourceFirst < 0) sourceFirst = ~sourceFirst; |
||||
|
if (sourceLast < 0) sourceLast = ~sourceLast - 1; |
||||
|
int sourceCount = sourceLast - sourceFirst + 1; |
||||
|
|
||||
|
// special case when copying to itself
|
||||
|
if (ReferenceEquals(this, target)) |
||||
|
{ |
||||
|
var values = new T[sourceCount]; |
||||
|
var indices = new int[sourceCount]; |
||||
|
|
||||
|
Array.Copy(Values, sourceFirst, values, 0, sourceCount); |
||||
|
for (int i = 0; i < indices.Length; i++) |
||||
|
{ |
||||
|
indices[i] = Indices[i + sourceFirst]; |
||||
|
} |
||||
|
|
||||
|
if (!skipClearing) |
||||
|
{ |
||||
|
Clear(targetIndex, count); |
||||
|
} |
||||
|
|
||||
|
for (int i = sourceFirst; i <= sourceLast; i++) |
||||
|
{ |
||||
|
At(indices[i] + offset, values[i]); |
||||
|
} |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// special case for empty target - much faster
|
||||
|
if (target.ValueCount == 0) |
||||
|
{ |
||||
|
var values = new T[sourceCount]; |
||||
|
var indices = new int[sourceCount]; |
||||
|
|
||||
|
Array.Copy(Values, sourceFirst, values, 0, sourceCount); |
||||
|
for (int i = 0; i < indices.Length; i++) |
||||
|
{ |
||||
|
indices[i] = Indices[i + sourceFirst] + offset; |
||||
|
} |
||||
|
|
||||
|
target.ValueCount = sourceCount; |
||||
|
target.Values = values; |
||||
|
target.Indices = indices; |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (!skipClearing) |
||||
|
{ |
||||
|
target.Clear(targetIndex, count); |
||||
|
} |
||||
|
|
||||
|
for (int i = sourceFirst; i <= sourceLast; i++) |
||||
|
{ |
||||
|
target.At(Indices[i] + offset, Values[i]); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue