Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion polymath/boolean.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
# polymath/boolean.py: Boolean subclass of PolyMath base class
##########################################################################################

from __future__ import division
import numpy as np

from polymath.qube import Qube
Expand Down
1 change: 1 addition & 0 deletions polymath/extensions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
Qube.split_items = item_ops.split_items
Qube.swap_items = item_ops.swap_items
Qube.chain = item_ops.chain
Qube.__matmul__ = item_ops.__matmul__

from polymath.extensions import iterator
Qube.__iter__ = iterator.__iter__
Expand Down
13 changes: 9 additions & 4 deletions polymath/extensions/item_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ def extract_numer(self, axis, index, classes=(), *, recursive=True):
def extract_denom(self, axis, index, classes=()):
"""Extract an object from one denominator axis.

Extracting from a denominator axis reduces the shape by removing that axis dimension.
For example, extracting from a Vector with shape (3,), numer (3,), denom (3,) at
index 1 returns a Vector with shape (), numer (3,), denom ().

Parameters:
axis (int): The item axis from which to extract a slice.
index (int): The index value at which to extract the slice.
Expand All @@ -61,7 +65,8 @@ def extract_denom(self, axis, index, classes=()):
in the list. Otherwise, a generic Qube object will be returned.

Returns:
Qube: An object extracted from the specified denominator axis.
Qube: An object extracted from the specified denominator axis. The shape is
reduced by removing the extracted axis dimension.

Raises:
ValueError: If the axis is out of range.
Expand Down Expand Up @@ -180,8 +185,8 @@ def transpose_numer(self, axis1=0, axis2=1, *, recursive=True):
ValueError: If either axis is out of range.
"""

self._require_axis_in_range(axis1, self._nrank, 'slice_numer()', 'axis1')
self._require_axis_in_range(axis2, self._nrank, 'slice_numer()', 'axis2')
self._require_axis_in_range(axis1, self._nrank, 'transpose_numer()', 'axis1')
self._require_axis_in_range(axis2, self._nrank, 'transpose_numer()', 'axis2')

# Position axis1 from left
a1 = axis1 if axis1 >= 0 else axis1 + self._nrank
Expand Down Expand Up @@ -321,7 +326,7 @@ def reshape_denom(self, shape):
# Validate the shape
shape = tuple(shape)
if self.dsize != int(np.prod(shape)):
opstr = self._opstr('reshape_numer()')
opstr = self._opstr('reshape_denom()')
raise ValueError(f'{opstr} denominator size must be unchanged: {self._denom}, '
f'{shape}')

Expand Down
31 changes: 21 additions & 10 deletions polymath/extensions/mask_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ def mask_where(self, mask, replace=None, *, remask=True, recursive=True):
values unchanged.
remask (bool, optional): True to leave the new values masked; False to replace
the values but leave them unmasked.
recursive (bool, optional): True to mask the derivatives as well;
False to leave them unmasked.
recursive (bool, optional): True to include and mask the derivatives as well;
False to exclude derivatives from the returned object.

Returns:
Qube: A copy of this object with the mask applied.
Expand Down Expand Up @@ -49,9 +49,9 @@ def mask_where(self, mask, replace=None, *, remask=True, recursive=True):
# Shapeless case
if self._is_scalar:
if replace is None:
obj = self.copy(recursive=True)
obj = self.copy(recursive=recursive)
else:
obj = replace.copy(recursive=True)
obj = replace.copy(recursive=recursive)

if remask:
obj = obj.remask(True, recursive=recursive)
Expand All @@ -61,7 +61,7 @@ def mask_where(self, mask, replace=None, *, remask=True, recursive=True):
# Case with no replacement
if replace is None:
# Note that the new mask must be a copy
obj = self.remask_or(mask, recursive=True)
obj = self.remask_or(mask, recursive=recursive)
return obj

# If replacement is an array or single Qube...
Expand All @@ -71,7 +71,7 @@ def mask_where(self, mask, replace=None, *, remask=True, recursive=True):
# use True, which will allow the replacement to broadcast as needed.
rep_mask = mask if replace._shape else True

obj = self.copy()
obj = self.copy(recursive=recursive)
obj[mask] = replace[rep_mask] # handles derivatives too!

if remask:
Expand Down Expand Up @@ -444,7 +444,12 @@ def _limit_from_qube(self, limit, masked, op):
"""

if isinstance(limit, np.ndarray):
if self._rank: # limits apply to items overall, not to individual components
if self._rank: # pragma: no cover
# Limits apply to items overall, not to individual components
# Note: For Scalars, _rank is always 0, so this line cannot be reached with
# Scalars
# This line would require a Qube type with _rank > 0, but such types don't
# have mask_where_le/clip because they require scalar items
limit = np.reshape(limit, self._rank * (1,))
return limit

Expand All @@ -462,16 +467,22 @@ def _limit_from_qube(self, limit, masked, op):
if limit._numer != self._numer:
raise ValueError(self._opstr(op) + ' limit item does not match object: '
f'{limit._numer}, {self._numer}')
tail = limit._numer + tail
# This requires both self and limit to have non-empty _numer that match
# Scalars have _numer = (), so we can't test this with Scalars
# But Scalars always have _numer = (), so this line cannot be reached with Scalars
tail = limit._numer + tail # pragma: no cover
elif self._numer:
tail = self._nrank * (1,) + tail
# This requires self._numer to be truthy (non-empty) and limit._numer to be falsy
# (empty)
# Scalars have _numer = (), so we can't test this with Scalars
tail = self._nrank * (1,) + tail # pragma: no cover

vals = np.broadcast_to(limit._values, self._shape + tail)

if not np.any(limit._mask):
return vals

mask = np.reshape(limit.mask, limit._mask.shape + self._rank * (1,))
mask = np.reshape(limit._mask, limit._mask.shape + self._rank * (1,))
mask = np.broadcast_to(mask, vals.shape)
vals = vals.copy()
vals[mask] = masked
Expand Down
Loading
Loading