示例#1
0
        private void PrettyPrint(StringBuilder s, bool flat = false)
        {
            if (Shape.Dimensions.Length == 0)
            {
                s.Append($"{GetValue(0)}");
                return;
            }
            if (Shape.Dimensions.Length == 1)
            {
                s.Append("[");
                s.Append(string.Join(", ", this.GetData().OfType <object>().Select(x => x == null ? "null" : x.ToString())));
                s.Append("]");
                return;
            }
            var last_dim = Shape.Dimensions.Last();
            var slices   = new Slice[Shape.Dimensions.Length];

            s.Append("[");
            for (int i = 0; i < last_dim; i++)
            {
                slices[0] = Slice.Index(i);
                var n_minus_one_dim_slice = new ViewStorage(this, slices);
                n_minus_one_dim_slice.PrettyPrint(s, flat);
                if (i < last_dim - 1)
                {
                    s.Append(", ");
                    if (!flat)
                    {
                        s.AppendLine();
                    }
                }
            }
            s.Append("]");
        }
示例#2
0
        private void PrettyPrint(StringBuilder s, bool flat = false)
        {
            if (shape.Length == 0)
            {
                s.Append($"{Storage.GetData().GetValue(0)}");
                return;
            }
            if (shape.Length == 1)
            {
                s.Append("[");
                s.Append(string.Join(", ", this.Array.OfType <object>().Select(x => x == null ? "null" : x.ToString())));
                s.Append("]");
                return;
            }
            var size = shape[0];

            s.Append("[");
            for (int i = 0; i < size; i++)
            {
                var n_minus_one_dim_slice = this[Slice.Index(i)];
                n_minus_one_dim_slice.PrettyPrint(s, flat);
                if (i < size - 1)
                {
                    s.Append(", ");
                    if (!flat)
                    {
                        s.AppendLine();
                    }
                }
            }
            s.Append("]");
        }
示例#3
0
        protected void SetIndices(object[] indicesObjects, NDArray values)
        {
            var indicesLen = indicesObjects.Length;
            if (indicesLen == 1)
            {
                switch (indicesObjects[0])
                {
                    case NDArray nd:
                        SetIndices(this, new NDArray[] {nd}, values);
                        return;
                    case int i:
                        Storage.SetData(values, i);
                        return;
                    case bool boolean:
                        if (boolean == false)
                            return; //do nothing

                        SetData(values);
                        return; // np.expand_dims(this, 0); //equivalent to [np.newaxis]

                    case int[] coords:
                        SetData(values, coords);
                        return;
                    case NDArray[] nds:
                        this[nds] = values;
                        return;
                    case object[] objs:
                        this[objs] = values;
                        return;
                    case string slicesStr:
                        new NDArray(Storage.GetView(Slice.ParseSlices(slicesStr))).SetData(values);
                        return;
                    case null:
                        throw new ArgumentNullException($"The 1th dimension in given indices is null.");
                    //no default
                }
            }

            int ints = 0;
            int bools = 0;
            for (var i = 0; i < indicesObjects.Length; i++)
            {
                switch (indicesObjects[i])
                {
                    case NDArray _:
                    case int[] _:
                        goto _NDArrayFound;
                    case int _:
                        ints++;
                        continue;
                    case bool @bool:
                        bools++;
                        continue;
                    case string _:
                    case Slice _:
                        continue;
                    case null: throw new ArgumentNullException($"The {i}th dimension in given indices is null.");
                    default: throw new ArgumentException($"Unsupported indexing type: '{(indicesObjects[i]?.GetType()?.Name ?? "null")}'");
                }
            }

            //handle all ints
            if (ints == indicesLen)
            {
                Storage.SetData(values, indicesObjects.Cast<int>().ToArray());
                return;
            }

            //handle all booleans
            if (bools == indicesLen)
            {
                this[np.array(indicesObjects.Cast<bool>().ToArray(), false).MakeGeneric<bool>()] = values;
                return;
            }

            Slice[] slices;
            //handle regular slices
            try
            {
                slices = indicesObjects.Select(x =>
                {
                    switch (x)
                    {
                        case Slice o: return o;
                        case int o: return Slice.Index(o);
                        case string o: return new Slice(o);
                        case bool o: return o ? Slice.NewAxis : throw new NumSharpException("false bool detected"); //TODO: verify this
                        case IConvertible o: return Slice.Index((int)o.ToInt32(CultureInfo.InvariantCulture));
                        default: throw new ArgumentException($"Unsupported slice type: '{(x?.GetType()?.Name ?? "null")}'");
                    }
                }).ToArray();
            }
            catch (NumSharpException e) when (e.Message.Contains("false bool detected"))
            {
                //handle rare case of false bool
                return;
            }

            new NDArray(Storage.GetView(slices)).SetData(values);

//handle complex ndarrays indexing
            _NDArrayFound:
            var @this = this;
            var indices = new List<NDArray>();
            bool foundNewAxis = false;
            int countNewAxes = 0;
            //handle ndarray indexing
            bool hasCustomExpandedSlice = false; //use for premature slicing detection
            for (int i = 0; i < indicesLen; i++)
            {
                var idx = indicesObjects[i];
                _recuse:
                switch (idx)
                {
                    case Slice o:

                        if (o.IsEllipsis)
                        {
                            indicesObjects = ExpandEllipsis(indicesObjects, @this.ndim).ToArray();
                            //TODO: i think we need to set here indicesLen = indicesObjects.Length
                            continue;
                        }

                        if (o.IsNewAxis)
                        {
                            //TODO: whats the approach to handling a newaxis in setter, findout.
                            countNewAxes++;
                            foundNewAxis = true;
                            continue;
                        }

                        hasCustomExpandedSlice = true;
                        indices.Add(GetIndicesFromSlice(@this.Shape.dimensions, o, i - countNewAxes));
                        continue;
                    case int o:
                        indices.Add(NDArray.Scalar<int>(o));
                        continue;
                    case string o:
                        indicesObjects[i] = idx = new Slice(o);

                        goto _recuse;
                    case bool o:
                        if (o)
                        {
                            indicesObjects[i] = idx = Slice.NewAxis;
                            goto _recuse;
                        }
                        else
                            return; //false bool causes nullification of return.
                    case IConvertible o:
                        indices.Add(NDArray.Scalar<int>(o.ToInt32(CultureInfo.InvariantCulture)));
                        continue;
                    case int[] o:
                        indices.Add(np.array(o, copy: false)); //we dont copy, pinning will be freed automatically after we done indexing.
                        continue;
                    case NDArray nd:
                        if (nd.typecode == NPTypeCode.Boolean)
                        {
                            //TODO: mask only specific axis??? find a unit test to check it against.
                            throw new Exception("if (nd.typecode == NPTypeCode.Boolean)");
                        }

                        indices.Add(nd);
                        continue;
                    default: throw new ArgumentException($"Unsupported slice type: '{(idx?.GetType()?.Name ?? "null")}'");
                }
            }

            NDArray[] indicesArray = indices.ToArray();

            //handle premature slicing when the shapes cant be broadcasted together
            if (hasCustomExpandedSlice && !np.are_broadcastable(indicesArray))
            {
                var ndim = indicesObjects.Length;
                var prematureSlices = new Slice[ndim];
                var dims = @this.shape;
                for (int i = 0; i < ndim; i++)
                {
                    if (indicesObjects[i] is Slice slice)
                    {
                        prematureSlices[i] = slice;
                        //todo: we might need this in the future indicesObjects[i] = Slice.All;
                    }
                    else
                    {
                        prematureSlices[i] = Slice.All;
                    }
                }

                @this = @this[prematureSlices];

                //updated premature axes
                dims = @this.shape;
                for (int i = 0; i < ndim; i++)
                {
                    if (prematureSlices[i] != Slice.All)
                    {
                        indicesArray[i] = GetIndicesFromSlice(dims, Slice.All, i);
                    }
                }
            }

            //TODO: we can use a slice as null indice instead of expanding it, then we use PrepareIndexGetters to actually simulate that.
            SetIndices(@this, indicesArray, values);

            //TODO: this is valid code for getter, we need to impl a similar technique before passing @this.
            //if (foundNewAxis)
            //{
            //    //TODO: This is not the behavior when setting with new axis, is it even possible?
            //    var targettedAxis = indices.Count - 1;
            //    var axisOffset = this.ndim - targettedAxis;
            //    var retShape = ret.Shape;
            //    for (int i = 0; i < indicesLen; i++)
            //    {
            //        if (!(indicesObjects[i] is Slice slc) || !slc.IsNewAxis)
            //            continue;
            //
            //        var axis = Math.Max(0, Math.Min(i - axisOffset, ret.ndim));
            //        retShape = retShape.ExpandDimension(axis);
            //    }
            //
            //    ret = ret.reshape(retShape);
            //}
            //
            //return ret;
        }
示例#4
0
        /// <summary>
        ///     Join a sequence of arrays along an existing axis.
        /// </summary>
        /// <param name="axis">The axis along which the arrays will be joined. If axis is None, arrays are flattened before use. Default is 0.</param>
        /// <param name="arrays">The arrays must have the same shape, except in the dimension corresponding to axis (the first, by default).</param>
        /// <returns>The concatenated array.</returns>
        /// <remarks>https://docs.scipy.org/doc/numpy/reference/generated/numpy.concatenate.html</remarks>
        public static NDArray concatenate(NDArray[] arrays, int axis = 0)
        {
            //What we do is we have the axis which is the only dimension that is allowed to be different
            //We need to perform a check if the dimensions actually match.
            //After we have the axis ax=1 where shape is (3,ax,3) - ax is the only dimension that can vary.
            //So if we got input of (3,5,3) and (3,1,3), we create a return shape of (3,6,3).
            //We perform the assignment by iterating a slice: (:,n,:) on src and dst where dst while n of dst grows as we iterate all arrays.

            if (arrays == null)
            {
                throw new ArgumentNullException(nameof(arrays));
            }

            if (arrays.Length == 0)
            {
                throw new ArgumentException("Value cannot be an empty collection.", nameof(arrays));
            }

            if (arrays.Length == 1)
            {
                return(arrays[0]);
            }

            var first      = arrays[0];
            var firstShape = (int[])first.shape.Clone();

            while (axis < 0)
            {
                axis = first.ndim + axis; //translate negative axis
            }
            int        i, j;
            int        axisSize = 0; //accumulated shape[axis] size for return shape.
            NPTypeCode retType  = first.GetTypeCode;

            foreach (var src in arrays)
            {
                //accumulate the concatenated axis
                var shape = src.shape;
                axisSize += shape[axis];

                if (ReferenceEquals(src, first))
                {
                    continue;
                }

                var srcType = src.GetTypeCode;

                //resolve what the return type should be and should we perform casting.
                if (first.GetTypeCode != srcType)
                {
                    if (srcType.CompareTo(retType) == 1)
                    {
                        retType = srcType;
                    }
                }

                if (shape.Length != first.ndim)
                {
                    throw new IncorrectShapeException("all the input arrays must have same number of dimensions.");
                }

                //verify the shapes are equal
                for (j = 0; j < shape.Length; j++)
                {
                    if (axis == j)
                    {
                        continue;
                    }

                    if (shape[j] != firstShape[j])
                    {
                        throw new IncorrectShapeException("all the input array dimensions except for the concatenation axis must match exactly.");
                    }
                }
            }

            //prepare return shape
            firstShape[axis] = axisSize;
            var retShape = new Shape(firstShape);

            var dst         = new NDArray(retType, retShape);
            var accessorDst = new Slice[retShape.NDim];
            var accessorSrc = new Slice[retShape.NDim];

            for (i = 0; i < accessorDst.Length; i++)
            {
                accessorSrc[i] = accessorDst[i] = Slice.All;
            }

            accessorSrc[axis] = Slice.Index(0);
            accessorDst[axis] = Slice.Index(0);

            foreach (var src in arrays)
            {
                var len = src.shape[axis];
                for (i = 0; i < len; i++)
                {
                    var writeTo   = dst[accessorDst];
                    var writeFrom = src[accessorSrc];
                    MultiIterator.Assign(writeTo.Storage, writeFrom.Storage);
                    accessorSrc[axis]++;
                    accessorDst[axis]++; //increment every step
                }

                accessorSrc[axis] = Slice.Index(0); //reset src
            }

            return(dst);
        }
        /// <summary>
        /// Get n-th dimension data
        /// </summary>
        /// <param name="indices">indexes</param>
        /// <returns>NDArray</returns>
        private NDArray GetData(params int[] indices)
        {
            if (indices.Length == 0)
            {
                return(this);
            }
            if (Storage.SupportsSpan)
            {
                Shape s1 = shape.Skip(indices.Length).ToArray();
                var   nd = new NDArray(dtype, s1);
                //nd.Storage.Slice = new Slice($"{}");
                switch (Type.GetTypeCode(dtype))
                {
                case TypeCode.Boolean:
                    nd.Array = Storage.GetSpanData <bool>(slice, indices).ToArray();
                    break;

                case TypeCode.Byte:
                    nd.Array = Storage.GetSpanData <byte>(slice, indices).ToArray();
                    break;

                case TypeCode.Int16:
                    nd.Array = Storage.GetSpanData <short>(slice, indices).ToArray();
                    break;

                case TypeCode.Int32:
                    nd.Array = Storage.GetSpanData <int>(slice, indices).ToArray();
                    break;

                case TypeCode.Int64:
                    nd.Array = Storage.GetSpanData <long>(slice, indices).ToArray();
                    break;

                case TypeCode.Single:
                    nd.Array = Storage.GetSpanData <float>(slice, indices).ToArray();
                    break;

                case TypeCode.Double:
                    nd.Array = Storage.GetSpanData <double>(slice, indices).ToArray();
                    break;

                case TypeCode.Decimal:
                    nd.Array = Storage.GetSpanData <decimal>(slice, indices).ToArray();
                    break;

                case TypeCode.String:
                    nd.Array = Storage.GetSpanData <string>(slice, indices).ToArray();
                    break;

                default:
                    return(Storage.GetSpanData <NDArray>(slice, indices).ToArray()[0]);
                }
                return(nd);
            }

            if (indices.Length < Storage.Shape.NDim)
            {
                // a slice was requested
                return(this[indices.Select(i => Slice.Index(i)).ToArray()]);
            }
            else if (indices.Length == Storage.Shape.NDim)
            {
                // a scalar was indexed
                var nd = new NDArray(this.dtype, new Shape());
                switch (Type.GetTypeCode(dtype))
                {
                case TypeCode.Boolean:
                    nd.Array = new [] { Storage.GetData <bool>(indices) };
                    break;

                case TypeCode.Byte:
                    nd.Array = new[] { Storage.GetData <byte>(indices) };
                    break;

                case TypeCode.Int16:
                    nd.Array = new[] { Storage.GetData <short>(indices) };
                    break;

                case TypeCode.Int32:
                    nd.Array = new[] { Storage.GetData <int>(indices) };
                    break;

                case TypeCode.Int64:
                    nd.Array = new[] { Storage.GetData <long>(indices) };
                    break;

                case TypeCode.Single:
                    nd.Array = new[] { Storage.GetData <float>(indices) };
                    break;

                case TypeCode.Double:
                    nd.Array = new[] { Storage.GetData <double>(indices) };
                    break;

                case TypeCode.Decimal:
                    nd.Array = new[] { Storage.GetData <decimal>(indices) };
                    break;

                case TypeCode.String:
                    nd.Array = new[] { Storage.GetData <string>(indices) };
                    break;

                default:
                    return(Storage.GetData <NDArray>(indices));
                }
                return(nd);
            }
            else
            {
                throw new ArgumentException("Too many index dimensions for shape " + Storage.Shape);
            }
        }