- BUGS AND LIMITATIONS
- LICENSE AND COPYRIGHT
PDLx::Mask - Mask multiple piddles with automatic two way feedback
use 5.10.0; use PDLx::Mask; use PDLx::MaskedData; $pdl = sequence( 9 ); $mask = PDLx::Mask->new( $pdl->ones ); say $mask; # [1 1 1 1 1 1 1 1 1] $data1 = PDLx::MaskedData->new( $pdl, $mask ); say $data1; # [0 1 2 3 4 5 6 7 8] $data2 = PDLx::MaskedData->new( $pdl + 1, $mask ); say $data2; # [1 2 3 4 5 6 7 8 9] # update the mask $mask->set( 3, 0 ); say $mask; # [1 1 1 0 1 1 1 1 1] # and see it propagate say $data1; # [0 1 2 0 4 5 6 7 8] say $data2; # [1 2 3 0 5 6 7 8 9] # use bad values for $data1 $data1->badflag(1); # notice that the invalid element is now bad say $data1; # [0 1 2 BAD 4 5 6 7 8] # push invalid values upstream to the shared mask $data1->upstream_mask(1); $data1->setbadat(0); say $data1; # [BAD 1 2 BAD 4 5 6 7 8] # see the mask change say $mask; # [0 1 1 0 1 1 1 1 1] # and see the other piddle change say $data2; # [0 2 3 0 5 6 7 8 9]
Typically PDL uses bad values to mark elements in a piddle which contain invalid data. When multiple piddles should have the same elements marked as invalid, a separate mask piddle (whose values are true for valid data and false otherwise) is often used.
PDLx::Mask in concert with PDLx::MaskedData simplifies the management of mutiple piddles sharing the same mask. PDLx::Mask is the shared mask, and PDLx::MaskedData is a specialized piddle which will dynamically respond to changes in the mask, so that they are always up-to-date.
Additionally, invalid elements in the data piddles may automatically be added to the shared mask, so that there is a consistent view of valid elements across all piddles.
PDLx::Mask is a subclass of PDL which manages a mask across on or more piddles. It can be used directly as a piddle, but be careful not to change its contents inadvertently. It should only be manipulated via the provided methods or overloaded operators.
It maintains two views of the mask:
the original base mask; and
the effective mask, which is the base mask combined with additional invalid elements from the data piddles.
The subscribe method is used to register callbacks to be invoked when the mask has been changed. Multiple subscriptions are allowed; each can register two callbacks:
A subroutine invoked when the mask has changed. It is passed a piddle containing the mask. It should not alter it.
A subroutine which will return a data mask. If the data mask changes, the mask's update method must be called.
$mask = PDLx::Mask->new( $base_mask ); # or $mask = PDLx::Mask->new( base => $base_mask );
Create a mask using the passed mask as the base mask. It does not copy the passed piddle.
$base = $mask->base;
This returns the base mask. Don't alter the returned piddle!
$pdl = $mask->mask; $pdl = $mask->mask( $new_mask );
Return the effective mask as a plain piddle. Don't alter the returned piddle!
If passed a piddle, it is copied to the base mask and the update method is called.
Note that the
$mask object can also be used directly without calling this method.
$nvalid_elements = $mask->nvalid;
The number of valid elements in the effective mask. This is lazily evaluated and cached.
$token = $mask->subscribe( apply_mask => $code_ref, %options );
Register the passed subroutines to be called when the effective mask is changed. The returned token may be used to unsubscribe the callbacks using unsubscribe.
The following options are available:
apply_mask=> code reference
This subroutine should expect a single argument (a mask piddle) and apply it. It should not alter the mask piddle. It is optional.
This callback will be invoked no arguments if the mask has been directed to unsubscribe the callbacks. See "unsubscribe"
data_mask=> code reference
This subroutine should return a piddle which encodes the intrinsic valid elements of the object's data. It is optional.
The mask object does not monitor this piddle for changes. If the data mask changes, the mask's update method must be called.
- token => scalar
Instead of creating a new subscription, update the entry with the given token, which was returned by a previous invocation of subscribe.
$bool = $mask->is_subscriber( $token );
Returns true if the passed token refers to an active subscriber.
$mask->unsubscribe( $token );
Unsubscribe the callbacks with the given token (returned by subscribe).
If the callbacks for
$token include the
apply_mask callback, it will be invoked with no arguments, indicating that it is being unsubscribed. At that time
$mask->is_subscriber($token) will return false.
This performs the following:
data_maskcallbacks are queried for their masks;
the effective mask is constructed from the base mask and the data masks; and
apply_maskcallbacks are invoked with the effective mask.
Returns a copy of the effective mask as an ordinary piddle.
This is a fatal operation.
This is a fatal operation if the passed value is non-zero.
$mask->set( $pos, $value);
This updates the base mask at position
$value and calls the update method.
Use of assignment operators (but not the underlying PDL methods or subroutines) other than the following should be fatal.
These operators may be used to update the base mask. The effective mask will automatically be updated.
Sometimes the primary mask should incorporate a secondary mask that's not associated with a data set. Here's how to do that:
$pmask = PDLx::Mask->new( pdl( byte, 1, 1, 1 ) ); $smask = PDLx::MaskedData->new( base => pdl( byte, 0, 1, 0 ), mask => $pmask, apply_mask => 0, data_mask => 1 );
The key difference between this and an ordinary dependency on a data mask, is that by turning off
apply_mask, changes in the primary mask won't be replicated in the secondary.
say $smask; # [ 0 1 0 ] say $pmask->base; # [ 1 1 1 ] say $pmask; # [ 0 1 0 ] $smask->set( 0, 1 ); say $smask; # [ 1 1 0 ] say $pmask->base; # [ 1 1 1 ] say $pmask; # [ 1 1 0 ] $pmask->set( 0, 0 ); say $smask; # [ 1 1 0 ] say $pmask->base; # [ 0 1 1 ] say $pmask; # [ 0 1 0 ]
Building upon the previous example, let's say the secondary mask is used intermittently. For example
$pmask = PDLx::Mask->new( [ 1, 1, 1 ] ); $smask = PDLx::MaskedData->new( base => [ 0, 1, 0 ], mask => $pmask, apply_mask => 0, data_mask => 1 ); $data = PDLx::MaskedData->new( [ 33, 22, 44 ], $pmask ); say $data # [ 0, 22, 0 ] # now want to ignore secondary mask $smask->unsubscribe; say $data # [ 33, 22, 44 ] # and now stop ignoring it $smask->subscribe; say $data # [ 0, 22, 0 ]
Please report any bugs or feature requests to
firstname.lastname@example.org, or through the web interface at http://rt.cpan.org/Public/Dist/Display.html?Name=PDLx-Mask.
Copyright (c) 2016 The Smithsonian Astrophysical Observatory
PDLx::Mask is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
Diab Jerius <email@example.com>