def __repr__(self): """Return ``repr(self)``.""" # Matrix printing itself in an executable way (for dense matrix) if self.matrix_issparse: # Don't convert to dense, can take forever matrix_str = repr(self.matrix) else: matrix_str = np.array2string(self.matrix, separator=', ') posargs = [matrix_str] # Optional arguments with defaults, inferred from the matrix optargs = [] # domain optargs.append( ('domain', self.domain, fn(self.matrix.shape[1], self.matrix.dtype))) # range optargs.append( ('range', self.range, fn(self.matrix.shape[0], self.matrix.dtype))) inner_str = signature_string(posargs, optargs, sep=[', ', ', ', ',\n'], mod=[['!s'], ['!r', '!r']]) return '{}(\n{}\n)'.format(self.__class__.__name__, indent_rows(inner_str))
def __init__(self, range): """Initialize a new instance. Parameters ---------- range : `LinearSpace` or `Field` Space that the operator should map to. Examples -------- General usage example: >>> X = odl.uniform_discr(min_pt=[-1, -1], max_pt=[1, 1], shape=[1, 2]) >>> A = odl.FlatteningOperatorAdjoint(X) >>> x = A.domain.element(range(A.domain.size)) >>> A(x) uniform_discr([-1.0, -1.0], [1.0, 1.0], (1, 2)).element([[0.0, 1.0]]) """ if not isinstance(range, (LinearSpace, Field)): raise TypeError('`range` {!r} not a `LinearSpace` or `Field` ' 'instance'.format(range)) domain = fn(range.size, dtype=range.dtype) super(FlatteningOperatorAdjoint, self).__init__( domain, range, linear=True)
def __init__(self, domain, order='C'): """Initialize a new instance. Parameters ---------- domain : `FnBase` or `DiscreteLp` Set of elements on which this operator acts. order : {'C', 'F'} (optional) The flattening is performed in this order. 'C' means that that the last index is changing fastest or in terms of a matrix that the read out is row-by-row. Likewise 'F' is column-by-column. Examples -------- General usage example: >>> X = odl.uniform_discr(min_pt=[-1, -1], max_pt=[1, 1], shape=[2, 3]) >>> x = X.element(range(X.size)) >>> A = odl.FlatteningOperator(X) >>> A(x) rn(6).element([0.0, 1.0, 2.0, 3.0, 4.0, 5.0]) """ if not isinstance(domain, (LinearSpace, Field)): raise TypeError('`domain` {!r} not a `LinearSpace` or `Field` ' 'instance'.format(domain)) self.__order = order range = fn(domain.size, dtype=domain.dtype) super(FlatteningOperator, self).__init__(domain, range, linear=True)
def __init__(self, domain, sampling_points, variant='point_eval'): """Initialize a new instance. Parameters ---------- domain : `FnBase` or `DiscreteLp` Set of elements on which this operator acts. sampling_points : sequence Sequence of indices that determine the sampling points. In n dimensions, it should be of length n. Each element of this list is a list by itself and should have the length of the total number of sampling points. Example: To sample a function at the points (0, 1) and (1, 1) the indices should be defined as sampling_points = [[0, 1], [1, 1]]. variant : {'point_eval', 'integrate'}, optional For `'point_eval'` this operator performs the sampling by evaluation the function at the sampling points. The `'integrate'` variant approximates integration by multiplying point evaluation with the cell volume. Examples -------- >>> X = odl.uniform_discr(min_pt=[0, 0], max_pt=[1, 1], shape=[2, 2]) >>> x = X.element(range(X.size)) >>> sampling_points = [[0, 1], [1, 1]] >>> A = odl.SamplingOperator(X, sampling_points, 'point_eval') >>> A(x) rn(2).element([1.0, 3.0]) >>> A = odl.SamplingOperator(X, sampling_points, 'integrate') >>> A(x) rn(2).element([0.25, 0.75]) """ if not isinstance(domain, (FnBase, DiscreteLp)): raise TypeError('`domain` {!r} not a `FnBase` or `DiscreteLp` ' 'instance'.format(domain)) self.__sampling_points = np.asarray(sampling_points, dtype=int) self.__variant = variant # Converts a single sequence to an array of integers # and a sequence of arrays to a vertically stacked array if self.sampling_points.ndim > 1: # Strides = increment in linear indices per axis strides = np.concatenate((np.cumprod(domain.shape[1:])[::-1], [1])) self.__indices_flat = np.sum(self.sampling_points * strides[:, None], axis=0) else: self.__indices_flat = self.sampling_points range = fn(self.indices_flat.size, dtype=domain.dtype) super().__init__(domain, range, linear=True)
def __init__(self, range, sampling_points, variant='dirac'): """Initialize a new instance. Parameters ---------- range : `FnBase` or `DiscreteLp` Set of elements into which this operator maps. sampling_points : sequence Sequence of indices that determine the sampling points. In n dimensions, it should be of length n. Each element of this list is a list by itself and should have the length of the total number of sampling points. Example: To sample a function at the points (0, 1) and (1, 1) the indices should be defined as sampling_points = [[0, 1], [1, 1]]. variant : {'dirac', 'char_fun'}, optional This option determines which function to sum over. Examples -------- >>> X = odl.uniform_discr(min_pt=[0, 0], max_pt=[1, 1], shape=[2, 2]) >>> sampling_points = [[0, 1], [1, 1]] >>> A = odl.WeightedSumSamplingOperator(X, sampling_points, 'dirac') >>> x = A.domain.one() >>> A(x) uniform_discr([0.0, 0.0], [1.0, 1.0], (2, 2)).element([[0.0, 4.0], [0.0, 4.0]]) >>> A = odl.WeightedSumSamplingOperator(X, sampling_points, 'char_fun') >>> A(x) uniform_discr([0.0, 0.0], [1.0, 1.0], (2, 2)).element([[0.0, 1.0], [0.0, 1.0]]) """ self.__sampling_points = np.asarray(sampling_points, dtype=int) self.__variant = variant # Converts a single sequence to an array of integers # and a sequence of arrays to a vertically stacked array if self.sampling_points.ndim > 1: # Strides = increment in linear indices per axis strides = np.concatenate((np.cumprod(range.shape[1:])[::-1], [1])) self.__indices_flat = np.sum( self.sampling_points * strides[:, None], axis=0) else: self.__indices_flat = self.sampling_points domain = fn(self.indices_flat.size, dtype=range.dtype) super(WeightedSumSamplingOperator, self).__init__( domain, range, linear=True)
def ufunc_class_factory(name, nargin, nargout, docstring): """Create a Ufunc `Operator` from a given specification.""" assert 0 <= nargin <= 2 def __init__(self, space): """Initialize an instance. Parameters ---------- space : `FnBase` The domain of the operator. """ if not isinstance(space, LinearSpace): raise TypeError('`space` {!r} not a `LinearSpace`'.format(space)) if _is_integer_only_ufunc(name) and not is_int_dtype(space.dtype): raise ValueError("ufunc '{}' only defined with integral dtype" "".format(name)) if nargin == 1: domain = space else: domain = ProductSpace(space, nargin) if nargout == 1: range = space else: range = ProductSpace(space, nargout) linear = name in LINEAR_UFUNCS Operator.__init__(self, domain=domain, range=range, linear=linear) def _call(self, x, out=None): """Return ``self(x)``.""" if out is None: if nargin == 1: return getattr(x.ufuncs, name)() else: return getattr(x[0].ufuncs, name)(*x[1:]) else: if nargin == 1: return getattr(x.ufuncs, name)(out=out) else: return getattr(x[0].ufuncs, name)(*x[1:], out=out) def __repr__(self): """Return ``repr(self)``.""" return '{}({!r})'.format(name, self.domain) # Create example (also functions as doctest) if 'shift' in name or 'bitwise' in name or name == 'invert': dtype = int else: dtype = float space = fn(3, dtype=dtype) if nargin == 1: vec = space.element([-1, 1, 2]) arg = '{}'.format(vec) with np.errstate(all='ignore'): result = getattr(vec.ufuncs, name)() else: vec = space.element([-1, 1, 2]) vec2 = space.element([3, 4, 5]) arg = '[{}, {}]'.format(vec, vec2) with np.errstate(all='ignore'): result = getattr(vec.ufuncs, name)(vec2) if nargout == 2: result = '{{{}, {}}}'.format(result[0], result[1]) examples_docstring = RAW_EXAMPLES_DOCSTRING.format(space=space, name=name, arg=arg, result=result) full_docstring = docstring + examples_docstring attributes = {"__init__": __init__, "_call": _call, "derivative": derivative_factory(name), "__repr__": __repr__, "__doc__": full_docstring} full_name = name + '_op' return type(full_name, (Operator,), attributes)
def ufunc_class_factory(name, nargin, nargout, docstring): """Create a Ufunc `Operator` from a given specification.""" assert 0 <= nargin <= 2 def __init__(self, space): """Initialize an instance. Parameters ---------- space : `FnBase` The domain of the operator. """ if not isinstance(space, LinearSpace): raise TypeError('`space` {!r} not a `LinearSpace`'.format(space)) if _is_integer_only_ufunc(name) and not is_int_dtype(space.dtype): raise ValueError("ufunc '{}' only defined with integral dtype" "".format(name)) if nargin == 1: domain = space else: domain = ProductSpace(space, nargin) if nargout == 1: range = space else: range = ProductSpace(space, nargout) linear = name in LINEAR_UFUNCS Operator.__init__(self, domain=domain, range=range, linear=linear) def _call(self, x, out=None): """Return ``self(x)``.""" if out is None: if nargin == 1: return getattr(x.ufuncs, name)() else: return getattr(x[0].ufuncs, name)(*x[1:]) else: if nargin == 1: return getattr(x.ufuncs, name)(out=out) else: return getattr(x[0].ufuncs, name)(*x[1:], out=out) def __repr__(self): """Return ``repr(self)``.""" return '{}({!r})'.format(name, self.domain) # Create example (also functions as doctest) if 'shift' in name or 'bitwise' in name or name == 'invert': dtype = int else: dtype = float space = fn(3, dtype=dtype) if nargin == 1: vec = space.element([-1, 1, 2]) arg = '{}'.format(vec) with np.errstate(all='ignore'): result = getattr(vec.ufuncs, name)() else: vec = space.element([-1, 1, 2]) vec2 = space.element([3, 4, 5]) arg = '[{}, {}]'.format(vec, vec2) with np.errstate(all='ignore'): result = getattr(vec.ufuncs, name)(vec2) if nargout == 2: result = '{{{}, {}}}'.format(result[0], result[1]) examples_docstring = RAW_EXAMPLES_DOCSTRING.format(space=space, name=name, arg=arg, result=result) full_docstring = docstring + examples_docstring attributes = { "__init__": __init__, "_call": _call, "derivative": derivative_factory(name), "__repr__": __repr__, "__doc__": full_docstring } full_name = name + '_op' return type(full_name, (Operator, ), attributes)
def _resize_discr(discr, newshp, offset, discr_kwargs): """Return a space based on ``discr`` and ``newshp``. Use the domain of ``discr`` and its partition to create a new uniformly discretized space with ``newshp`` as shape. In axes where ``offset`` is given, it determines the number of added/removed cells to the left. Where ``offset`` is ``None``, the points are distributed evenly to left and right. The ``discr_kwargs`` parameter is passed to `uniform_discr` for further specification of discretization parameters. """ nodes_on_bdry = discr_kwargs.get('nodes_on_bdry', False) if np.shape(nodes_on_bdry) == (): nodes_on_bdry = ([(bool(nodes_on_bdry), bool(nodes_on_bdry))] * discr.ndim) elif discr.ndim == 1 and len(nodes_on_bdry) == 2: nodes_on_bdry = [nodes_on_bdry] elif len(nodes_on_bdry) != discr.ndim: raise ValueError('`nodes_on_bdry` has length {}, expected {}' ''.format(len(nodes_on_bdry), discr.ndim)) dtype = discr_kwargs.pop('dtype', discr.dtype) impl = discr_kwargs.pop('impl', discr.impl) exponent = discr_kwargs.pop('exponent', discr.exponent) interp = discr_kwargs.pop('interp', discr.interp) order = discr_kwargs.pop('order', discr.order) weighting = discr_kwargs.pop('weighting', discr.weighting) affected = np.not_equal(newshp, discr.shape) ndim = discr.ndim for i in range(ndim): if affected[i] and not discr.is_uniform_byaxis[i]: raise ValueError('cannot resize in non-uniformly discretized ' 'axis {}'.format(i)) grid_min, grid_max = discr.grid.min(), discr.grid.max() cell_size = discr.cell_sides new_minpt, new_maxpt = [], [] for axis, (n_orig, n_new, off, on_bdry) in enumerate( zip(discr.shape, newshp, offset, nodes_on_bdry)): if not affected[axis]: new_minpt.append(discr.min_pt[axis]) new_maxpt.append(discr.max_pt[axis]) continue n_diff = n_new - n_orig if off is None: num_r = n_diff // 2 num_l = n_diff - num_r else: num_r = n_diff - off num_l = off try: on_bdry_l, on_bdry_r = on_bdry except TypeError: on_bdry_l = on_bdry on_bdry_r = on_bdry if on_bdry_l: new_minpt.append(grid_min[axis] - num_l * cell_size[axis]) else: new_minpt.append(grid_min[axis] - (num_l + 0.5) * cell_size[axis]) if on_bdry_r: new_maxpt.append(grid_max[axis] + num_r * cell_size[axis]) else: new_maxpt.append(grid_max[axis] + (num_r + 0.5) * cell_size[axis]) fspace = FunctionSpace(IntervalProd(new_minpt, new_maxpt), out_dtype=dtype) dspace = fn(np.prod(newshp), dtype=dtype, impl=impl, exponent=exponent, weighting=weighting) # Stack together the (unchanged) nonuniform axes and the (new) uniform # axes in the right order part = uniform_partition([], [], ()) for i in range(ndim): if discr.is_uniform_byaxis[i]: part = part.append( uniform_partition(new_minpt[i], new_maxpt[i], newshp[i], nodes_on_bdry=nodes_on_bdry[i])) else: part = part.append(discr.partition.byaxis[i]) return DiscreteLp(fspace, part, dspace, exponent=exponent, interp=interp, order=order)
def __init__(self, matrix, domain=None, range=None): """Initialize a new instance. Parameters ---------- matrix : `array-like` or `scipy.sparse.base.spmatrix` 2-dimensional array representing the linear operator. domain : `FnBase`, optional Space of elements on which the operator can act. Its ``dtype`` must be castable to ``range.dtype``. For the default ``None``, a `NumpyFn` space with size ``matrix.shape[1]`` is used, together with the matrix' data type. range : `FnBase`, optional Space of elements on to which the operator maps. Its ``shape`` and ``dtype`` attributes must match the ones of the result of the multiplication. For the default ``None``, the range is inferred from ``matrix`` and ``domain``. Examples -------- By default, ``domain`` and ``range`` are `NumpyFn` type spaces: >>> matrix = np.ones((3, 4)) >>> op = MatrixOperator(matrix) >>> op MatrixOperator( [[ 1., 1., 1., 1.], [ 1., 1., 1., 1.], [ 1., 1., 1., 1.]] ) >>> op.domain rn(4) >>> op.range rn(3) >>> op([1, 2, 3, 4]) rn(3).element([10.0, 10.0, 10.0]) They can also be provided explicitly, for example with `uniform_discr` spaces: >>> dom = odl.uniform_discr(0, 1, 4) >>> ran = odl.uniform_discr(0, 1, 3) >>> op = MatrixOperator(matrix, domain=dom, range=ran) >>> op(dom.one()) uniform_discr(0.0, 1.0, 3).element([4.0, 4.0, 4.0]) For storage efficiency, SciPy sparse matrices can be used: >>> import scipy >>> row_idcs = np.array([0, 3, 1, 0]) >>> col_idcs = np.array([0, 3, 1, 2]) >>> values = np.array([4.0, 5.0, 7.0, 9.0]) >>> matrix = scipy.sparse.coo_matrix((values, (row_idcs, col_idcs)), ... shape=(4, 4)) >>> matrix.toarray() array([[ 4., 0., 9., 0.], [ 0., 7., 0., 0.], [ 0., 0., 0., 0.], [ 0., 0., 0., 5.]]) >>> op = MatrixOperator(matrix) >>> op(op.domain.one()) rn(4).element([13.0, 7.0, 0.0, 5.0]) """ # TODO: fix dead link `scipy.sparse.spmatrix` if scipy.sparse.isspmatrix(matrix): self.__matrix = matrix else: self.__matrix = np.asarray(matrix) if self.matrix.ndim != 2: raise ValueError('matrix {} has {} axes instead of 2' ''.format(matrix, self.matrix.ndim)) # Infer domain and range from matrix if necessary if domain is None: domain = fn(self.matrix.shape[1], dtype=self.matrix.dtype) elif not isinstance(domain, FnBase): raise TypeError('`domain` {!r} is not an `FnBase` instance' ''.format(domain)) if range is None: range = fn(self.matrix.shape[0], dtype=self.matrix.dtype) elif not isinstance(range, FnBase): raise TypeError('`range` {!r} is not an `FnBase` instance' ''.format(range)) # Check compatibility of matrix with domain and range if self.matrix.shape != (range.size, domain.size): raise ValueError('matrix shape {} does not match the required ' 'shape {} of a matrix {} --> {}' ''.format(self.matrix.shape, (range.size, domain.size), domain, range)) if not np.can_cast(domain.dtype, range.dtype): raise TypeError('domain data type {!r} cannot be safely cast to ' 'range data type {!r}' ''.format(domain.dtype, range.dtype)) if not np.can_cast(self.matrix.dtype, range.dtype): raise TypeError('matrix data type {!r} cannot be safely cast to ' 'range data type {!r}.' ''.format(matrix.dtype, range.dtype)) super().__init__(domain, range, linear=True)