add cond function in linalg package

This commit is contained in:
wyq 2023-06-28 00:10:47 +08:00
parent 8c32998bec
commit 7efed43099
16 changed files with 1392 additions and 86 deletions

View File

@ -5,7 +5,7 @@ MeteoInfo: GIS and scientific computation environment for meteorological communi
[![DOI](https://zenodo.org/badge/172686439.svg)](https://zenodo.org/badge/latestdoi/172686439)
[![Project Map](https://sourcespy.com/shield.svg)](https://sourcespy.com/github/meteoinfometeoinfo/)
**MeteoInfo** is an intergrated framework for GIS application (**MeteoInfoMap**), scientific computation and
**MeteoInfo** is an integrated framework for GIS application (**MeteoInfoMap**), scientific computation and
visualization environment (**MeteoInfoLab**), especially for meteorological community.
**MeteoInfoMap** is a GIS application which enables the user to visualize and analyze
@ -18,8 +18,6 @@ ability of multiple dimensional array calculation and 2D/3D plotting.
![MeteoInfoLab GUI](images/MeteoInfoLab.PNG)
**MeteoInfoLib** is a Java library providing main functions of MeteoInfoMap and MeteoInfoLab.
It requires that Java 8 or greater be installed on your computer. See the
http://www.java.com website for a free download of Java if you do not have it
already installed.

View File

@ -1,36 +1,36 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<MeteoInfo File="milconfig.xml" Type="configurefile">
<Path OpenPath="D:\Working\MIScript\Jython\mis\plot_types\patch">
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\satellite"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\satellite\omi"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\wind"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\pie"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io\micaps"/>
<Path OpenPath="D:\Working\MIScript\Jython\mis\common_math\linalg">
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\map\topology"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\array"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\plot"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io\radar"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\map"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\map\projection"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\patch"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\3d"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl\city"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\common_math"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\array"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\common_math\linalg"/>
</Path>
<File>
<OpenedFiles>
<OpenedFile File="D:\MyProgram\java\MeteoInfoDev\toolbox\meteoview3d\_reload.py"/>
<OpenedFile File="D:\MyProgram\java\MeteoInfoDev\toolbox\meteoview3d\mainGUI.py"/>
<OpenedFile File="D:\Working\MIScript\Jython\mis\io\micaps\mdfs_dataframe_2.py"/>
<OpenedFile File="D:\Working\MIScript\Jython\mis\plot_types\patch\wind_circle_2.py"/>
<OpenedFile File="D:\Working\MIScript\Jython\mis\plot_types\patch\wind_circle_1.py"/>
<OpenedFile File="D:\Working\MIScript\Jython\mis\common_math\linalg\svd.py"/>
<OpenedFile File="D:\Working\MIScript\Jython\mis\common_math\linalg\cond.py"/>
<OpenedFile File="D:\Working\MIScript\Jython\mis\common_math\linalg\norm_1.py"/>
</OpenedFiles>
<RecentFiles>
<RecentFile File="D:\MyProgram\java\MeteoInfoDev\toolbox\meteoview3d\_reload.py"/>
<RecentFile File="D:\MyProgram\java\MeteoInfoDev\toolbox\meteoview3d\mainGUI.py"/>
<RecentFile File="D:\Working\MIScript\Jython\mis\io\micaps\mdfs_dataframe_2.py"/>
<RecentFile File="D:\Working\MIScript\Jython\mis\plot_types\patch\wind_circle_2.py"/>
<RecentFile File="D:\Working\MIScript\Jython\mis\plot_types\patch\wind_circle_1.py"/>
<RecentFile File="D:\Working\MIScript\Jython\mis\common_math\linalg\svd.py"/>
<RecentFile File="D:\Working\MIScript\Jython\mis\common_math\linalg\cond.py"/>
<RecentFile File="D:\Working\MIScript\Jython\mis\common_math\linalg\norm_1.py"/>
</RecentFiles>
</File>
<Font>

View File

@ -698,6 +698,16 @@ class NDArray(object):
else:
ArrayMath.setImage(self._array, val)
def conj(self):
"""
Return the complex conjugate, element-wise.
The complex conjugate of a complex number is obtained by changing the sign of its imaginary part.
:return: (*array*) Complex conjugate array.
"""
return NDArray(ArrayMath.conj(self._array))
def min(self, axis=None):
"""
Get minimum value along an axis.
@ -707,7 +717,12 @@ class NDArray(object):
:returns: Minimum values.
"""
if isinstance(axis, (list, tuple)) and len(axis) == self.ndim:
if self.ndim == 1:
axis = None
elif isinstance(axis, (list, tuple)):
if len(axis) == 1:
axis = axis[0]
elif len(axis) == self.ndim:
axis = None
if axis is None:
@ -768,7 +783,12 @@ class NDArray(object):
:returns: Maximum values.
"""
if isinstance(axis, (list, tuple)) and len(axis) == self.ndim:
if self.ndim == 1:
axis = None
elif isinstance(axis, (list, tuple)):
if len(axis) == 1:
axis = axis[0]
elif len(axis) == self.ndim:
axis = None
if axis is None:
@ -787,12 +807,27 @@ class NDArray(object):
returns: (*array_like*) Sum result
"""
if isinstance(axis, (list, tuple)) and len(axis) == self.ndim:
if self.ndim == 1:
axis = None
elif isinstance(axis, (list, tuple)):
if len(axis) == 1:
axis = axis[0]
elif len(axis) == self.ndim:
axis = None
if axis is None:
return ArrayMath.sum(self._array)
else:
if isinstance(axis, (list, tuple)):
aa = []
for a in axis:
if a < 0:
a = self.ndim + a
aa.append(a)
axis = aa
else:
if axis < 0:
axis = self.ndim + axis
r = ArrayMath.sum(self._array, axis)
return NDArray(r)

View File

@ -138,15 +138,15 @@ def sum(x, axis=None):
r = ArrayMath.sum(x.asarray())
return r
else:
r = ArrayMath.sum(x.asarray(), axis)
if type(x) is NDArray:
return NDArray(r)
else:
r = x.sum(axis)
if isinstance(x, DimArray):
dims = []
for i in range(0, x.ndim):
if i != axis:
dims.append(x.dims[i])
return DimArray(NDArray(r), dims, x.fill_value, x.proj)
return DimArray(r, dims, x.fill_value, x.proj)
else:
return r
def prod(x, axis=None):

View File

@ -37,7 +37,7 @@ newaxis = None
__all__ = [
'pi','e','inf','nan','acos','abs','all','allclose','any','arange','arange1',
'argmin','argmax','argsort','array','array_split','asanyarray','asarray','asgridarray',
'argmin','argmax','argsort','array','array_split','amax','amin','asanyarray','asarray','asgridarray',
'asgriddata','arcsin','asin','asmiarray','asstationdata','atleast_1d','atleast_2d','arctan','atan',
'arctan2','atan2','ave_month','average','histogram','broadcast_to','cdiff','ceil',
'concatenate','conj','conjugate','corrcoef','cos','cosh','cylinder','degrees','delete','delnan','diag','diff',
@ -1526,6 +1526,19 @@ def min(a, axis=None):
a = array(a)
return a.min(axis)
def amin(a, axis=None):
"""
Return the minimum of an array or minimum along an axis.
`amin` is an alias of `~numeric.min`.
See Also
--------
min : alias of this function
NDArray.min : equivalent method
"""
return min(a, axis)
def max(a, axis=None):
"""
Returns the maximum values along an axis.
@ -1541,6 +1554,19 @@ def max(a, axis=None):
a = array(a)
return a.max(axis)
def amax(a, axis=None):
"""
Return the maximum of an array or maximum along an axis.
`amax` is an alias of `~numeric.max`.
See Also
--------
max : alias of this function
NDArray.max : equivalent method
"""
return max(a, axis)
def argmin(a, axis=None):
"""
Returns the indices of the minimum values along an axis.

View File

@ -12,7 +12,7 @@ from org.meteoinfo.math.stats import StatsUtil
from .. import core as np
__all__ = ['solve', 'cholesky', 'det', 'lu', 'qr', 'svd', 'eig', 'inv', 'lstsq', 'slogdet',
__all__ = ['solve', 'cholesky', 'cond', 'det', 'lu', 'qr', 'svd', 'eig', 'inv', 'lstsq', 'slogdet',
'solve_triangular', 'norm', 'pinv']
@ -177,7 +177,7 @@ def qr(a):
return q, r
def svd(a, full_matrices=True):
def svd(a, full_matrices=True, compute_uv=True):
"""
Singular Value Decomposition.
@ -193,9 +193,13 @@ def svd(a, full_matrices=True):
full_matrices: bool, optional
If True (default), u and vh have the shapes (..., M, M) and (..., N, N), respectively.
Otherwise, the shapes are (..., M, K) and (..., K, N), respectively, where K = min(M, N).
compute_uv:bool, optional
Whether to compute u and vh in addition to s. True by default.
Returns
-------
When `compute_uv` is True, the result is a namedtuple with the following attribute names:
U : ndarray
Unitary matrix having left singular vectors as columns.
Of shape ``(M,K)``.
@ -208,10 +212,13 @@ def svd(a, full_matrices=True):
"""
r = LinalgUtil.svd(a.asarray(), full_matrices)
# r = LinalgUtil.svd_EJML(a.asarray())
U = np.NDArray(r[0])
s = np.NDArray(r[1])
if compute_uv:
U = np.NDArray(r[0])
Vh = np.NDArray(r[2])
return U, s, Vh
else:
return s
def eig(a):
@ -326,6 +333,34 @@ def slogdet(a):
return r[0], r[1]
def _multi_svd_norm(x, row_axis, col_axis, op):
"""Compute a function of the singular values of the 2-D matrices in `x`.
This is a private utility function used by `numpy.linalg.norm()`.
Parameters
----------
x : ndarray
row_axis, col_axis : int
The axes of `x` that hold the 2-D matrices.
op : callable
This should be either amin or `amax` or `sum`.
Returns
-------
result : float or ndarray
If `x` is 2-D, the return values is a float.
Otherwise, it is an array with ``x.ndim - 2`` dimensions.
The return values are either the minimum or maximum or sum of the
singular values of the matrices, depending on whether `op`
is `amin` or `amax` or `sum`.
"""
y = np.moveaxis(x, (row_axis, col_axis), (-2, -1))
result = op(svd(y, compute_uv=False), axis=-1)
return result
def norm(x, ord=None, axis=None, keepdims=False):
"""
Matrix or vector norm.
@ -407,50 +442,144 @@ def norm(x, ord=None, axis=None, keepdims=False):
# None of the str-type keywords for ord ('fro', 'nuc')
# are valid for vectors
elif isinstance(ord, str):
raise ValueError("Invalid norm order '{ord}' for vectors".format(ord))
raise ValueError("Invalid norm order {} for vectors".format(ord))
else:
absx = np.abs(x)
absx **= ord
ret = absx.sum(axis=axis)
ret **= np.reciprocal(ord)
ret **= (1. / ord)
return ret
# elif len(axis) == 2:
# row_axis, col_axis = axis
# row_axis = np.normalize_axis_index(row_axis, nd)
# col_axis = np.normalize_axis_index(col_axis, nd)
# if row_axis == col_axis:
# raise ValueError('Duplicate axes given.')
# if ord == 2:
# ret = _multi_svd_norm(x, row_axis, col_axis, amax)
# elif ord == -2:
# ret = _multi_svd_norm(x, row_axis, col_axis, amin)
# elif ord == 1:
# if col_axis > row_axis:
# col_axis -= 1
# ret = add.reduce(np.abs(x), axis=row_axis).max(axis=col_axis)
# elif ord == np.inf:
# if row_axis > col_axis:
# row_axis -= 1
# ret = add.reduce(np.abs(x), axis=col_axis).max(axis=row_axis)
# elif ord == -1:
# if col_axis > row_axis:
# col_axis -= 1
# ret = add.reduce(np.abs(x), axis=row_axis).min(axis=col_axis)
# elif ord == -np.inf:
# if row_axis > col_axis:
# row_axis -= 1
# ret = add.reduce(np.abs(x), axis=col_axis).min(axis=row_axis)
# elif ord in [None, 'fro', 'f']:
# ret = np.sqrt(add.reduce((x.conj() * x).real, axis=axis))
# elif ord == 'nuc':
# ret = _multi_svd_norm(x, row_axis, col_axis, sum)
# else:
# raise ValueError("Invalid norm order for matrices.")
# if keepdims:
# ret_shape = list(x.shape)
# ret_shape[axis[0]] = 1
# ret_shape[axis[1]] = 1
# ret = ret.reshape(ret_shape)
# return ret
# else:
# raise ValueError("Improper number of dimensions to norm.")
elif len(axis) == 2:
row_axis, col_axis = axis
row_axis = np.normalize_axis_index(row_axis, nd)
col_axis = np.normalize_axis_index(col_axis, nd)
if row_axis == col_axis:
raise ValueError('Duplicate axes given.')
if ord == 2:
ret = _multi_svd_norm(x, row_axis, col_axis, np.amax)
elif ord == -2:
ret = _multi_svd_norm(x, row_axis, col_axis, np.amin)
elif ord == 1:
if col_axis > row_axis:
col_axis -= 1
ret = np.sum(np.abs(x), axis=row_axis).max(axis=col_axis)
elif ord == np.inf:
if row_axis > col_axis:
row_axis -= 1
ret = np.sum(np.abs(x), axis=col_axis).max(axis=row_axis)
elif ord == -1:
if col_axis > row_axis:
col_axis -= 1
ret = np.sum(np.abs(x), axis=row_axis).min(axis=col_axis)
elif ord == -np.inf:
if row_axis > col_axis:
row_axis -= 1
ret = np.sum(np.abs(x), axis=col_axis).min(axis=row_axis)
elif ord in [None, 'fro', 'f']:
ret = np.sqrt(np.sum((x.conj() * x).real, axis=axis))
elif ord == 'nuc':
ret = _multi_svd_norm(x, row_axis, col_axis, np.sum)
else:
raise ValueError("Invalid norm order for matrices.")
if keepdims:
ret_shape = list(x.shape)
ret_shape[axis[0]] = 1
ret_shape[axis[1]] = 1
ret = ret.reshape(ret_shape)
return ret
else:
raise ValueError("Improper number of dimensions to norm.")
def cond(x, p=None):
"""
Compute the condition number of a matrix.
This function is capable of returning the condition number using
one of seven different norms, depending on the value of `p` (see
Parameters below).
Parameters
----------
x : (..., M, N) array_like
The matrix whose condition number is sought.
p : {None, 1, -1, 2, -2, inf, -inf, 'fro'}, optional
Order of the norm used in the condition number computation:
===== ============================
p norm for matrices
===== ============================
None 2-norm, computed directly using the ``SVD``
'fro' Frobenius norm
inf max(sum(abs(x), axis=1))
-inf min(sum(abs(x), axis=1))
1 max(sum(abs(x), axis=0))
-1 min(sum(abs(x), axis=0))
2 2-norm (largest sing. value)
-2 smallest singular value
===== ============================
inf means the `numpy.inf` object, and the Frobenius norm is
the root-of-sum-of-squares norm.
Returns
-------
c : {float, inf}
The condition number of the matrix. May be infinite.
See Also
--------
numpy.linalg.norm
Notes
-----
The condition number of `x` is defined as the norm of `x` times the
norm of the inverse of `x` [1]_; the norm can be the usual L2-norm
(root-of-sum-of-squares) or one of a number of other matrix norms.
References
----------
.. [1] G. Strang, *Linear Algebra and Its Applications*, Orlando, FL,
Academic Press, Inc., 1980, pg. 285.
Examples
--------
>>> from mipylib.numeric import linalg as LA
>>> a = np.array([[1, 0, -1], [0, 1, 0], [1, 0, 1]])
>>> a
array([[ 1, 0, -1],
[ 0, 1, 0],
[ 1, 0, 1]])
>>> LA.cond(a)
1.4142135623730951
>>> LA.cond(a, 'fro')
3.1622776601683795
>>> LA.cond(a, np.inf)
2.0
>>> LA.cond(a, -np.inf)
1.0
>>> LA.cond(a, 1)
2.0
>>> LA.cond(a, -1)
1.0
>>> LA.cond(a, 2)
1.4142135623730951
>>> LA.cond(a, -2)
0.70710678118654746 # may vary
>>> min(LA.svd(a, compute_uv=False))*min(LA.svd(LA.inv(a), compute_uv=False))
0.70710678118654746 # may vary
"""
x = np.asarray(x) # in case we have a matrix
if p is None or p == 2 or p == -2:
s = svd(x, compute_uv=False)
if p == -2:
r = s[..., -1] / s[..., 0]
else:
r = s[..., 0] / s[..., -1]
else:
# Call inv(x) ignoring errors. The result array will
# contain nans in the entries where inversion failed.
invx = inv(x)
r = norm(x, p, axis=(-2, -1)) * norm(invx, p, axis=(-2, -1))
return r

View File

@ -1501,16 +1501,19 @@ class Axes3DGL(Axes3D):
def isosurface(self, *args, **kwargs):
"""
creates a three-dimensional isosurface plot
creates a three-dimensional isosurface plot.
:param x: (*array_like*) Optional. X coordinate array.
:param y: (*array_like*) Optional. Y coordinate array.
:param z: (*array_like*) Optional. Z coordinate array.
:param data: (*array_like*) 3D data array.
:param cmap: (*string*) Color map string.
:param nthread: (*int*) Thread number. Default is 4.
:param data: (*array_like*) Volume data array.
:param isovalue: (*float*) Specified isosurface value.
:param cdata: (*array_like*) Optional. Volume color data array.
:param facecolor: (*color*) Optional. Color map string.
:param cmap: (*string*) Optional. Color map string.
:param nthread: (*int*) Optional. Thread number. Default is 4.
:returns: 3D Mesh graphic
:returns: 3D Mesh graphic.
"""
if len(args) <= 3:
x = args[0].dimvalue(2)

View File

@ -53,6 +53,16 @@
<artifactId>openblas-platform</artifactId>
<version>0.3.10-1.5.4</version>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>mkl-platform</artifactId>
<version>2020.3-1.5.4</version>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>mkl-platform-redist</artifactId>
<version>2020.3-1.5.4</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>

View File

@ -53,8 +53,8 @@ public interface BLAS {
Logger logger = Logger.getLogger("BLAS.class");
try {
Class<?> clazz = Class.forName("smile.math.blas.mkl.MKL");
logger.info("smile-mkl module is available.");
Class<?> clazz = Class.forName("org.meteoinfo.math.blas.mkl.MKL");
logger.info("mkl module is available.");
return (BLAS) clazz.getDeclaredConstructor().newInstance();
} catch (Exception e) {
logger.info(String.format("Failed to create MKL instance: %s", e));

View File

@ -53,8 +53,8 @@ public interface LAPACK {
Logger logger = Logger.getLogger("LAPACK.class");
try {
Class<?> clazz = Class.forName("smile.math.blas.mkl.MKL");
logger.info("smile-mkl module is available.");
Class<?> clazz = Class.forName("org.meteoinfo.math.blas.mkl.MKL");
logger.info("mkl module is available.");
return (LAPACK) clazz.getDeclaredConstructor().newInstance();
} catch (Exception e) {
logger.info(String.format("Failed to create MKL instance: %s", e));

File diff suppressed because it is too large Load Diff

View File

@ -6729,7 +6729,50 @@ public class ArrayMath {
ranges.add(new Range(current[idx], current[idx], 1));
}
}
s = sum(a, ranges);
s = sumRange(a, ranges);
r.setDouble(i, s);
indexr.incr();
}
return r;
}
/**
* Compute sum value of an array along axes (dimension)
*
* @param a Array a
* @param axes Axes
* @return Sum value array
* @throws InvalidRangeException
*/
public static Array sum(Array a, List<Integer> axes) throws InvalidRangeException {
int[] dataShape = a.getShape();
int[] shape = new int[dataShape.length - axes.size()];
int idx = 0;
for (int i = 0; i < dataShape.length; i++) {
if (axes.contains(i)) {
continue;
}
shape[idx] = dataShape[i];
idx += 1;
}
Array r = Array.factory(a.getDataType(), shape);
double s;
Index indexr = r.getIndex();
int[] current;
for (int i = 0; i < r.getSize(); i++) {
current = indexr.getCurrentCounter();
List<Range> ranges = new ArrayList<>();
idx = 0;
for (int j = 0; j < dataShape.length; j++) {
if (axes.contains(j)) {
ranges.add(new Range(0, dataShape[j] - 1, 1));
} else {
ranges.add(new Range(current[idx], current[idx], 1));
idx += 1;
}
}
s = sumRange(a, ranges);
r.setDouble(i, s);
indexr.incr();
}
@ -6745,7 +6788,7 @@ public class ArrayMath {
* @return Sum value
* @throws InvalidRangeException
*/
public static double sum(Array a, List<Range> ranges) throws InvalidRangeException {
public static double sumRange(Array a, List<Range> ranges) throws InvalidRangeException {
double s = 0.0, v;
int n = 0;
IndexIterator ii = a.getRangeIterator(ranges);
@ -6763,7 +6806,7 @@ public class ArrayMath {
}
/**
* Compute the sum arry from a list of arrays
* Compute the sum array from a list of arrays
*
* @param alist list of arrays
* @return Sum array