Interactive Tabular Matrix Problems via Pseudoinverse Estimation
Project description
Interactive Tabular Matrix Problems via Pseudoinverse Estimation
Interactive Tabular Matrix Problems via Pseudoinverse Estimation (TMPinvi) provides an interactive wrapper for the tmpinv() function from the pytmpinv package, with options extending its functionality to pre- and post-estimation processing and streamlined incorporation of prior cell information. The Tabular Matrix Problems via Pseudoinverse Estimation (TMPinv) is a two-stage estimation method that reformulates structured table-based systems — such as allocation problems, transaction matrices, and input–output tables — as structured least-squares problems. Based on the Convex Least Squares Programming (CLSP) framework, TMPinv solves systems with row and column constraints, block structure, and optionally reduced dimensionality by (1) constructing a canonical constraint form and applying a pseudoinverse-based projection, followed by (2) a convex-programming refinement stage to improve fit, coherence, and regularization (e.g., via Lasso, Ridge, or Elastic Net). All calculations are performed in numpy.float64 precision.
Installation
pip install pytmpinvi
Quick Example
import numpy as np
import pandas as pd
import statsmodels.formula.api as smf
from scipy.stats import norm
from tmpinvi import tmpinvi
# Reproducibility
rng = np.random.default_rng(123456789)
iso2 = ["CN", "DE", "JP", "NL", "US"]
T = 10
year = np.arange(
pd.Timestamp.today().year - T + 1,
pd.Timestamp.today().year + 1
)
m = len(iso2)
# Construct panel-like data frame
df = pd.MultiIndex.from_product(
[year, iso2],
names=["year", "iso2"]
).to_frame(index=False)
df = df.sort_values(["year", "iso2"]).reset_index(drop=True)
ex_cols = [f"EX_{c}" for c in iso2]
for nm in ex_cols:
df[nm] = np.nan
df["EX"] = np.nan
df["IM"] = np.nan
X_true = {}
# Generate true transaction matrices and incomplete observations
for t, y in enumerate(year, start=1):
scale = 1000.0 * (1.05 ** (t - 1))
X = rng.uniform(0.0, scale, size=(m, m))
np.fill_diagonal(X, 0.0)
X_true[str(y)] = X.copy()
rows = df["year"].eq(y)
df.loc[rows, "EX"] = X.sum(axis=1)
df.loc[rows, "IM"] = X.sum(axis=0)
miss = rng.uniform(size=(m, m)) > 0.5
X_obs = X.copy()
X_obs[miss] = np.nan
df.loc[rows, ex_cols] = X_obs
# Construct upper bounds using linear models
cv = norm.ppf(0.975)
for nm in ex_cols:
fit = smf.ols(f"{nm} ~ year * C(iso2)", data=df).fit()
pr = fit.get_prediction(df)
sf = pr.summary_frame(alpha=0.05)
ub = sf["mean"].to_numpy() + cv * sf["mean_se"].to_numpy()
ub[ub < 0.0] = np.nan
df[f"_{nm}_lb"] = 0.0
df[f"_{nm}_ub"] = ub
def make_bounds(lb, ub):
return [(a, b) for a, b in zip(lb, ub)]
df_out = df.copy()
lb_cols = [f"_EX_{c}_lb" for c in iso2]
ub_cols = [f"_EX_{c}_ub" for c in iso2]
# Iterative completion/refinement
for step in range(1, 3):
for y in year:
idx = df_out["year"].eq(y)
d = df_out.loc[idx].copy()
ival = d[ex_cols].to_numpy(dtype=np.float64)
lb = d[lb_cols].to_numpy(dtype=np.float64).ravel(order="C")
ub = d[ub_cols].to_numpy(dtype=np.float64).ravel(order="C")
fit = tmpinvi(
ival=ival,
ibounds=make_bounds(lb, ub),
b_row=d["EX"].to_numpy(dtype=np.float64),
b_col=d["IM"].to_numpy(dtype=np.float64),
alpha=1.0,
update=True,
)
df_out.loc[idx, ex_cols] = fit.data
# Drop temporary bound columns
drop_cols = df_out.filter(regex=r"^_EX_.*_(lb|ub)$").columns
df_out = df_out.drop(columns=drop_cols)
df_out
User Reference
For comprehensive information on the estimator's capabilities, advanced configuration options, and implementation details, please refer to the pytmpinv module, on which TMPinvi is based.
To ensure cross-platform reproducibility, all CLSP implementations use a modified condition number function based on singular values, with a relative cutoff equal to cond_tolerance * the largest singular value.
TMPinvi Parameters:
ival : array_like or None, default = None
Prior information on known cell values. If supplied and not entirely missing, ival is flattened and used to construct b_val and the corresponding identity-subset model matrix M internally. Missing entries (np.nan) are ignored. If all entries of ival are np.nan, no prior information is used and b_val and M are not passed to tmpinv(). When ival is provided, it overrides any b_val or M arguments supplied through keyword arguments.
ibounds : tuple, list, or None, default = None
Dynamic cell-value bounds passed to tmpinv(bounds=...). The object supplied to ibounds may be created or modified programmatically (for example within preestimation()). If a single pair such as (low, high) is provided, it is applied uniformly to all cells. Alternatively, a list of pairs may be supplied to specify cell-specific bounds with others set to None or np.nan. When ibounds is not None, it overrides any bounds argument supplied through keyword arguments.
preestimation : callable or None, default = None
A function executed prior to model estimation. If supplied, it is called as preestimation(ival) and may perform arbitrary preparatory steps, such as constructing dynamic bounds or modifying objects in the calling environment. The return value is ignored.
postestimation : callable or None, default = None
A function executed after model estimation. For a full model, it is called as postestimation(model). For reduced (block-wise) models, it is called as postestimation(model_i, i) for each block index i. The return value is ignored.
update : bool, default = False
If True and ival is supplied, missing entries (np.nan) in ival are replaced by the corresponding fitted values from result.x. The updated matrix is returned in the result.data component. If False, the data component contains the fitted solution matrix result.x.
TMPinv Parameters:
S : array_like of shape (m + p, m + p), optional
A diagonal sign slack (surplus) matrix with entries in {0, ±1}.
- 0 enforces equality (==
b_roworb_col), - 1 enforces a lower-than-or-equal (≤) condition,
- –1 enforces a greater-than-or-equal (≥) condition.
The first m diagonal entries correspond to row constraints, and the remaining p to column constraints. Please note that, in the reduced model, S is ignored: slack behavior is derived implicitly from block-wise marginal totals.
M : array_like of shape (k, m * p), optional
A model matrix with entries in {0, 1}. Each row defines a linear restriction on the flattened solution matrix. The corresponding right-hand side values must be provided in b_val. This block is used to encode known cell values. Please note that, in the reduced model, M must be a unique row subset of an identity matrix (i.e., diagonal-only). Arbitrary or non-diagonal model matrices cannot be mapped to reduced blocks, making the model infeasible.
b_row : array_like of shape (m,)
Right-hand side vector of row totals. Please note that both b_row and b_col must be provided.
b_col : array_like of shape (p,)
Right-hand side vector of column totals. Please note that both b_row and b_col must be provided.
b_val : array_like of shape (k,)
Right-hand side vector of known cell values.
i : int, default = 1
Number of row groups.
j : int, default = 1
Number of column groups.
zero_diagonal : bool, default = False
If True, enforces the zero diagonal.
reduced : tuple of (int, int), optional
Dimensions of the reduced problem. If specified, the problem is estimated as a set of reduced problems constructed from contiguous submatrices of the original table. For example, reduced = (6, 6) implies 5×5 data blocks with 1 slack row and 1 slack column each (edge blocks may be smaller).
symmetric : bool, default = False
If True, enforces symmetry of the estimated solution matrix as: x = 0.5 * (x + x.T)
Applies to TMPinviResult.x only. For TMPinviResult.model symmetry, add explicit symmetry constraints to M in a full-model solve instead of using this flag.
bounds : sequence of (low, high), optional
Bounds on cell values. If a single tuple (low, high) is given, it is applied to all m * p cells. Example: (0, None).
replace_value : float or None, default = np.nan
Final replacement value for any cell in the solution matrix that violates the specified bounds by more than the given tolerance.
tolerance : float, default = square root of machine epsilon
Convergence tolerance for bounds.
iteration_limit : int, default = 50
Maximum number of iterations allowed in the refinement loop.
CLSP Parameters:
r : int, default = 1
Number of refinement iterations for the pseudoinverse-based estimator.
Z : np.ndarray or None
A symmetric idempotent matrix (projector) defining the subspace for Bott–Duffin pseudoinversion. If None, the identity matrix is used, reducing the Bott–Duffin inverse to the Moore–Penrose case.
final : bool, default = True
If True, a convex programming problem is solved to refine zhat. The resulting solution z minimizes a weighted L1/L2 norm around zhat subject to Az = b.
alpha : float, list[float] or None, default = None
Regularization parameter (weight) in the final convex program:
- α = 0: Lasso (L1 norm)
- α = 1: Tikhonov Regularization/Ridge (L2 norm)
- 0 < α < 1: Elastic Net
If a scalar float is provided, that value is used after clipping to [0, 1].
If a list/iterable of floats is provided, each candidate is evaluated via a full solve, and the α with the smallest NRMSE is selected.
If None, α is chosen, based on an error rule: α = min(1.0, NRMSE_{α = 0} / (NRMSE_{α = 0} + NRMSE_{α = 1} + tolerance))
cond_tolerance : float or None, default = None
Singular-value cutoff for the custom condition number function.
If None, the implementation uses an internal relative cutoff of 1e-14.
*args, **kwargs : optional
CVXPY arguments passed to the CVXPY solver.
Returns: TMPinviResult
TMPinviResult.full : bool
Indicates if this result comes from the full (non-reduced) model.
TMPinviResult.model : CLSP or list of CLSP
A single CLSP object in the full model, or a list of CLSP objects for each reduced block in the reduced model.
TMPinviResult.x : np.ndarray
Final estimated solution matrix of shape (m, p).
TMPinviResult.data : np.ndarray
Processed output matrix of shape (m, p). If update=True and ival is supplied in tmpinvi(), data contains ival with missing entries replaced by fitted values from x. Otherwise, data contains the fitted solution matrix x.
TMPinviResult.summarize(i, display)
An alias of TMPinviResult.summary().
TMPinviResult.summary(i, display)
Return or print a summary of the underlying CLSP result, where i : int, default = None is the index of a reduced-block model in TMPinviResult.model.
Bibliography
Bolotov, I. (2025). CLSP: Linear Algebra Foundations of a Modular Two-Step Convex Optimization-Based Estimator for Ill-Posed Problems. Mathematics, 13(21), 3476. https://doi.org/10.3390/math13213476
License
MIT License — see the LICENSE file.
Project details
Release history Release notifications | RSS feed
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 pytmpinvi-4.0.0.tar.gz.
File metadata
- Download URL: pytmpinvi-4.0.0.tar.gz
- Upload date:
- Size: 8.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
62312fcef0b24c08174f29d6b808d7a2012e217446a2461f1980168845ba0991
|
|
| MD5 |
25fe58ff1a60e75255ae94e9a0ecad58
|
|
| BLAKE2b-256 |
1cf53aec1544210038cdd11a73e01d082a96ef0068bd0ae85a54eb98da51c680
|
File details
Details for the file pytmpinvi-4.0.0-py3-none-any.whl.
File metadata
- Download URL: pytmpinvi-4.0.0-py3-none-any.whl
- Upload date:
- Size: 9.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cf54cbae667b264b1c94afe22a1ba02f280721101faf1d0310f8165e3961deac
|
|
| MD5 |
fcc9621f0bec1dcfab87412882f4040f
|
|
| BLAKE2b-256 |
98e3a8f118f82483af4eccdd6d789e1ea6f7fc02f173175344fb1f87244b0238
|