Python API

The public pyfracessa package calls the native C++ analyzer in-process. Inputs are small dataclasses and every result is an ordinary Python dictionary; no conversion from a result class is required.

Execution contract

Every execution function requires "safe" or "fast". safe is the complete exact route. fast can miss candidates during its binary64 prefilter, although every surviving candidate and final stability decision is exact. See Getting Started for method selection and matrix text formats.

run() and run_multiprocessing() have the same return modes:

Input

Sink

Return value

One Matrix

None

One result dictionary.

Iterable of matrices

None

A lazy iterator of result dictionaries.

One matrix or an iterable

Provided

The number of result dictionaries written eagerly.

Result dictionary

Every result contains the following fields:

Field

Meaning

run_id

Identifier supplied by the caller or generated for the complete wrapper call.

matrix_id

Signed 64-bit identifier copied from the input matrix.

status

Integer StatusCode: 0 success, 1 parse error, 4 execution error, or 255 internal error.

candidate_count

Number of exact equilibrium candidates retained by the ESS search, including circular multipliers. fast can miss valid supports before exact verification.

ess_count

Number of ESS found by the selected method, including circular multipliers. The count is complete with safe.

candidate_structure

Dictionary mapping support size to candidate count at that size.

ess_structure

Dictionary mapping support size to ESS count at that size.

elapsed_ns

Native analyzer duration in nanoseconds, measured with a monotonic clock; Python scheduling and file output are excluded.

safe_fallback

Whole-matrix fast fallback reason, or None. A local exact retry for one support does not set this field.

error_message

Empty on success; otherwise the parser, execution, or worker diagnostic.

candidates

Stored representative rows when RunConfig.include_candidates is true; otherwise an empty list.

metadata

The caller’s original matrix metadata dictionary, or None.

The possible non-null whole-matrix fallback reasons are "precision_span", "equilibration_invalid", and "equilibration_non_convergence".

Candidate counts are not a census of every algebraic Nash equilibrium. The complete safe ESS search can discard a singular support immediately and, after finding an exact equilibrium support, skip its strict supersets because they cannot add another ESS. full_support=True tests the full support before this ordinary search and can therefore retain a non-ESS full-support candidate that the normal order would later skip. See Algorithm Overview for the precise search semantics.

Candidate dictionary

Each stored candidate contains:

Field

Meaning

candidate_id

One-based identifier of the stored representative in deterministic search order.

vector

Exact full-dimensional probability vector as comma-separated rational text; entries outside the support are zero.

support / support_size

Arbitrary-precision integer bit mask for I(x) and its number of set bits.

extended_support / extended_support_size

Bit mask for J(x), all pure best replies to the candidate, and its number of set bits.

multiplier

Number of distinct circular rotations/reflections represented by the row, or None for an ordinary candidate.

is_ess

Whether exact stability checking accepted the candidate as an ESS.

stability

Machine-readable reason code for the stability decision.

payoff

Exact rational payoff as text.

payoff_dbl

Lossy binary64 approximation provided only for convenience.

Counts and structures include every candidate found by the selected method even when representative rows are disabled. For large searches, leave candidates disabled unless their individual vectors are needed; candidate payloads can dominate memory and output volume.

Stability reason codes

Every candidate reason records the exact path that completed its stability decision:

Code

ESS

Meaning

T_pure_ess

Yes

A pure candidate has no other tied best reply.

F_reduced_hessian_not_nd

No

The support-only reduced Hessian is not negative definite.

T_reduced_hessian_nd

Yes

The reduced Hessian is negative definite and no outside best reply ties the payoff.

T_copos

Yes

The remaining reduced Bomze matrix is strictly copositive.

F_not_copos

No

The remaining reduced Bomze matrix is not strictly copositive.

Core types

class pyfracessa.StatusCode(*values)

Status values returned in every result dictionary.

OK

Analysis completed successfully.

PARSE_ERROR

The native parser rejected the matrix text.

EXEC_ERROR

Analysis failed after parsing.

INTERNAL_ERROR

The Python worker or native boundary caught an unexpected failure.

class pyfracessa.Matrix(matrix_id, matrix, metadata=None)

Describe one matrix submitted for analysis.

Parameters:
  • matrix_id (int)

  • matrix (str)

  • metadata (dict[str, Any] | None)

matrix_id

Signed 64-bit identifier preserved in every output row.

Type:

int

matrix

Exact matrix text in dimension#values or Matrix Market form. Values-only text is also accepted when metadata contains an integer dimension.

Type:

str

metadata

Optional user dictionary copied unchanged to the result and, when used, the sink’s metadata output.

Type:

dict[str, Any] | None

matrix accepts the full upper triangle of a symmetric matrix, the compact circular-symmetric layout described in Getting Started, or a complete Matrix Market document. Entries may be integers, fractions, exact decimals, or exact scientific notation.

class pyfracessa.RunConfig(full_support=False, include_candidates=False, enable_logging=False)

Configure native analysis for sequential or multiprocessing execution.

Parameters:
  • full_support (bool)

  • include_candidates (bool)

  • enable_logging (bool)

full_support

Check the full support first. If the selected candidate route finds it and exact stability accepts it as an ESS, stop; otherwise continue the ordinary search without checking that support twice.

Type:

bool

include_candidates

Include individual representative dictionaries in result["candidates"]. Counts and support-size structures for the selected method are returned regardless of this setting.

Type:

bool

enable_logging

Write the native diagnostic trace for sequential execution. Multiprocessing rejects this option because workers cannot safely share the rotating log file.

Type:

bool

class pyfracessa.MPConfig(workers=4, prefetch_per_worker=128, queue_maxsize=4096, start_method='spawn')

Configure process scheduling for run_multiprocessing().

Parameters:
  • workers (int)

  • prefetch_per_worker (int)

  • queue_maxsize (int)

  • start_method (Literal['spawn', 'forkserver', 'fork'])

workers

Number of worker processes. The default is the CPUs available to the Python process; each worker analyzes one matrix at a time.

Type:

int

prefetch_per_worker

Pending-matrix allowance per worker. Together with queue_maxsize, this bounds submitted work.

Type:

int

queue_maxsize

Maximum serialized-result queue size and upper bound on the pending-matrix window.

Type:

int

start_method

Python multiprocessing start method. The portable default, "spawn", requires the calling script to use an if __name__ == "__main__": guard.

Type:

Literal[‘spawn’, ‘forkserver’, ‘fork’]

Analyzer

pyfracessa.new_run_id(prefix='run')

Return prefix plus the current UTC timestamp to second precision.

The result is convenient for output file names but is not a globally unique identifier when several calls use the same prefix during one second.

Parameters:

prefix (str)

Return type:

str

pyfracessa.compute_matrix(method, matrix, config, run_id)

Compute one matrix with the native extension and return its canonical Python result.

This is the low-level public adapter used by run() and run_multiprocessing(). Candidate rows are augmented with run_id and matrix_id. Parser and analyzer failures are returned as nonzero status codes with error_message rather than raised by the native boundary.

Parameters:
  • method (Literal['fast', 'safe']) – Required candidate-search method. "safe" is complete and exact; "fast" uses a potentially incomplete binary64 candidate filter before exact verification.

  • matrix (Matrix) – Validated matrix input.

  • config (RunConfig) – Native analysis options.

  • run_id (str) – Identifier attached to the result and candidate rows.

Returns:

The canonical result dictionary consumed by every sink. It contains run_id, matrix_id, status, candidate and ESS counts and support-size structures, native elapsed_ns, safe_fallback, error_message, optional representative candidates, and the original metadata.

Raises:
  • TypeError – If method or values-only dimension metadata has the wrong type.

  • ValueError – If method is unknown or values-only matrix text has no dimension metadata.

  • ModuleNotFoundError – If the native fracessa_core extension cannot be imported.

Return type:

dict

pyfracessa.run(method, matrices, config=None, run_id=None, sink=None)

Run matrix analysis sequentially.

A single Matrix returns one result dictionary immediately. An iterable returns a lazy iterator unless sink is provided; with a sink, all results are written eagerly and the number written is returned. Every matrix uses the same run ID and is completed before the next matrix begins. FracESSA imposes no per-matrix computation timeout.

Parameters:
  • method (Literal['fast', 'safe']) – Required candidate-search method. "safe" is complete and exact; "fast" can miss candidates during its binary64 prefilter.

  • matrices (Matrix | Iterable[Matrix]) – One matrix or an iterable of matrices.

  • config (RunConfig | None) – Analysis options; defaults to RunConfig.

  • run_id (str | None) – Output identifier; a timestamp-based ID is generated when omitted.

  • sink – Optional object providing write_result(), close(), and optionally abort().

Returns:

One result dictionary, a lazy result iterator, or a written-result count.

Raises:
  • TypeError – If method is not a string.

  • ValueError – If method is unknown.

Return type:

dict | Iterator[dict] | int

pyfracessa.run_multiprocessing(method, matrices, config=None, run_id=None, sink=None, mp_config=None)

Run matrix analysis across worker processes.

A single Matrix blocks and returns one result dictionary. An iterable returns a lazy completion-order iterator unless sink is provided; with a sink, all results are written eagerly and the number written is returned. Each worker analyzes one matrix at a time, so parallelism is across matrices rather than within one matrix. FracESSA imposes no per-matrix computation timeout.

Submission is bounded by min(queue_maxsize, workers * prefetch_per_worker). Closing the iterator before completion terminates its workers. An unexpected exception inside a worker becomes an INTERNAL_ERROR result so one bad matrix cannot break the queue protocol. Scripts using the default "spawn" start method require an if __name__ == "__main__": guard.

Parameters:
  • method (Literal['fast', 'safe']) – Required candidate-search method. "safe" is complete and exact; "fast" can miss candidates during its binary64 prefilter.

  • matrices (Matrix | Iterable[Matrix]) – One matrix or an iterable of matrices.

  • config (RunConfig | None) – Analysis options; defaults to RunConfig.

  • run_id (str | None) – Output identifier; a timestamp-based ID is generated when omitted.

  • sink – Optional object providing write_result(), close(), and optionally abort().

  • mp_config (MPConfig | None) – Process scheduling options; defaults to MPConfig.

Returns:

One result dictionary, a lazy result iterator, or a written-result count.

Raises:
  • ValueError – If config.enable_logging is true.

  • RuntimeError – If workers exit before returning every submitted result.

Return type:

dict | Iterator[dict] | int

Multiprocessing notes

One worker analyzes one matrix at a time; parallelism is across matrices, and results are yielded in completion order. Submission is bounded by min(queue_maxsize, workers * prefetch_per_worker). Closing an unfinished iterator terminates its workers. Scripts using the default spawn method must protect their entry point with if __name__ == "__main__":. Native logging is sequential-only; enabling it for a multiprocessing call raises ValueError before workers are started.

Input and output

pyfracessa.load_matrices_from_json(path, matrices_key='matrices', id_key='id', matrix_key='matrix', dimension_key='dimension')

Load, validate, and sort matrices from a JSON file.

Supported input shapes:

  • {"matrices": [...]}

  • [...]

Field names can be remapped with the key arguments. Extra row fields are preserved as metadata. Complete dimension#values and Matrix Market strings are preserved after trimming; values-only matrix strings require an integer dimension field and are prefixed with dimension#.

Parameters:
  • path (str | Path) – JSON file to read.

  • matrices_key (str) – Top-level key containing rows when the document is an object.

  • id_key (str) – Row key containing the signed 64-bit matrix identifier.

  • matrix_key (str) – Row key containing the matrix string.

  • dimension_key (str) – Row key used to prefix values-only matrix strings.

Returns:

Validated matrices sorted by matrix_id.

Raises:

ValueError – If the JSON shape or any matrix row is invalid.

Return type:

list[Matrix]

pyfracessa.create_sink(kind, output_dir, run_id)

Create a CSV, JSON, or Parquet sink with run-specific file names.

The output directory is created when needed. Each sink writes separate summary, candidate, and metadata files and refuses to overwrite any of them.

Parameters:
  • kind (str) – Case-insensitive "csv", "json", or "parquet".

  • output_dir (str | Path) – Directory that will contain the output triplet.

  • run_id (str) – Identifier used in all three file names.

Returns:

A new CsvSink, JsonSink, or ParquetSink.

Raises:
  • ValueError – If kind is unsupported.

  • FileExistsError – If any target output already exists.

class pyfracessa.CsvSink(summary_path, candidates_path, metadata_path=None)

Write summary and candidate CSV files plus a metadata JSON sidecar.

Files are opened exclusively. A successful close() preserves the output triplet; abort() closes and removes all three files.

Parameters:
  • summary_path (str | Path)

  • candidates_path (str | Path)

  • metadata_path (str | Path | None)

abort()

Close and remove incomplete outputs; repeated calls are safe.

Return type:

None

close()

Finalize and preserve every output; repeated calls do nothing.

Return type:

None

write_result(result)

Write one canonical result and all of its candidate and metadata rows.

Parameters:

result (dict)

Return type:

None

class pyfracessa.JsonSink(summary_path, candidates_path, metadata_path=None)

Write summary, candidate, and metadata arrays without retaining rows.

Files are opened exclusively. A successful close() preserves the output triplet; abort() closes and removes all three files.

Parameters:
  • summary_path (str | Path)

  • candidates_path (str | Path)

  • metadata_path (str | Path | None)

abort()

Close and remove incomplete outputs; repeated calls are safe.

Return type:

None

close()

Finalize and preserve every JSON array; repeated calls do nothing.

Return type:

None

write_result(result)

Write one canonical result and all of its candidate and metadata rows.

Parameters:

result (dict)

Return type:

None

class pyfracessa.ParquetSink(summary_path, candidates_path, metadata_path=None)

Write batched summary and candidate Parquet files plus metadata JSON.

Rows are buffered in fixed-size batches before being passed to PyArrow. Files are opened exclusively and removed by abort() after any failure.

Parameters:
  • summary_path (str | Path)

  • candidates_path (str | Path)

  • metadata_path (str | Path | None)

abort()

Close and remove incomplete outputs; repeated calls are safe.

Return type:

None

close()

Flush, finalize, and preserve outputs; repeated calls do nothing.

Return type:

None

write_result(result)

Buffer one canonical result and its candidate and metadata rows.

Parameters:

result (dict)

Return type:

None

The JSON loader accepts either a top-level list or an object with a matrices list. By default each row uses id and matrix; all other fields become metadata. A matrix row may contain complete dimension#values or Matrix Market text. Values-only text requires an integer dimension field:

[
  {"id": 1, "matrix": "3#-1,0,0,-2,0,-3", "source": "example"},
  {"id": 2, "dimension": 3, "matrix": "4,13/2,1/2,5,11/2,3"}
]

For streamed output, create one sink and pass it directly to run() or run_multiprocessing(). The call then consumes all matrices eagerly, closes the sink, and returns the number of written summaries:

from pyfracessa import RunConfig, create_sink, load_matrices_from_json, run

run_id = "example"
matrices = load_matrices_from_json("matrices.json")
sink = create_sink("json", "results", run_id)
written = run(
    "safe",
    matrices,
    config=RunConfig(include_candidates=True),
    run_id=run_id,
    sink=sink,
)

All sinks create separate summary, candidate, and metadata outputs without overwriting existing paths. A failed construction, run, write, or finalization removes only files created by that attempt and re-raises the original error. CSV and JSON preserve arbitrary-width support integers. The current Parquet candidate schema is uint64 and rejects supports above 2^64 - 1 rather than truncating them.