using System; using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Runtime.InteropServices; using System.Text; using Numpy.Models; using Python.Runtime; namespace Numpy { public partial class NDarray : PythonObject { // this is needed for constructors in NDarray protected NDarray() : base() { } // these are manual overrides of functions or properties that can not be automatically generated public NDarray(PyObject pyobj) : base(pyobj) { } public NDarray(NDarray t) : base((PyObject) t.PyObject) { } /// /// Creates an array from an unmanaged (or fixed) memory pointer without copying. /// /// Pointer to a block of unmanaged memory or to a block of pinned managed memory /// The length of the block of memory in bytes /// The data type of the resulting NDarray public NDarray(IntPtr dataPtr, long dataLength, Dtype dtype) : base() { // adapted from https://pastebin.com/4hANmBNy // thanks to Eli Belash var g = (Numpy.ctypes.dynamic_self.c_uint8 * dataLength).from_address(dataPtr.ToInt64()); self = np.dynamic_self.frombuffer(g, dtype.PyObject, -1); } /// /// Returns a copy of the array data /// public T[] GetData() { if (!PyObject.flags.c_contiguous) return np.ascontiguousarray(this).GetData(); long ptr = PyObject.ctypes.data; int size = PyObject.size; object array = null; if (typeof(T) == typeof(byte)) array = new byte[size]; else if (typeof(T) == typeof(short)) array = new short[size]; else if (typeof(T) == typeof(int)) array = new int[size]; else if (typeof(T) == typeof(long)) array = new long[size]; else if (typeof(T) == typeof(float)) array = new float[size]; else if (typeof(T) == typeof(double)) array = new double[size]; else if (typeof(T) == typeof(bool)) array = new byte[size]; else if (typeof(T) == typeof(Complex)) array = new Complex[size]; else throw new InvalidOperationException( "Can not copy the data with data type due to limitations of Marshal.Copy: " + typeof(T).Name); switch (array) { case byte[] a: Marshal.Copy(new IntPtr(ptr), a, 0, a.Length); break; case short[] a: Marshal.Copy(new IntPtr(ptr), a, 0, a.Length); break; case int[] a: Marshal.Copy(new IntPtr(ptr), a, 0, a.Length); break; case long[] a: Marshal.Copy(new IntPtr(ptr), a, 0, a.Length); break; case float[] a: Marshal.Copy(new IntPtr(ptr), a, 0, a.Length); break; case double[] a: Marshal.Copy(new IntPtr(ptr), a, 0, a.Length); break; case Complex[] a: var real = this.real.GetData(); var imag = this.imag.GetData(); for(int i =0; i true, 0 => false if (typeof(T) == typeof(bool)) return (T[])(object)((byte[])array).Select(x=>x>0).ToArray(); return (T[]) array; } /// /// Information about the memory layout of the array. /// public Flags flags => new Flags(self.GetAttr("flags")); // TODO: implement Flags /// /// Tuple of array dimensions. /// public Shape shape => new Shape( self.GetAttr("shape").As()); /// /// Tuple of bytes to step in each dimension when traversing an array. /// public int[] strides => self.GetAttr("strides").As(); /// /// Number of array dimensions. /// public int ndim => self.GetAttr("ndim").As(); /// /// Python buffer object pointing to the start of the array’s data. /// public PyObject data => self.GetAttr("data"); /// /// Number of elements in the array. /// public int size => self.GetAttr("size").As(); /// /// Length of one array element in bytes. /// public int itemsize => self.GetAttr("itemsize").As(); /// /// Total bytes consumed by the elements of the array. /// public int nbytes => self.GetAttr("nbytes").As(); /// /// Base object if memory is from some other object. /// public NDarray @base { get { PyObject base_obj = self.GetAttr("base"); if (base_obj.IsNone()) return null; return new NDarray(base_obj); } } /// /// Data-type of the array’s elements. /// public Dtype dtype => new Dtype(self.GetAttr("dtype")); /// /// Same as self.transpose(), except that self is returned if self.ndim < 2. /// public NDarray T => new NDarray(self.GetAttr("T")); ///// ///// The real part of the array. ///// //public NDarray real => new NDarray(self.GetAttr("real")); ///// ///// The imaginary part of the array. ///// //public NDarray imag => new NDarray(self.GetAttr("imag")); /// /// A 1-D iterator over the array. /// public PyObject flat => self.GetAttr("flat"); // todo: wrap and support usecases /// /// An object to simplify the interaction of the array with the ctypes module. /// public PyObject ctypes => self.GetAttr("ctypes"); // TODO: wrap ctypes /// /// Length of the array (same as size) /// public int len => self.InvokeMethod("__len__").As(); /// /// Insert scalar into an array (scalar is cast to array’s dtype, if possible) /// /// There must be at least 1 argument, and define the last argument /// as item. Then, a.itemset(*args) is equivalent to but faster /// than a[args] = item. The item should be a scalar value and args /// must select a single item in the array a. /// /// Notes /// /// Compared to indexing syntax, itemset provides some speed increase /// for placing a scalar into a particular location in an ndarray, /// if you must do this. However, generally this is discouraged: /// among other problems, it complicates the appearance of the code. /// Also, when using itemset (and item) inside a loop, be sure /// to assign the methods to a local variable to avoid the attribute /// look-up at each loop iteration. /// /// /// If one argument: a scalar, only used in case a is of size 1. /// If two arguments: the last argument is the value to be set /// and must be a scalar, the first argument specifies a single array /// element location. It is either an int or a tuple. /// public void itemset(params object[] args) { var pyargs = ToTuple(args); var kwargs = new PyDict(); dynamic py = self.InvokeMethod("itemset", pyargs, kwargs); } /// /// Construct Python bytes containing the raw data bytes in the array. /// /// Constructs Python bytes showing a copy of the raw contents of /// data memory. The bytes object can be produced in either ‘C’ or ‘Fortran’, /// or ‘Any’ order (the default is ‘C’-order). ‘Any’ order means C-order /// unless the F_CONTIGUOUS flag in the array is set, in which case it /// means ‘Fortran’ order. /// /// This function is a compatibility alias for tobytes. Despite its name it returns bytes not strings. /// /// /// Order of the data for multidimensional arrays: /// C, Fortran, or the same as for the original array. /// /// /// Python bytes exhibiting a copy of a’s raw data. /// public byte[] tostring(string order = null) { return tobytes(); } /// /// Construct Python bytes containing the raw data bytes in the array. /// /// Constructs Python bytes showing a copy of the raw contents of /// data memory. The bytes object can be produced in either ‘C’ or ‘Fortran’, /// or ‘Any’ order (the default is ‘C’-order). ‘Any’ order means C-order /// unless the F_CONTIGUOUS flag in the array is set, in which case it /// means ‘Fortran’ order. /// /// /// Order of the data for multidimensional arrays: /// C, Fortran, or the same as for the original array. /// /// /// Python bytes exhibiting a copy of a’s raw data. /// public byte[] tobytes(string order = null) { throw new NotImplementedException("TODO: this needs to be implemented with Marshal.Copy"); var pyargs = ToTuple(new object[] { }); var kwargs = new PyDict(); if (order != null) kwargs["order"] = ToPython(order); dynamic py = self.InvokeMethod("tobytes", pyargs, kwargs); return ToCsharp(py); } /// /// New view of array with the same data. /// /// Notes /// /// a.view() is used two different ways: /// /// a.view(some_dtype) or a.view(dtype=some_dtype) constructs a view /// of the array’s memory with a different data-type. This can cause a /// reinterpretation of the bytes of memory. /// /// a.view(ndarray_subclass) or a.view(type=ndarray_subclass) just /// returns an instance of ndarray_subclass that looks at the same array /// (same shape, dtype, etc.) This does not cause a reinterpretation of the /// memory. /// /// For a.view(some_dtype), if some_dtype has a different number of /// bytes per entry than the previous dtype (for example, converting a /// regular array to a structured array), then the behavior of the view /// cannot be predicted just from the superficial appearance of a (shown /// by print(a)). It also depends on exactly how a is stored in /// memory. Therefore if a is C-ordered versus fortran-ordered, versus /// defined as a slice or transpose, etc., the view may give different /// results. /// /// /// Data-type descriptor of the returned view, e.g., float32 or int16. The /// default, None, results in the view having the same data-type as a. /// This argument can also be specified as an ndarray sub-class, which /// then specifies the type of the returned object (this is equivalent to /// setting the type parameter). /// /// /// Type of the returned view, e.g., ndarray or matrix. Again, the /// default None results in type preservation. /// public void view(Dtype dtype = null, Type type = null) { throw new NotImplementedException("Get python type 'ndarray' and 'matrix' and substitute them for the given .NET type"); var pyargs = ToTuple(new object[] { }); var kwargs = new PyDict(); if (dtype != null) kwargs["dtype"] = ToPython(dtype); if (type != null) kwargs["type"] = ToPython(type); dynamic py = self.InvokeMethod("view", pyargs, kwargs); } /// /// Change shape and size of array in-place. /// /// Notes /// /// This reallocates space for the data area if necessary. /// /// Only contiguous arrays (data elements consecutive in memory) can be /// resized. /// /// The purpose of the reference count check is to make sure you /// do not use this array as a buffer for another Python object and then /// reallocate the memory. However, reference counts can increase in /// other ways so if you are sure that you have not shared the memory /// for this array with another Python object, then you may safely set /// refcheck to False. /// /// /// Shape of resized array. /// /// /// If False, reference count will not be checked. Default is True. /// public void resize(Shape new_shape, bool? refcheck = null) { var pyargs = ToTuple(new object[] { new_shape, }); var kwargs = new PyDict(); if (refcheck != null) kwargs["refcheck"] = ToPython(refcheck); dynamic py = self.InvokeMethod("resize", pyargs, kwargs); } /// /// Gives a new shape to an array without changing its data. /// /// Notes /// /// It is not always possible to change the shape of an array without /// copying the data. If you want an error to be raised when the data is copied, /// you should assign the new shape to the shape attribute of the array: /// /// The order keyword gives the index ordering both for fetching the values /// from a, and then placing the values into the output array. /// For example, let’s say you have an array: /// /// You can think of reshaping as first raveling the array (using the given /// index order), then inserting the elements from the raveled array into the /// new array using the same kind of index ordering as was used for the /// raveling. /// /// /// The new shape should be compatible with the original shape. If /// an integer, then the result will be a 1-D array of that length. /// One shape dimension can be -1. In this case, the value is /// inferred from the length of the array and remaining dimensions. /// /// /// This will be a new view object if possible; otherwise, it will /// be a copy. Note there is no guarantee of the memory layout (C- or /// Fortran- contiguous) of the returned array. /// public NDarray reshape(params int[] newshape) { //auto-generated code, do not change var @this = this; return np.reshape(@this, new Shape(newshape)); } /// /// returns the 'array([ .... ])'-representation known from the console /// public string repr => self.InvokeMethod("__repr__").As(); /// /// returns the '[ .... ]'-representation /// public string str => self.InvokeMethod("__str__").As(); public NDarray this[string slicing_notation] { get { var tuple=new PyTuple(Slice.ParseSlices(slicing_notation).Select(s => { if (s.IsIndex) return new PyInt(s.Start.Value); else return s.ToPython(); }).ToArray()); return new NDarray(this.PyObject[tuple]); } set { var tuple = new PyTuple(Slice.ParseSlices(slicing_notation).Select(s => { if (s.IsIndex) return new PyInt(s.Start.Value); else return s.ToPython(); }).ToArray()); self.SetItem(tuple, ToPython(value)); } } public NDarray this[params int[] coords] { get { var tuple = ToTuple(coords); return new NDarray(this.PyObject[tuple]); } set { var tuple = ToTuple(coords); self.SetItem(tuple, ToPython(value)); } } public NDarray this[params NDarray[] indices] { get { var tuple = new PyTuple(indices.Select(a => (PyObject)a.PyObject).ToArray()); return new NDarray(this.PyObject[tuple]); } set { var tuple = new PyTuple(indices.Select(a => (PyObject)a.PyObject).ToArray()); self.SetItem(tuple, ToPython(value)); } } public NDarray this[params object[] arrays_slices_or_indices] { get { var pyobjs = arrays_slices_or_indices.Select(x => { switch (x) { case int i: return new PyInt(i); case NDarray a: return a.PyObject; case string s: return new Slice(s).ToPython(); default: return ToPython(x); } }).ToArray(); var tuple = new PyTuple(pyobjs); return new NDarray(this.PyObject[tuple]); } set { var pyobjs = arrays_slices_or_indices.Select(x => { switch (x) { case int i: return new PyInt(i); case NDarray a: return a.PyObject; case string s: return new Slice(s).ToPython(); default: return ToPython(x); } }).ToArray(); var tuple = new PyTuple(pyobjs); self.SetItem(tuple, ToPython(value)); } } /// /// Convert an array of size 1 to its scalar equivalent. /// /// /// Scalar representation of a. The output data type is the same type /// returned by the input’s item method. /// public T asscalar() { return np.asscalar(this); } public override bool Equals(object obj) { if (obj == null) return false; var array = obj as NDarray; if (!object.ReferenceEquals(array, null)) return np.array_equal(this, array); return base.Equals(obj); } public NDarray real { get { dynamic py = self.GetAttr("real"); return ToCsharp(py); } set { self.SetAttr("real", value.PyObject); } } public NDarray imag { get { dynamic py = self.GetAttr("imag"); return ToCsharp(py); } set { self.SetAttr("imag", value.PyObject); } } /// /// Returns a view of the array with axes transposed.

/// /// For a 1-D array, this has no effect.

/// (To change between column and /// row vectors, first cast the 1-D array into a matrix object.) /// For a 2-D array, this is the usual matrix transpose.

/// /// For an n-D array, if axes are given, their order indicates how the /// axes are permuted (see Examples).

/// If axes are not provided and /// a.shape = (i[0], i[1], ... i[n-2], i[n-1]), then /// a.transpose().shape = (i[n-1], i[n-2], ... i[1], i[0]). ///
/// /// View of a, with axes suitably permuted. /// public NDarray transpose(params int[] axes) { //auto-generated code, do not change var __self__ = self; var pyargs = ToTuple(new object[] { ToPython(axes), }); //if (axes != null) kwargs["axes"] = ToPython(axes); dynamic py = __self__.InvokeMethod("transpose", pyargs); return ToCsharp(py); } } public class NDarray : NDarray { public NDarray(NDarray t) : base(t) { } public NDarray(PyObject pyobject) : base(pyobject) { } public NDarray(T[] array) : base() { var nd = np.array(array); self = nd.self; } /// /// Returns a copy of the array data /// public T[] GetData() { return base.GetData(); } public new NDarray this[string slicing_notation] { get { var tuple = new PyTuple(Slice.ParseSlices(slicing_notation).Select(s => { if (s.IsIndex) return new PyInt(s.Start.Value); else return s.ToPython(); }).ToArray()); return new NDarray(this.PyObject[tuple]); } set { var tuple = new PyTuple(Slice.ParseSlices(slicing_notation).Select(s => { if (s.IsIndex) return new PyInt(s.Start.Value); else return s.ToPython(); }).ToArray()); self.SetItem(tuple, ToPython(value)); } } public new NDarray this[params int[] coords] { get { var tuple = ToTuple(coords); return new NDarray(this.PyObject[tuple]); } set { var tuple = ToTuple(coords); self.SetItem(tuple, ToPython(value)); } } public new NDarray this[params NDarray[] indices] { get { var tuple = new PyTuple(indices.Select(a => (PyObject)a.PyObject).ToArray()); return new NDarray(this.PyObject[tuple]); } set { var tuple = new PyTuple(indices.Select(a => (PyObject)a.PyObject).ToArray()); self.SetItem(tuple, ToPython(value)); } } public new NDarray this[params object[] arrays_slices_or_indices] { get { var pyobjs = arrays_slices_or_indices.Select(x => { switch (x) { case int i: return new PyInt(i); case NDarray a: return a.PyObject; case string s: return new Slice(s).ToPython(); default: return ToPython(x); } }).ToArray(); var tuple = new PyTuple(pyobjs); return new NDarray(this.PyObject[tuple]); } set { var pyobjs = arrays_slices_or_indices.Select(x => { switch (x) { case int i: return new PyInt(i); case NDarray a: return a.PyObject; case string s: return new Slice(s).ToPython(); default: return ToPython(x); } }).ToArray(); var tuple = new PyTuple(pyobjs); self.SetItem(tuple, ToPython(value)); } } } }