DualPerspective
Maximum-entropy solutions to underdetermined linear systems, from Python.
Given a wide matrix A and measurements b, Ax = b has infinitely many solutions.
DualPerspective picks the nonnegative one closest — in Kullback-Leibler divergence — to a
prior q, while fitting the data to within a tolerance set by a regularization parameter.
It is a Python interface to
DualPerspective.jl,
which solves the dual problem with Newton-type methods.
Installation
pip install DualPerspective
Julia itself is installed automatically on first use, via juliacall. Nothing happens at import time; Julia starts lazily, on the first call that needs it.
Basic usage
import numpy as np
from DualPerspective import DPModel, solve
np.random.seed(42)
n, m = 200, 100 # solution dimension, number of measurements
tmp = np.random.rand(n)
x0 = np.pi * tmp / tmp.sum() # a nonnegative signal of total mass π
A = np.random.rand(m, n)
b = A @ x0 # measurements
model = DPModel(A, b, lam=1e-4)
x = solve(model)
print(f"sum of solution: {x.sum():.6f} (should be about {np.pi:.6f})")
The default solver estimates the total mass from the data rather than taking it as given, so the recovered solution sums to roughly π even though the prior sums to 1 — that is what the printed check tests.
solve returns a NumPy array. Pass full_output=True for the full result:
result = solve(model, full_output=True)
result.x # primal solution
result.status # 'optimal', 'max_iter', ...
result.iterations
result.elapsed_time
result.optimality # final ‖∇d(y)‖, the quantity the stopping rule tests
result.residual # covariance-weighted residual
result.primal_obj, result.dual_obj
result.trace # per-iteration history (a list of dicts), when the solver records one
result.to_pandas() # the trace as a DataFrame, if pandas is installed
model.solve(...) is equivalent to solve(model, ...). For a quick experiment,
rand_dp_model(m, n) builds a random model.
The model
DPModel(A, b, q=None, C=None, c=None, lam=None)
Minimizes (1/2λ)‖Ax - b‖²_{C⁻¹} + ⟨c, x⟩ + KL(x ‖ q) over nonnegative x of a given
total mass — the probability simplex when that mass is 1.
| Argument | Shape | Meaning | Default |
|---|---|---|---|
A |
(m, n) |
Forward operator | — |
b |
(m,) |
Measurements | — |
q |
(n,) |
Prior | uniform, summing to 1 |
C |
(m, m) |
Covariance weighting the residual b - Ax |
identity |
c |
(n,) |
Linear cost | all -1 |
lam |
scalar | Regularization parameter (also accepted as λ) |
√eps |
Integer and single-precision arrays are accepted and converted to double precision. All arrays are copied into Julia, so mutating them afterwards does not change the model.
lam and scale are settable properties. scale is the total mass of the solution:
model.lam = 1e-4 # regularization parameter
model.scale = 2.0 # total mass
Choosing a solver
solve(model, method="sequential", atol=1e-6, rtol=1e-6,
t=None, verbose=False, logging=0, full_output=False)
method |
Algorithm |
|---|---|
"sequential" |
Sequential scaling (default) |
"trust-region" |
Trust-region Newton-CG |
"level-set" |
Level-set method |
"adaptive-level-set" |
Adaptive level-set method |
"self-scaled" |
Self-scaled Gauss-Newton, for unknown total mass |
The solver stops when ‖∇d(y)‖ < atol + rtol*‖b‖.
tsets the total mass and defaults tosum(q). The"trust-region"and"self-scaled"methods determine the mass themselves and reject it.verboseprints root-finding progress ("sequential"only);loggingsets solver log verbosity,0being silent.- Unrecognized keywords are passed straight through to the Julia solver.
Versioning
The PyPI package and the Julia package version independently: each wheel pins one exact Julia release and is tested against it, so the two version numbers differ by design.
import DualPerspective
DualPerspective.__version__ # the Python package
DualPerspective.version() # the Julia package it pins
Diagnosing problems
python -m DualPerspective.doctor
Prints the Python, Julia and resolved Julia package versions, the relevant environment variables, and runs a small solve. Include its output in any bug report.
Julia errors surface as DualPerspectiveError, whose message is the first line of the Julia
exception; the full Julia backtrace is on .julia_traceback, and the original exception is
chained as __cause__.
Running on a cluster
- Put the Julia depot on fast local storage. Precompilation is very sensitive to
filesystem latency, and
~/.juliaon NFS is usually slow and quota-limited:export JULIA_DEPOT_PATH=/local/scratch/$USER/julia_depot
- Resolve before going offline. Import the package once on a login node with the same
JULIA_DEPOT_PATHbefore submitting jobs to nodes without network access. - Shared environments.
PYTHON_JULIACALL_PROJECTpoints juliacall at an existing Julia project, which must already have a matching PythonCall.jl. Do not mutate an admin-managed project. - Threads and signals.
PYTHON_JULIACALL_HANDLE_SIGNALS=yesavoids segfaults from Julia's garbage collector in multi-threaded programs, but interferes with Python's own signal handling (including Ctrl-C). It is not set by default; enable it only if you need it.
License
MIT.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dualperspective-0.2.2.tar.gz.
File metadata
- Download URL: dualperspective-0.2.2.tar.gz
- Upload date:
- Size: 18.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3463d541a27da58db0fa122063a1e692c89e6cedd0f3390149361f7bc4a82326
|
|
| MD5 |
8a716f434c14a2a3eb6d6710021c4bec
|
|
| BLAKE2b-256 |
a9fe91e965c0a5ff72b5a6adf8df2611e395fd627112072ad998bf77feff1b75
|
File details
Details for the file dualperspective-0.2.2-py3-none-any.whl.
File metadata
- Download URL: dualperspective-0.2.2-py3-none-any.whl
- Upload date:
- Size: 15.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3d5b3eab3578771526e251f15bc481eabda3526ecbf236fe4c001773bb4a3b8d
|
|
| MD5 |
2f4be3bbc984f81323d7cf57f0f64330
|
|
| BLAKE2b-256 |
48b49ea2bb3c65d8cf7a9b67882f204c630b76c8b4356c3550b85b101764529d
|