Getting Started¶
FracESSA accepts exact symmetric rational matrices through either Python or the command line. Both interfaces call the same C++ analyzer and return the same mathematical counts.
Install¶
Python 3.11 through 3.14 users can install the native extension and Python API from PyPI:
python -m pip install pyfracessa
Parquet output is optional:
python -m pip install "pyfracessa[parquet]"
Standalone command-line binaries for Linux, macOS, and Windows are available on the release page. To build the CLI and Python extension from source instead:
install a C++17 compiler, CMake 3.18 or newer, Python 3.11 or newer with development headers, GMP, MPFR, and FLINT;
use Git with submodules enabled; and
allow the first CMake configure to download
argparse,pybind11,spdlog, and GoogleTest.
git clone --recurse-submodules https://github.com/reinhardullrich/fracessa.git
cd fracessa
cmake -S cpp -B cpp/build -DCMAKE_BUILD_TYPE=Release
cmake --build cpp/build --parallel
The resulting command-line program is cpp/build/fracessa and the Python extension is cpp/build/fracessa_core.*.
Choose a method¶
Every call requires an explicit method; there is no default.
Method |
Intended use |
Numerical contract |
|---|---|---|
|
Complete, correctness-first analysis |
Uses exact arithmetic for every candidate and stability decision. |
|
Faster analysis when completeness is not required |
Uses a binary64 candidate filter. Every surviving candidate and every final stability decision is checked exactly, but an early floating-point rejection can miss a valid candidate. |
fast switches the complete matrix to exact candidate search when preparation detects a dangerous numerical case.
The result then records the reason in safe_fallback. An inconclusive solve for only one support is also retried exactly, but that
local retry does not set safe_fallback. A candidate returned by fast is therefore a genuine, exactly verified candidate; the
risk is an incomplete result because the floating-point filter can reject a valid support too early.
Encode a matrix¶
Every input has the form:
dimension#comma-separated-values
Values can be integers, fractions, finite decimals, or scientific numbers, such as -3/5, -0.6, or -6e-1. Every form is
parsed as an exact rational number. A fraction’s denominator must be a positive integer; put an optional sign before the numerator,
writing -1/2 rather than 1/-2. There are two accepted value layouts.
General symmetric matrix¶
Write the upper triangle row by row. For
the input is:
3#a11,a12,a13,a22,a23,a33
Exactly n(n+1)/2 values are required.
Compact circular-symmetric matrix¶
A circular-symmetric matrix is unchanged when all row and column indices are shifted cyclically by the same amount. Because the matrix is also symmetric, an entry depends only on the circular distance
For dimensions two and larger, compact input supplies the common diagonal and one payoff for each positive circular distance:
n#c0,c1,c2,...,c_floor(n/2)
Exactly floor(n/2)+1 values are required. The first value \(c_0\) fills every diagonal entry; \(c_k\) fills entries at
circular distance \(k\). FracESSA does not normalize or otherwise modify these values.
For example, 5#2,3,5 expands to a 5-by-5 matrix whose first row is 2,3,5,5,3. FracESSA recognizes the shorter value count
automatically and applies its circular-symmetry reductions. Dimension one uses the full upper-triangular layout because its compact
and full forms would both contain one value and therefore cannot be distinguished.
Matrix files¶
The CLI accepts a file path in place of inline input. The file can contain either dimension#values or a Matrix Market array
or coordinate matrix declared symmetric. Matrix Market integer, real, pattern, and complex fields are accepted; complex
entries must have zero imaginary part. Decimal and scientific values remain exact. Slash fractions are available only in
dimension#values input.
Python accepts the same two contents in Matrix.matrix; it does not interpret that string as a file path. Use
Path.read_text() to load a file. Python also accepts a values-only string when the matrix metadata contains an integer
dimension:
Matrix(7, "-1,0,0,-2,0,-3", metadata={"dimension": 3})
Run one matrix from Python¶
from pyfracessa import Matrix, RunConfig, run
matrix = Matrix(1, "3#-1,0,0,-2,0,-3")
result = run(
"safe",
matrix,
config=RunConfig(include_candidates=True),
run_id="example",
)
print(result["status"])
print(result["ess_count"])
print(result["ess_structure"])
print(result["candidates"])
The result is a plain dictionary. candidate_count and ess_count are the totals found by the selected method, and
candidate_structure and ess_structure divide those totals by support size. These four fields are always returned. The ESS
total is complete with safe; candidate totals follow FracESSA’s ESS-search semantics and are not a census of every degenerate or
pruned-superset equilibrium. fast can miss valid supports. include_candidates=True also returns each stored candidate
representative; it does not change the counts or the analysis.
Check status before consuming a result. 0 means success, 1 means invalid matrix input, 4 means an execution error,
and 255 means an unexpected internal error. A nonzero status is accompanied by error_message. elapsed_ns measures only
the native analyzer with a monotonic clock; Python scheduling and output writing are excluded. FracESSA deliberately imposes no
per-matrix timeout.
Run one matrix from the command line¶
The general form for inline input is:
./cpp/build/fracessa [OPTIONS] METHOD "DIMENSION#VALUES"
For example:
./cpp/build/fracessa safe --candidates "3#-1,0,0,-2,0,-3"
The CLI always writes one JSON summary line. --candidates appends semicolon-separated candidate rows. Useful options are:
Option |
Meaning |
|---|---|
|
Print stored candidate representatives after the summary. |
|
Check the full support first. If the selected candidate route finds it and exact stability accepts it as an ESS, stop; otherwise continue the normal search without checking it twice. |
|
Write a rotating diagnostic trace to |
|
Attach a signed 64-bit matrix identifier to the summary and log. |
The decimal support masks in candidate rows use the least significant bit for the first matrix strategy. For example, support
7 has its first three bits set. On failure the CLI still writes a JSON summary to standard output, writes a diagnostic to
standard error, and exits unsuccessfully.
Run several matrices¶
run() processes matrices sequentially. run_multiprocessing() uses several worker processes, each analyzing one matrix at a
time. Results arrive in completion order, so use matrix_id rather than list position to identify them.
from pyfracessa import MPConfig, Matrix, run_multiprocessing
matrices = [
Matrix(1, "3#-1,0,0,-2,0,-3"),
Matrix(2, "3#4,13/2,1/2,5,11/2,3"),
]
if __name__ == "__main__":
for result in run_multiprocessing("safe", matrices, mp_config=MPConfig(workers=4)):
print(result["matrix_id"], result["ess_count"])
The if __name__ == "__main__": guard is required with the portable default start method, spawn. Native logging is available
only for sequential runs. For large streams, consume the iterator continuously or pass a CSV, JSON, or Parquet sink so results do
not accumulate in memory.
Dimensions and running time¶
Every support is one 64-bit handle interpreted by a matrix-wide support context. Through dimension 64 the handle stores the bits
directly; above 64 it addresses fixed-width context-owned storage. There is no separate application-level dimension cap, but the
dense matrix must fit in memory and the nonempty support space contains 2^n - 1 possibilities. Full-support searches and strongly
pruned games can therefore be practical above 64 even though exhaustive searches usually are not.
Continue with Algorithm Overview for the search itself, Python API for every Python field and return mode, or C++ API for the native interface.