| 
						
						
						
					 | 
					 | 
					@ -1,4 +1,9 @@ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					"""This module contains definitions and utilities for 2-, 4-, and 8-valued logic operations. | 
					 | 
					 | 
					 | 
					"""This module contains definitions and tools for 2-, 4-, and 8-valued logic operations. | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					Logic values are stored in numpy arrays with data type ``np.uint8``. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					There are no explicit data structures in KyuPy for holding patterns, pattern sets or vectors. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					However, there are conventions on logic value encoding and on the order of axes. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					Utility functions defined here follow these conventions. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					8 logic values are defined as integer constants. | 
					 | 
					 | 
					 | 
					8 logic values are defined as integer constants. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -21,10 +26,22 @@ Except when bit0 differs from bit1, but bit2 (activity) is 0: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					4-valued logic only considers bit0 and bit1. | 
					 | 
					 | 
					 | 
					4-valued logic only considers bit0 and bit1. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					8-valued logic considers all 3 bits. | 
					 | 
					 | 
					 | 
					8-valued logic considers all 3 bits. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					Logic values are stored in numpy arrays of data type ``np.uint8``. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					The axis convention is as follows: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					* The **last** axis goes along patterns/vectors. I.e. ``values[...,0]`` is pattern 0, ``values[...,1]`` is pattern 1, etc. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					* The **second-to-last** axis goes along the I/O and flip-flops of circuits. For a circuit ``c``, this axis is usually | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					``len(c.s_nodes)`` long. The values of all inputs, outputs and flip-flops are stored within the same array and the location | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					along the second-to-last axis is determined by the order in ``c.s_nodes``. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					Two storage formats are used in KyuPy: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					* ``mv...`` (for "multi-valued"): Each logic value is stored in the least significant 3 bits of ``np.uint8``. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					* ``bp...`` (for "bit-parallel"): Groups of 8 logic values are stored as three ``np.uint8``. This format is used | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					for bit-parallel logic simulations. It is also more memory-efficient. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					The functions in this module use the ``mv...`` and ``bp...`` prefixes to signify the storage format they operate on. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					""" | 
					 | 
					 | 
					 | 
					""" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					import math | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					from collections.abc import Iterable | 
					 | 
					 | 
					 | 
					from collections.abc import Iterable | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					import numpy as np | 
					 | 
					 | 
					 | 
					import numpy as np | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					 | 
					@ -80,41 +97,12 @@ def interpret(value): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    return UNKNOWN | 
					 | 
					 | 
					 | 
					    return UNKNOWN | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					_bit_in_lut = np.array([2 ** x for x in range(7, -1, -1)], dtype='uint8') | 
					 | 
					 | 
					 | 
					def mvarray(*a): | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					    """Converts (lists of) Boolean values or strings into a multi-valued array. | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					@numba.njit | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					def bit_in(a, pos): | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    return a[pos >> 3] & _bit_in_lut[pos & 7] | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					def unpackbits(a : np.ndarray): | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    """Unpacks the bits of given ndarray `a`. | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    Similar to `np.unpackbits`, but accepts any dtype, preserves the shape of `a` and | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    adds a new last axis with the bits of each item. Bits are in 'little'-order, i.e., | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    a[...,0] is the least significant bit. | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    """ | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    return np.unpackbits(a.view(np.uint8), bitorder='little').reshape(*a.shape, 8*a.itemsize) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					def packbits(a, dtype=np.uint8): | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    """Packs the values of a boolean-valued array `a` along its last axis into bits. | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    Similary to `np.packbits`, but returns an array of given dtype and the shape of `a` with the last axis removed. | 
					 | 
					 | 
					 | 
					    The given values are interpreted and the axes are arranged as per KyuPy's convention. | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    The last axis of `a` is truncated or padded according to the bit-width of the given dtype. | 
					 | 
					 | 
					 | 
					    Use this function to convert strings into multi-valued arrays. | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    Signed integer datatypes are padded with the most significant bit, all others are padded with `0`. | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    """ | 
					 | 
					 | 
					 | 
					    """ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    dtype = np.dtype(dtype) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    bits = 8 * dtype.itemsize | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    a = a[...,:bits] | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if a.shape[-1] < bits: | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        p = [(0,0)]*(len(a.shape)-1) + [(0, bits-a.shape[-1])] | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        a = np.pad(a, p, 'edge') if dtype.name[0] == 'i' else np.pad(a, p, 'constant', constant_values=0) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    return np.packbits(a, bitorder='little').view(dtype).reshape(a.shape[:-1]) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					def mvarray(*a): | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    mva = np.array(interpret(a), dtype=np.uint8) | 
					 | 
					 | 
					 | 
					    mva = np.array(interpret(a), dtype=np.uint8) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if mva.ndim < 2: return mva | 
					 | 
					 | 
					 | 
					    if mva.ndim < 2: return mva | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if mva.shape[-2] > 1: return mva.swapaxes(-1, -2) | 
					 | 
					 | 
					 | 
					    if mva.shape[-2] > 1: return mva.swapaxes(-1, -2) | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -122,24 +110,13 @@ def mvarray(*a): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					def mv_str(mva, delim='\n'): | 
					 | 
					 | 
					 | 
					def mv_str(mva, delim='\n'): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    """Renders a given multi-valued array into a string. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    """ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    sa = np.choose(mva, np.array([*'0X-1PRFN'], dtype=np.unicode_)) | 
					 | 
					 | 
					 | 
					    sa = np.choose(mva, np.array([*'0X-1PRFN'], dtype=np.unicode_)) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if mva.ndim == 1: return ''.join(sa) | 
					 | 
					 | 
					 | 
					    if mva.ndim == 1: return ''.join(sa) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    return delim.join([''.join(c) for c in sa.swapaxes(-1,-2)]) | 
					 | 
					 | 
					 | 
					    return delim.join([''.join(c) for c in sa.swapaxes(-1,-2)]) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					def mv_to_bp(mva): | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if mva.ndim == 1: mva = mva[..., np.newaxis] | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    return np.packbits(unpackbits(mva)[...,:3], axis=-2, bitorder='little').swapaxes(-1,-2) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					def bparray(*a): | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    return mv_to_bp(mvarray(*a)) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					def bp_to_mv(bpa): | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    return packbits(np.unpackbits(bpa, axis=-1, bitorder='little').swapaxes(-1,-2)) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					def _mv_not(out, inp): | 
					 | 
					 | 
					 | 
					def _mv_not(out, inp): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    np.bitwise_xor(inp, 0b11, out=out)  # this also exchanges UNASSIGNED <-> UNKNOWN | 
					 | 
					 | 
					 | 
					    np.bitwise_xor(inp, 0b11, out=out)  # this also exchanges UNASSIGNED <-> UNKNOWN | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    np.putmask(out, (inp == UNKNOWN), UNKNOWN)  # restore UNKNOWN | 
					 | 
					 | 
					 | 
					    np.putmask(out, (inp == UNKNOWN), UNKNOWN)  # restore UNKNOWN | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -148,13 +125,10 @@ def _mv_not(out, inp): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					def mv_not(x1 : np.ndarray, out=None): | 
					 | 
					 | 
					 | 
					def mv_not(x1 : np.ndarray, out=None): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    """A multi-valued NOT operator. | 
					 | 
					 | 
					 | 
					    """A multi-valued NOT operator. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    :param x1: An :py:class:`MVArray` or data the :py:class:`MVArray` constructor accepts. | 
					 | 
					 | 
					 | 
					    :param x1: A multi-valued array. | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    :param out: Optionally an :py:class:`MVArray` as storage destination. If None, a new :py:class:`MVArray` | 
					 | 
					 | 
					 | 
					    :param out: An optional storage destination. If None, a new multi-valued array is returned. | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        is returned. | 
					 | 
					 | 
					 | 
					    :return: A multi-valued array with the result. | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    :return: An :py:class:`MVArray` with the result. | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    """ | 
					 | 
					 | 
					 | 
					    """ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    #m = mv_getm(x1) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    #x1 = mv_cast(x1, m=m)[0] | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    out = out or np.empty(x1.shape, dtype=np.uint8) | 
					 | 
					 | 
					 | 
					    out = out or np.empty(x1.shape, dtype=np.uint8) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    _mv_not(out, x1) | 
					 | 
					 | 
					 | 
					    _mv_not(out, x1) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    return out | 
					 | 
					 | 
					 | 
					    return out | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -176,14 +150,11 @@ def _mv_or(out, *ins): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					def mv_or(x1, x2, out=None): | 
					 | 
					 | 
					 | 
					def mv_or(x1, x2, out=None): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    """A multi-valued OR operator. | 
					 | 
					 | 
					 | 
					    """A multi-valued OR operator. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    :param x1: An :py:class:`MVArray` or data the :py:class:`MVArray` constructor accepts. | 
					 | 
					 | 
					 | 
					    :param x1: A multi-valued array. | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    :param x2: An :py:class:`MVArray` or data the :py:class:`MVArray` constructor accepts. | 
					 | 
					 | 
					 | 
					    :param x2: A multi-valued array. | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    :param out: Optionally an :py:class:`MVArray` as storage destination. If None, a new :py:class:`MVArray` | 
					 | 
					 | 
					 | 
					    :param out: An optional storage destination. If None, a new multi-valued array is returned. | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        is returned. | 
					 | 
					 | 
					 | 
					    :return: A multi-valued array with the result. | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    :return: An :py:class:`MVArray` with the result. | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    """ | 
					 | 
					 | 
					 | 
					    """ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    #m = mv_getm(x1, x2) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    #x1, x2 = mv_cast(x1, x2, m=m) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    out = out or np.empty(np.broadcast(x1, x2).shape, dtype=np.uint8) | 
					 | 
					 | 
					 | 
					    out = out or np.empty(np.broadcast(x1, x2).shape, dtype=np.uint8) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    _mv_or(out, x1, x2) | 
					 | 
					 | 
					 | 
					    _mv_or(out, x1, x2) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    return out | 
					 | 
					 | 
					 | 
					    return out | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -206,14 +177,11 @@ def _mv_and(out, *ins): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					def mv_and(x1, x2, out=None): | 
					 | 
					 | 
					 | 
					def mv_and(x1, x2, out=None): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    """A multi-valued AND operator. | 
					 | 
					 | 
					 | 
					    """A multi-valued AND operator. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    :param x1: An :py:class:`MVArray` or data the :py:class:`MVArray` constructor accepts. | 
					 | 
					 | 
					 | 
					    :param x1: A multi-valued array. | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    :param x2: An :py:class:`MVArray` or data the :py:class:`MVArray` constructor accepts. | 
					 | 
					 | 
					 | 
					    :param x2: A multi-valued array. | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    :param out: Optionally an :py:class:`MVArray` as storage destination. If None, a new :py:class:`MVArray` | 
					 | 
					 | 
					 | 
					    :param out: An optional storage destination. If None, a new multi-valued array is returned. | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        is returned. | 
					 | 
					 | 
					 | 
					    :return: A multi-valued array with the result. | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    :return: An :py:class:`MVArray` with the result. | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    """ | 
					 | 
					 | 
					 | 
					    """ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    #m = mv_getm(x1, x2) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    #x1, x2 = mv_cast(x1, x2, m=m) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    out = out or np.empty(np.broadcast(x1, x2).shape, dtype=np.uint8) | 
					 | 
					 | 
					 | 
					    out = out or np.empty(np.broadcast(x1, x2).shape, dtype=np.uint8) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    _mv_and(out, x1, x2) | 
					 | 
					 | 
					 | 
					    _mv_and(out, x1, x2) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    return out | 
					 | 
					 | 
					 | 
					    return out | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -233,24 +201,28 @@ def _mv_xor(out, *ins): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					def mv_xor(x1, x2, out=None): | 
					 | 
					 | 
					 | 
					def mv_xor(x1, x2, out=None): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    """A multi-valued XOR operator. | 
					 | 
					 | 
					 | 
					    """A multi-valued XOR operator. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    :param x1: An :py:class:`MVArray` or data the :py:class:`MVArray` constructor accepts. | 
					 | 
					 | 
					 | 
					    :param x1: A multi-valued array. | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    :param x2: An :py:class:`MVArray` or data the :py:class:`MVArray` constructor accepts. | 
					 | 
					 | 
					 | 
					    :param x2: A multi-valued array. | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    :param out: Optionally an :py:class:`MVArray` as storage destination. If None, a new :py:class:`MVArray` | 
					 | 
					 | 
					 | 
					    :param out: An optional storage destination. If None, a new multi-valued array is returned. | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        is returned. | 
					 | 
					 | 
					 | 
					    :return: A multi-valued array with the result. | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    :return: An :py:class:`MVArray` with the result. | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    """ | 
					 | 
					 | 
					 | 
					    """ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    #m = mv_getm(x1, x2) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    #x1, x2 = mv_cast(x1, x2, m=m) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    out = out or np.empty(np.broadcast(x1, x2).shape, dtype=np.uint8) | 
					 | 
					 | 
					 | 
					    out = out or np.empty(np.broadcast(x1, x2).shape, dtype=np.uint8) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    _mv_xor(out, x1, x2) | 
					 | 
					 | 
					 | 
					    _mv_xor(out, x1, x2) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    return out | 
					 | 
					 | 
					 | 
					    return out | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					def mv_latch(d, t, q_prev, out=None): | 
					 | 
					 | 
					 | 
					def mv_latch(d, t, q_prev, out=None): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    """A latch that is transparent if `t` is high. `q_prev` has to be the output value from the previous clock cycle. | 
					 | 
					 | 
					 | 
					    """A multi-valued latch operator. | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    A latch outputs ``d`` when transparent (``t`` is high). | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    It outputs ``q_prev`` when in latched state (``t`` is low). | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    :param d: A multi-valued array for the data input. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    :param t: A multi-valued array for the control input. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    :param q_prev: A multi-valued array with the output value of this latch from the previous clock cycle. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    :param out: An optional storage destination. If None, a new multi-valued array is returned. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    :return: A multi-valued array for the latch output ``q``. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    """ | 
					 | 
					 | 
					 | 
					    """ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    #m = mv_getm(d, t, q_prev) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    #d, t, q_prev = mv_cast(d, t, q_prev, m=m) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    out = out or np.empty(np.broadcast(d, t, q_prev).shape, dtype=np.uint8) | 
					 | 
					 | 
					 | 
					    out = out or np.empty(np.broadcast(d, t, q_prev).shape, dtype=np.uint8) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    out[...] = t & d & 0b011 | 
					 | 
					 | 
					 | 
					    out[...] = t & d & 0b011 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    out[...] |= ~t & 0b010 & (q_prev << 1) | 
					 | 
					 | 
					 | 
					    out[...] |= ~t & 0b010 & (q_prev << 1) | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -268,16 +240,11 @@ def mv_transition(init, final, out=None): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    Pulses in the input data are ignored. If any of the inputs are ``UNKNOWN``, the result is ``UNKNOWN``. | 
					 | 
					 | 
					 | 
					    Pulses in the input data are ignored. If any of the inputs are ``UNKNOWN``, the result is ``UNKNOWN``. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    If both inputs are ``UNASSIGNED``, the result is ``UNASSIGNED``. | 
					 | 
					 | 
					 | 
					    If both inputs are ``UNASSIGNED``, the result is ``UNASSIGNED``. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    :param init: An :py:class:`MVArray` or data the :py:class:`MVArray` constructor accepts. | 
					 | 
					 | 
					 | 
					    :param init: A multi-valued array. | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    :param final: An :py:class:`MVArray` or data the :py:class:`MVArray` constructor accepts. | 
					 | 
					 | 
					 | 
					    :param final: A multi-valued array. | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    :param out: Optionally an :py:class:`MVArray` as storage destination. If None, a new :py:class:`MVArray` | 
					 | 
					 | 
					 | 
					    :param out: An optional storage destination. If None, a new multi-valued array is returned. | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        is returned. | 
					 | 
					 | 
					 | 
					    :return: A multi-valued array with the result. | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    :return: An :py:class:`MVArray` with the result. | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    """ | 
					 | 
					 | 
					 | 
					    """ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    #m = mv_getm(init, final) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    #init, final = mv_cast(init, final, m=m) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    #init = init.data | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    #final = final.data | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    out = out or np.empty(np.broadcast(init, final).shape, dtype=np.uint8) | 
					 | 
					 | 
					 | 
					    out = out or np.empty(np.broadcast(init, final).shape, dtype=np.uint8) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    out[...] = (init & 0b010) | (final & 0b001) | 
					 | 
					 | 
					 | 
					    out[...] = (init & 0b010) | (final & 0b001) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    out[...] |= ((out << 1) ^ (out << 2)) & 0b100 | 
					 | 
					 | 
					 | 
					    out[...] |= ((out << 1) ^ (out << 2)) & 0b100 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -288,6 +255,28 @@ def mv_transition(init, final, out=None): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    return out | 
					 | 
					 | 
					 | 
					    return out | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					def mv_to_bp(mva): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    """Converts a multi-valued array into a bit-parallel array. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    """ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    if mva.ndim == 1: mva = mva[..., np.newaxis] | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    return np.packbits(unpackbits(mva)[...,:3], axis=-2, bitorder='little').swapaxes(-1,-2) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					def bparray(*a): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    """Converts (lists of) Boolean values or strings into a bit-parallel array. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    The given values are interpreted and the axes are arranged as per KyuPy's convention. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    Use this function to convert strings into bit-parallel arrays. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    """ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    return mv_to_bp(mvarray(*a)) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					def bp_to_mv(bpa): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    """Converts a bit-parallel array into a multi-valued array. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    """ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    return packbits(np.unpackbits(bpa, axis=-1, bitorder='little').swapaxes(-1,-2)) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					def bp_buf(out, inp): | 
					 | 
					 | 
					 | 
					def bp_buf(out, inp): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    unknown = (inp[..., 0, :] ^ inp[..., 1, :]) & ~inp[..., 2, :] | 
					 | 
					 | 
					 | 
					    unknown = (inp[..., 0, :] ^ inp[..., 1, :]) & ~inp[..., 2, :] | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    out[..., 0, :] = inp[..., 0, :] | unknown | 
					 | 
					 | 
					 | 
					    out[..., 0, :] = inp[..., 0, :] | unknown | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					 | 
					@ -356,3 +345,37 @@ def bp_latch(out, d, t, q_prev): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    out[..., 1, :] &= ~any_unknown | 
					 | 
					 | 
					 | 
					    out[..., 1, :] &= ~any_unknown | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    out[..., 2, :] &= ~any_unknown | 
					 | 
					 | 
					 | 
					    out[..., 2, :] &= ~any_unknown | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    return out | 
					 | 
					 | 
					 | 
					    return out | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					_bit_in_lut = np.array([2 ** x for x in range(7, -1, -1)], dtype='uint8') | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					@numba.njit | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					def bit_in(a, pos): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    return a[pos >> 3] & _bit_in_lut[pos & 7] | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					def unpackbits(a : np.ndarray): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    """Unpacks the bits of given ndarray ``a``. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    Similar to ``np.unpackbits``, but accepts any dtype, preserves the shape of ``a`` and | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    adds a new last axis with the bits of each item. Bits are in 'little'-order, i.e., | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    a[...,0] is the least significant bit of each item. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    """ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    return np.unpackbits(a.view(np.uint8), bitorder='little').reshape(*a.shape, 8*a.itemsize) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					def packbits(a, dtype=np.uint8): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    """Packs the values of a boolean-valued array ``a`` along its last axis into bits. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    Similar to ``np.packbits``, but returns an array of given dtype and the shape of ``a`` with the last axis removed. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    The last axis of `a` is truncated or padded according to the bit-width of the given dtype. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    Signed integer datatypes are padded with the most significant bit, all others are padded with `0`. | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    """ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    dtype = np.dtype(dtype) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    bits = 8 * dtype.itemsize | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    a = a[...,:bits] | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    if a.shape[-1] < bits: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        p = [(0,0)]*(len(a.shape)-1) + [(0, bits-a.shape[-1])] | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					        a = np.pad(a, p, 'edge') if dtype.name[0] == 'i' else np.pad(a, p, 'constant', constant_values=0) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    return np.packbits(a, bitorder='little').view(dtype).reshape(a.shape[:-1]) | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
					 | 
					 | 
					
  |