NormalMPO¶
MPO subclass that tracks the overall scale factor separately from the unit-normed internal tensors.
NormalMPO
¶
NormalMPO(
tensors: List[Tensor],
log_scale: float = 0.0,
bc: str = "OBC",
center: Optional[int] = None,
)
Bases: MPO
MPO with a separately tracked overall scale magnitude.
Represents the physical operator as exp(log_scale) × mpo, where the
internal MPO satisfies mpo.norm() ≈ 1 after compact() or
from_mpo(). Tracking the magnitude in log form (rather than as a raw
float) keeps it representable even when it is far outside float64
range — this matters for XTRG, where Tr[ρ] is repeatedly squared and
can reach ~10^500 or beyond at low temperature.
The sign of the physical operator lives directly in the internal
MPO's tensor data. This works because mpo.norm() ≈ 1 does not pin
down a sign (-X and X have the same Frobenius norm), and because
canonicalization (compact(), canonical()) is an exact gauge
transform that cannot change the value of any fully-contracted
quantity such as Tr[ρ] — so whatever sign is baked into the tensors
survives compaction unchanged. Whenever an operation needs to apply a
sign flip (e.g. __mul__ by a negative scalar, or the alternating
(-β)^n terms in thermal_mpo's Taylor accumulation), it is folded
into site 0's tensor by multiplying it by -1.0. Site 0 is used by
convention because, in an OBC chain, its left bond is the trivial
boundary dimension, making it generically the cheapest tensor to
touch. Because tensor contraction and addition are linear, sign flips
baked into the operands' tensors propagate correctly through
__matmul__/__add__ — see those methods' docstrings.
All arithmetic operations (+, @, *) preserve this representation,
updating log_scale analytically (by addition, never by exponentiating
a possibly-astronomical magnitude) without altering the unit-norm
convention until compact() is called explicitly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tensors
|
List[Tensor]
|
Non-empty list of site tensors, each with 4 axes
|
required |
log_scale
|
float
|
|
0.0
|
bc
|
str
|
Boundary condition: |
'OBC'
|
center
|
Optional[int]
|
Orthogonality center site index, or |
None
|
Attributes¶
log_scale
property
¶
log(scale) (read-only). The primary, overflow-safe representation.
scale
property
¶
Overall scale magnitude as a raw, non-negative float (read-only).
Reconstructs exp(log_scale). For magnitudes too large to
represent in float64, math.exp raises OverflowError; this is
caught here and inf is returned instead, since a getter should
never crash. Callers needing to represent genuinely astronomical
magnitudes should use log_scale directly instead of this
property.
Methods:¶
from_mpo
classmethod
¶
Create a NormalMPO from a plain MPO.
The input MPO is not modified. The returned object has
mpo.norm() ≈ 1 and scale == original_frobenius_norm.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mpo
|
MPO
|
Source MPO. May have any canonical form. |
required |
Returns:
| Type | Description |
|---|---|
NormalMPO
|
Normalized copy with |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the source MPO has exactly zero or non-finite norm. |
scale_by
¶
Multiply the tracked scale magnitude in-place by exp(log_scale_delta).
This is the safe, in-place counterpart to __init__: it combines
two log-magnitudes by addition rather than ever exponentiating
either one, so it cannot overflow even when the combined scale
would be far outside float64 range. Used by XTRG's _fit_mpo to
fold pre-computed physical scale magnitudes into an
already-compacted NormalMPO.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
log_scale_delta
|
float
|
|
required |
compact
¶
Compress bond dimensions in-place, keeping the scale consistent.
Performs the same two-sweep canonicalization as MPO.compact(), but
folds the extracted norm into log_scale instead of redistributing
it across the site tensors. After the call, the internal MPO is
approximately unit-normed and log_scale absorbs the physical
magnitude.
Bond arrows may be reoriented by the QR/SVD sweeps; this method
restores the standard IN/OUT convention via capcup after the
sweeps, so that subsequent trace(), __add__, and __matmul__
calls work correctly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
trunc
|
Optional[dict]
|
Truncation options forwarded to |
None
|
log_trace
¶
Compute \(\log|\operatorname{Tr}[\rho]|\) and its sign.
Performs a left-to-right transfer-matrix sweep. At each site the
physical indices (phys_in and phys_out, sharing itag s{i:02d}
with opposite directions) are traced using Nicole's trace function,
yielding a 2nd-order bond tensor. Adjacent bond tensors are chained with
einsum.
This is the overflow-safe counterpart to trace(): it never forms
Tr[ρ] itself, so it remains finite even when the physical trace
would be far outside float64 range (as happens for XTRG runs at low
temperature, where Tr[ρ] can reach ~10^500).
Returns:
| Type | Description |
|---|---|
float
|
|
float
|
|
trace
¶
Compute the MPO trace \(\operatorname{Tr}[\rho]\).
Thin wrapper over log_trace() that exponentiates back to a raw
magnitude. For magnitudes too large to represent in float64, ±inf
is returned instead of raising (see scale). Prefer log_trace()
directly when the trace magnitude may be extreme.
Returns:
| Type | Description |
|---|---|
float
|
|
Notes¶
Representation. A NormalMPO stores the physical operator as exp(log_scale) × mpo, where after compact() or from_mpo() the internal MPO satisfies norm() == 1. Tracking the magnitude in log form as log_scale keeps it representable even far outside float64 range — this matters for XTRG, where Tr[ρ] is repeatedly squared and can reach ~10^500 or beyond at low temperature. The sign of the physical operator lives directly in the internal tensor data: mpo.norm() == 1 never pins down a sign (-X and X have the same Frobenius norm), so a sign flip (e.g. __mul__ by a negative scalar, or the alternating (-β)^n terms in thermal_mpo's Taylor accumulation) is folded into site 0's tensor by negating it — site 0 is used by convention since, in an OBC chain, its trivial boundary bond makes it the cheapest tensor to touch. Canonicalization (compact(), canonical()) is an exact gauge transform, so a sign baked into the tensors survives it unchanged.
Arithmetic. The operators @, +, *, and * (right) build new raw tensors and update log_scale analytically (by addition, never by exponentiating a possibly-astronomical magnitude) without performing any canonicalization. Since tensor contraction and addition are linear, sign carried by the operands' tensor data propagates through @ and + automatically. Call compact() explicitly afterward to restore the unit-norm invariant and truncate bond dimension. scale_by() provides the same safe, log-domain update for mutating an existing NormalMPO's scale magnitude in place.
Overflow-safe trace. trace() returns the raw magnitude Tr[ρ] and is convenient for moderate magnitudes, but may return ±inf once the true value exceeds float64 range. log_trace() returns (log|Tr[ρ]|, sign) instead, and never overflows in this regime — prefer it whenever the trace magnitude may be extreme (as in XTRG's cooling loop). The sign in this pair is derived by contracting the tensor data.
Dtype preservation. All arithmetic keeps real-valued (float64) tensors real. In __add__, the per-site scale factor is α^{1/L} where α = other.scale / self.scale is a non-negative magnitude ratio, so no complex roots arise.
See Also¶
- MPO — base class.
- thermal_mpo — constructs
exp(-βH)as aNormalMPO. - observe — accepts a
NormalMPOasstate; computesTr[ρ O] / Tr[ρ].