PyKronecker
PyKronecker: A Python Library for the Efficient Manipulation of Kronecker Products and Related Structures - Published in JOSS (2023)
Science Score: 93.0%
This score indicates how likely this project is to be science-related based on various indicators:
-
○CITATION.cff file
-
✓codemeta.json file
Found codemeta.json file -
✓.zenodo.json file
Found .zenodo.json file -
✓DOI references
Found 4 DOI reference(s) in README and JOSS metadata -
✓Academic publication links
Links to: joss.theoj.org -
○Committers with academic emails
-
○Institutional organization owner
-
✓JOSS paper metadata
Published in Journal of Open Source Software
Scientific Fields
Repository
A python library for creating Kronecker Product operators with efficient lazy computation
Basic Info
- Host: GitHub
- Owner: nickelnine37
- License: mit
- Language: Python
- Default Branch: main
- Size: 380 KB
Statistics
- Stars: 16
- Watchers: 1
- Forks: 3
- Open Issues: 2
- Releases: 2
Metadata Files
README.md

Check out the full documentation and install instructions here :)
Overview
PyKronecker is a library for manipulating matrices which have a Kronecker product structure. Systems involving Kronecker products arise in many areas of applied mathematics and statistics. The aim of this library is to provide a clean interface for dealing with such systems, combining lazy evaluation and algebraic tricks to deliver large savings in terms of both memory and execution time.
Installation
Installation on Windows, OSX and Linux can be performed by running
pip3 install pykronecker
This will install the vanilla version of the library, with support for NumPy arrays only. Linux users have the additional option of installing PyKronecker with Jax support. The benefit of this is significantly faster runtimes, even when working with NumPy arrays only, due to Jax's JIT complier. This can be installed by running
pip3 install "pykronecker[jax]"
For Linux users with an Nvidia graphics card, PyKronecker is also compatible with the GPU and TPU version of Jax. However, since this relies on CUDA and cuDNN, it is recommended to follow the instructions here to install Jax first.
Usage
The concept of this library is to create instances of a KroneckerOperator class, which can be broadly treated as if it is a square numpy array. These objects are designed to be used with the @ syntax for matrix multiplication.
Basic operators
KroneckerProduct
Create a KroneckerProduct from two or more square NumPy/Jax arrays. These can be real or complex valued.
```python import numpy as np from pykronecker import KroneckerProduct
A = np.random.normal(size=(5, 5)) B = np.random.normal(size=(6, 6))
KP = KroneckerProduct([A, B]) ```
This object can operate on both vectors of shape (5 * 6, ) and tensors of shape (5, 6) using the @ syntax for matrix multiplication. The returned array will be of the same shape.
```python x = np.random.normal(size=5 * 6) X = x.reshape(5, 6)
assert np.allclose(KP @ x, (KP @ X).ravel()) ```
KroneckerSum
A KronekerSum can be created and used in much the same way.
```python
import numpy as np
from pykronecker import KroneckerSum
A = np.random.normal(size=(5, 5)) B = np.random.normal(size=(6, 6)) x = np.random.normal(size=5 * 6)
KS = KroneckerSum([A, B]) print(KS @ x) ```
KroneckerDiag
KroneckerDiag provides support for diagonal matrices, and can be created by passing a tensor of the appropriate size. This creates, in effect, a matrix with the vectorized tensor along the diagonal.
```python import numpy as np from pykronecker import KroneckerDiag
D = np.random.normal(size=(5, 6)) x = np.random.normal(size=5 * 6)
KD = KroneckerDiag(D) print(KD @ x) ```
KroneckerIdentity
Finally, KroneckerIdentity creates the identity matrix, which can be instantiated by passing another operator of the same size, or the shape of tensors the operator is expected to act on.
```python import numpy as np from pykronecker import KroneckerIdentity, KroneckerDiag
create another KroneckerDiag operator
D = np.random.normal(size=(5, 6)) KD = KroneckerDiag(D)
create a KroneckerIdentity by passing like parameter
KI1 = KroneckerIdentity(like=KD)
create KroneckerIdentity by passing tensor_shape parameter
KI2 = KroneckerIdentity(tensor_shape=(5, 6))
x = np.random.normal(size=5 * 6)
assert np.allclose(KI1 @ x, x) assert np.allclose(KI2 @ x, x) ```
Deriving new operators
All four of these objects can be added or multiplied together arbitrarily to create new composite operators. In this way, they can be treated similarly to literal NumPy arrays.
```python import numpy as np from pykronecker import *
A = np.random.normal(size=(5, 5)) B = np.random.normal(size=(6, 6)) D = np.random.normal(size=(5, 6)) x = np.random.normal(size=5 * 6)
KP = KroneckerProduct([A, B]) KS = KroneckerSum([A, B]) KD = KroneckerDiag(D) KI = KroneckerIdentity(like=KP)
create a new composite operator!
new_operator1 = KP @ KD + KS - KI
print(new_operator1 @ x) ```
Other possible operations include transposing with .T, and multiplying/dividing by a scalar.
```python new_operator2 = 5 * KP.T - KS / 2
print(new_operator2 @ x) ```
Many basic operators can also be multipled element-wise just as with NumPy arrays.
```python new_operator3 = KS * KP
print(new_operator3 @ x) ```
Some operators (notably, not KroneckerSums) can be raised to a power element-wise
```python new_operator4 = KP ** 2
print(new_operator4 @ x) ```
Block operators
Block operators are composed of smaller operators which have been stacked into a set of blocks. In the example below, we create a new block operator KB which is composed of four other block operators.
```python import numpy as np from pykronecker import *
A = np.random.normal(size=(5, 5)) B = np.random.normal(size=(6, 6)) D = np.random.normal(size=(5, 6))
KP = KroneckerProduct([A, B]) KS = KroneckerSum([A, B]) KD = KroneckerDiag(D) KI = KroneckerIdentity(like=KP)
Create a block of pure KroneckerOperators
KB1 = KroneckerBlock([[KP, KD], [KI, KS]])
x1 = np.random.normal(size=5 * 6 * 2) print(KB1 @ x1) ```
We can also create block operators that contain a mixture of KroneckerOperators and NumPy arrays
```python
Create a block with a mixture of KroneckerOperators and ndarrays
M11 = KP M12 = np.ones((5 * 6, 5)) M21 = np.random.normal(size=(5, 5 * 6)) M22 = np.eye(5)
KB2 = KroneckerBlock([[M11, M12], [M21, M22]])
x2 = np.random.normal(size=5 * 6 + 5) print(KB2 @ x2)
```
Block diagonal matrices can also be created in a similar way
```python from pykronecker import KroneckerBlockDiag
KBD = KroneckerBlockDiag([KP, KS])
x3 = np.random.normal(size=5 * 6 * 2) print(KBD @ x3) ```
Other features
For operators that are products of KroneckerProducts, KroneckerDiags, or KroneckerIdentitys, we can find the inverse with .inv().
```python import numpy as np from pykronecker import *
A = np.random.normal(size=(5, 5)) B = np.random.normal(size=(6, 6)) D = np.random.normal(size=(5, 6)) x = np.random.normal(size=5 * 6)
KP = KroneckerProduct([A, B]) KS = KroneckerSum([A, B]) KD = KroneckerDiag(D) KI = KroneckerIdentity(like=KP)
find the inverse
M = (KP @ KD).inv() print(M @ x) ```
Basic indexing is supported for all operators. This will return literal numpy arrays. Advanced indexing is not supported.
```python J = KP.T + KS @ KD
print(J[0]) print(J[2:5]) print(J[2:8:3]) print(J[:, 2]) print(J[:, 2:5]) print(J[:, 2:8:3]) print(J[2, 5]) print(J[2:5, 2:8:3]) print(J[:]) print(J[:, :]) ```
Summing down an axis or over the whole matrix is supported for any opertor.
python
print(J.sum(0))
print(J.sum(1))
print(J.sum())
Any operator can also be converted to a literal array. This should only be used for small test purposes, as the arrays created can be very large.
python
print(J.to_array())
The matrix diagonal of most operators can be found with .diag(). This returns a one-dimensional array.
python
print(J.diag())
The conjugate transpose of any complex operator can be found with .H
```python
A_ = np.random.normal(size=(5, 5)) + 1j * np.random.normal(size=(5, 5)) B_ = np.random.normal(size=(6, 6)) + 1j * np.random.normal(size=(6, 6))
KP_ = KroneckerProduct([A, B])
print(KP_.H @ x) ```
Use with JAX
Operators and tensors can also be created from Jax arrays for accelerated computation when the pykronecker[jax] extra has been installed. Note that this is only available on Linux and MacOS.
```python import numpy as np import jax.numpy as jnp from pykronecker import KroneckerProduct
A = jnp.asarray(np.random.normal(size=(5, 5))) B = jnp.asarray(np.random.normal(size=(6, 6))) x = jnp.asarray(np.random.normal(size=5 * 6))
KP = KroneckerProduct([A, B])
print(KP @ x) ```
Owner
- Login: nickelnine37
- Kind: user
- Repositories: 4
- Profile: https://github.com/nickelnine37
JOSS Publication
PyKronecker: A Python Library for the Efficient Manipulation of Kronecker Products and Related Structures
Authors
Heriot-Watt University, United Kingdom
University of California Santa Barbara, United States of America
Heriot-Watt University, United Kingdom
Tags
Numpy Jax Kronecker product Kronecker sum linear system GPUGitHub Events
Total
- Watch event: 4
Last Year
- Watch event: 4
Committers
Last synced: 7 months ago
Top Committers
| Name | Commits | |
|---|---|---|
| nickelnine37 | e****1@g****m | 70 |
| Julian Karl Bauer | j****r@g****e | 3 |
Committer Domains (Top 20 + Academic)
Issues and Pull Requests
Last synced: 6 months ago
All Time
- Total issues: 3
- Total pull requests: 12
- Average time to close issues: about 2 hours
- Average time to close pull requests: 11 minutes
- Total issue authors: 3
- Total pull request authors: 2
- Average comments per issue: 4.0
- Average comments per pull request: 0.75
- Merged pull requests: 12
- Bot issues: 0
- Bot pull requests: 0
Past Year
- Issues: 1
- Pull requests: 0
- Average time to close issues: N/A
- Average time to close pull requests: N/A
- Issue authors: 1
- Pull request authors: 0
- Average comments per issue: 0.0
- Average comments per pull request: 0
- Merged pull requests: 0
- Bot issues: 0
- Bot pull requests: 0
Top Authors
Issue Authors
- JulianKarlBauer (1)
- qiyang-ustc (1)
- ThomasHilger (1)
Pull Request Authors
- nickelnine37 (12)
- JulianKarlBauer (2)
Top Labels
Issue Labels
Pull Request Labels
Packages
- Total packages: 1
-
Total downloads:
- pypi 279 last-month
- Total dependent packages: 0
- Total dependent repositories: 1
- Total versions: 7
- Total maintainers: 1
pypi.org: pykronecker
Tools for performing efficient Kronecker product-based matrix operations
- Homepage: https://github.com/nickelnine37/pykronecker
- Documentation: https://pykronecker.readthedocs.io/
- License: MIT
-
Latest release: 0.1.3
published over 2 years ago
Rankings
Maintainers (1)
Dependencies
- numpy ==1.21.5
- pytest ==7.1.2 development
- pytest-cov ==3.0.0 development
- tox ==3.25.1 development
- click ==8.1.3
- ghp-import ==2.1.0
- griffe ==0.22.0
- importlib-metadata ==4.12.0
- jinja2 ==3.1.2
- markdown ==3.3.7
- markdown-include ==0.6.0
- markupsafe ==2.1.1
- mergedeep ==1.3.4
- mkdocs ==1.3.0
- mkdocs-autorefs ==0.4.1
- mkdocstrings ==0.19.0
- mkdocstrings-python ==0.7.1
- packaging ==21.3
- pymdown-extensions ==9.5
- pyparsing ==3.0.9
- python-dateutil ==2.8.2
- pyyaml ==6.0
- pyyaml-env-tag ==0.1
- six ==1.16.0
- watchdog ==2.1.9
- zipp ==3.8.0
- actions/checkout v2 composite
- actions/setup-python v2 composite
- jax >=0.2.7
- jaxlib >=0.1.57
- numpy >=1.10
