Metadata-Version: 2.4
Name: spintensor
Version: 0.1.0
Summary: Build linear constraints for tensors under transformations using Kronecker products
Author: Connor1y
License-Expression: MIT
Project-URL: Homepage, https://github.com/Connor1y/spintensor
Project-URL: Repository, https://github.com/Connor1y/spintensor
Project-URL: Issues, https://github.com/Connor1y/spintensor/issues
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Topic :: Scientific/Engineering :: Mathematics
Classifier: Topic :: Scientific/Engineering :: Physics
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.7
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.19.0
Dynamic: license-file

# SpinTensor

A Python library for constructing linear constraints on tensors under transformations using Kronecker products. Supports operation-dependent T-parity, P-parity, and index-permutation symmetries, with automatic nullspace computation via SVD and LaTeX output.

## Overview

This library constructs linear constraints for order-r tensors in dimension n under spin-group operations `(Rs, Rr)` of the form:

```
Tᵢ₁…ᵢᵣ = sign_T · sign_P · Rrᵢ₁ⱼ₁ … Rrᵢᵣⱼᵣ Tⱼ₁…ⱼᵣ
```

where the **per-operation sign factors** are computed from matrix determinants:

```
sign_T = det(Rs) ** T_constraint
sign_P = det(Rr) ** P_constraint
```

- `Rs` is the **spin-space matrix**: `det(Rs) = -1` contributes an effective time-reversal sign for T-odd tensors (`T_constraint = 1`).
- `Rr` is the **real-space matrix** applied to every tensor axis: `det(Rr) = -1` contributes a parity sign for P-odd tensors (`P_constraint = 1`).
- The real-space action `Rr ⊗ Rr ⊗ ... ⊗ Rr` always drives the tensor transformation; `Rs` only contributes a sign.

The library builds constraint matrices, computes their nullspace using SVD (not row reduction), and outputs readable LaTeX relations with automatic index-to-symbol mapping.

## Installation

```bash
pip install spintensor
```

Or install in development mode:

```bash
pip install -e .
```

## Features

- **Operation-Dependent Sign Logic**: Per-operation sign factors `sign_T = det(Rs)**T_constraint` and `sign_P = det(Rr)**P_constraint` (not global fixed values)
- **Kronecker Product Transformations**: Efficiently build transformation matrices using Kronecker products of the real-space matrix `Rr`
- **T-parity and P-parity**: Support for time reversal and spatial parity symmetries
- **Index Permutation Symmetries**: Handle symmetric, antisymmetric, and general permutation symmetries
- **SVD-based Nullspace**: Numerically stable nullspace computation using Singular Value Decomposition
- **LaTeX Output**: Automatic generation of readable constraint relations with smart index notation

## Quick Start

```python
import numpy as np
from spintensor import solve_tensor_constraints

# Define operations in [Rs, Rr] format
# Rs: spin-space matrix; Rr: real-space matrix
n = 3  # dimension
r = 2  # tensor rank

# Spin reflection (det(Rs) = -1) in spin space, identity in real space
Rs = np.diag([1., 1., -1.])  # det = -1
Rr = np.eye(3)               # det = +1

# T-odd tensor: sign_T = det(Rs)**1 = -1
A, nullspace, relations, components = solve_tensor_constraints(
    [[Rs, Rr]], n, r, T_constraint=1, P_constraint=0
)

print(f"Independent components: {nullspace.shape[1]}")
for rel in relations:
    print(f"  {rel}")
```

## Input Formats

All transformation formats are supported. The `[Rs, Rr]` format is the primary API for the new per-operation sign convention:

### Format 1: `[Rs, Rr]` (recommended)

```python
# Each entry is [spin_matrix, real_space_matrix]
transformations = [
    [Rs1, Rr1],  # sign_T = det(Rs1)**T_constraint, sign_P = det(Rr1)**P_constraint
    [Rs2, Rr2],
]
```

With `spin_axes` set, `Rs` is also applied to the specified tensor axes.

### Format 2: `[Tflag, Rr]` (explicit anti-unitary flag)

```python
# Tflag=True means the operation is anti-unitary (contributes sign_T=-1 when T_constraint=1)
transformations = [
    [False, Rr_unitary],    # sign_T = +1
    [True, Rr_antiunitary], # sign_T = -1
]
```

### Format 3: `Rr` only (no spin component)

```python
# Single numpy array: treated as Rs = identity (det=+1, no T sign)
transformations = [Rr1, Rr2]  # sign_T = +1 always
```

### Format 4: Legacy `(R, s_T, s_P)` tuple

```python
# Explicit parity values (backward compatible)
transformations = [(R, s_T, s_P)]
```

## API Reference

### Main Function

#### `solve_tensor_constraints(transformations, n, r, symmetries=None, antisymmetries=None, tol=1e-10, spin_axes=None, T_constraint=0, det_tol=1e-6, symbol='\\sigma', extra_constraints=None, P_constraint=0)`

Complete solver for tensor constraints.

**Parameters:**
- `transformations`: List of transformations in any supported format
- `n`: int, dimension of the tensor space
- `r`: int, order (rank) of the tensor
- `symmetries`: list of tuples, optional index permutations for symmetric constraints (T_perm = T)
- `antisymmetries`: list of tuples, optional index permutations for antisymmetric constraints (T_perm = -T)
- `tol`: float, tolerance for numerical operations
- `spin_axes`: optional tuple of axis indices for spin axes (used with [Rs, Rr] format)
- `T_constraint`: int, exponent for time-reversal sign. `sign_T = det(Rs)**T_constraint`. Use `1` for T-odd tensors (default `0`)
- `P_constraint`: int, exponent for parity sign. `sign_P = det(Rr)**P_constraint`. Use `1` for P-odd/axial tensors (default `0`)
- `det_tol`: float, tolerance for determinant comparison to ±1

**Returns:**
- `constraint_matrix`: numpy array, full constraint matrix
- `nullspace_basis`: numpy array, columns are basis vectors
- `latex_relations`: list of strings, formatted LaTeX relations
- `tensor_components`: nested list of component expressions

### Convenience Functions

#### `solve_qmd(transformations, n=3, tol=1e-10)`

Solve for QMD (quadrupole moment derivative) tensors: T-odd, rank-3, with last-two-index symmetry.

#### `solve_imd(transformations, n=3, tol=1e-10)`

Solve for IMD tensors: T-odd, rank-3, with both last-two and first-two index symmetry.

#### `solve_bcd(transformations, n=3, tol=1e-10, use_cyclic_constraint=True)`

Solve for BCD (Berry curvature dipole) tensors: T-even, rank-3, with last-two-index symmetry and optional cyclic sum constraint.

### Core Functions

#### `build_constraint_matrix(transformations, n, r, ...)`

Build the full constraint matrix from transformations and symmetries.

#### `compute_nullspace_svd(A, tol=1e-10)`

Compute nullspace of matrix A using SVD.

#### `format_latex_relations(nullspace, n, r, tol=1e-10)`

Format nullspace basis vectors as readable LaTeX relations.

## Examples

### Example 1: AHE Conductivity (T-odd, polar, rank-2)

```python
import numpy as np
from spintensor import solve_tensor_constraints

# Operations in [Rs, Rr] format
# T-odd tensor: sign_T = det(Rs)**1

ops = [
    [np.diag([1., 1.,  1.]), np.diag([1., 1., 1.])],   # identity
    [np.diag([1., 1., -1.]), np.diag([1., 1.,-1.])],   # z-mirror: det(Rs)=-1 → s_T=-1
]

A, nullspace, relations, comps = solve_tensor_constraints(
    ops, n=3, r=2, T_constraint=1, P_constraint=0
)

print(f"Independent components: {nullspace.shape[1]}")
```

### Example 2: QMD Tensor with Spin-Group Operations

```python
import numpy as np
from spintensor import solve_qmd

# Operations in [Rs, Rr] format
# Mirror op: det(Rs) = -1 → T-odd sign applies
s = np.sqrt(3) / 2
ops = [
    [np.eye(3), np.eye(3)],
    [np.array([[-0.5, s, 0], [s, 0.5, 0], [0, 0, 1]]),
     np.array([[-0.5, s, 0], [s, 0.5, 0], [0, 0, 1]])],
    # ... more operations
]

A, nullspace, relations, comps = solve_qmd(ops)
```

### Example 3: P-odd (Axial) Tensor

```python
import numpy as np
from spintensor import solve_tensor_constraints

# P_constraint=1: sign_P = det(Rr)**1
# For a reflection with det(Rr)=-1, this adds a -1 sign factor
R_reflect = np.diag([1., 1., -1.])

A, nullspace, relations, comps = solve_tensor_constraints(
    [[np.eye(3), R_reflect]], n=3, r=2,
    T_constraint=0, P_constraint=1
)
```

### Example 4: Legacy Format (Backward Compatible)

```python
import numpy as np
from spintensor import solve_tensor_constraints

R_z = np.array([[-1, 0, 0], [0, -1, 0], [0, 0, 1]])

# Legacy tuple format: (R, s_T, s_P) — explicit parity values
transformations = [(R_z, 1.0, 1.0)]
A, nullspace, relations, comps = solve_tensor_constraints(
    transformations, n=3, r=2, symmetries=[(1, 0)]
)
```

## Mathematical Background

### Operation-Dependent Sign Logic

For each operation `(Rs, Rr)`, the transformation matrix acting on the flattened tensor is:

```
M = sign_T · sign_P · (Rr ⊗ Rr ⊗ ... ⊗ Rr)
```

where:

```
sign_T = det(Rs) ** T_constraint
sign_P = det(Rr) ** P_constraint
```

The constraint for a symmetry-invariant tensor is `(M - I) · T = 0`.

**Physical interpretation:**
- `det(Rs) = -1`: The spin-space operation flips the spin (effective time-reversal character). For T-odd tensors (`T_constraint=1`), this contributes a factor of `-1`.
- `det(Rr) = -1`: The real-space operation is an improper rotation (parity-odd). For P-odd/axial tensors (`P_constraint=1`), this contributes a factor of `-1`.
- These factors are **per-operation** — each operation in the symmetry group can independently flip or not flip each parity sign.

### Index Symmetries

Index permutation symmetries impose additional constraints:
```
(P_perm - I) · T = 0   (symmetric)
(P_perm + I) · T = 0   (antisymmetric)
```

### Nullspace Computation

The solution space consists of all tensors satisfying the constraints. This is the nullspace of the combined constraint matrix A, computed via SVD:

```
A = U · Σ · Vᵀ
```

The nullspace is spanned by columns of V corresponding to zero singular values. The basis is further reduced to RREF form for clean, canonical coefficients.

## Output Format

Relations are output in LaTeX format with automatic index notation:
- For n ≤ 4: uses letters (x, y, z, w)
- For n > 4: uses numbers (1, 2, 3, ...)

Example output:
```
\sigma_{xxy} = \sigma_{xyx} = \sigma_{yxx} = -\sigma_{yyy}
\sigma_{xyz} = \sigma_{xzy} = -\sigma_{yxz} = -\sigma_{yzx}
```

## Requirements

- Python ≥ 3.7
- NumPy ≥ 1.19.0

## License

MIT License

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## Citation

If you use this library in your research, please cite:

```bibtex
@software{spintensor,
  title = {SpinTensor: Linear Constraints for Tensors Under Transformations},
  author = {Connor1y},
  year = {2026},
  url = {https://github.com/Connor1y/spintensor}
}
```
