qarp.graphs

Graphs, hypergraphs and simplicial complexes for problem construction. Public depth: flat. Submodules are private.

class qarp.graphs.Graph(*args, backend=None, **kwargs)[source]

Bases: Graph

adjacency_matrix()[source]

Weighted adjacency matrix, rows/columns in node insertion order.

Returns:

SciPy sparse array. Append .toarray() for dense representation.

degree_matrix()[source]

Returns the degree matrix of the graph. The degree matrix is a diagonal matrix where each diagonal element is the degree of the corresponding node.

Returns:

The degree matrix of the graph. Append .toarray() for dense representation.

Return type:

SciPy sparse array

classmethod from_qubit_operator(qubit_op) Graph[source]

Graph of a Z/ZZ QubitOperator: Z_i Z_j terms become weighted edges and Z_i terms the node attribute linear; the constant is dropped.

Inverse of to_cost_hamiltonian up to that constant. Equal to qarp.graphs.qubit_operator_to_graph().

incidence_matrix()[source]

Unsigned node × edge incidence matrix (entry 1 where the node is an endpoint), nodes in insertion order and edges in self.edges order.

Returns:

SciPy sparse array. Append .toarray() for dense representation.

laplacian_matrix()[source]

Weighted graph Laplacian D A, rows/columns in node insertion order.

Returns:

SciPy sparse array. Append .toarray() for dense representation.

max_modularity_eigenvector()[source]

Returns the eigenvector relative to the largest eigenvalue of the modularity matrix.

Two behaviours are inherited from networkx’s modularity_matrix: entries are indexed by node insertion order (not by label), and edge weights are ignored — every edge counts 1.

Returns:

The eigenvector relative to the largest eigenvalue of the modularity matrix.

Return type:

np.ndarray

Raises:

ValueError – If the graph has no nodes, or has nodes but no edges — the modularity matrix B = A k kᵀ / 2m is undefined at 2m = 0.

property n_qubits: int

highest label + 1, 0 when empty.

Labels are qubit indices, so a non-contiguous labelling such as {0, 1, 3} still needs qubit 3 to exist: this is not the node count.

Raises:

TypeError – If a node label is not a non-negative integer.

Type:

Qubit count implied by the node labels

normalized_laplacian_matrix()[source]

Normalized Laplacian I D^{-1/2} A D^{-1/2}, node insertion order.

An isolated node contributes a zero row and column (networkx’s convention), not NaN.

Returns:

SciPy sparse array. Append .toarray() for dense representation.

plot(highlight_nodes: Dict[int, str] | List[int] | None = None, figsize: Tuple | None = (4, 3), pos: Dict | None = None, with_labels: bool = True, node_color: str = 'lightblue', font_size: int = 9, return_fig: bool = False)[source]

Plots the graph.

Parameters:
  • highlight_nodes – Optional dictionary mapping node IDs to colors for highlighting.

  • figsize – Size of the figure in inches.

  • pos – Optional dictionary of node positions. If None, a spring layout is computed.

  • with_labels – Whether to display node labels.

  • node_color – Default color used for nodes not in highlight_nodes.

  • font_size – Font size for node labels.

  • return_fig – If True, return (fig, ax) for external saving/customization.

Returns:

(fig, ax) if return_fig is True, otherwise None.

to_cost_hamiltonian(*, linear=None)[source]

Ising cost Hamiltonian Σ_e w_e Z_i Z_j + Σ_i c_i Z_i of this graph.

Thin wrapper over qarp.graphs.graph_to_cost_hamiltonian(), which documents the convention (this is QAOA’s Ising form, not the MaxCut objective: minimising it maximises the cut, cut = (Σ_e w_e ⟨H⟩) / 2).

Parameters:

linear – Mapping node → coefficient of its Z_i term. None reads the per-node linear attributes (set by from_qubit_operator).

Returns:

The cost Hamiltonian.

Return type:

QubitOperator

to_graph_state_block(**kwargs)[source]

The graph-state preparation block: H on every qubit, then CZ per edge.

A graph state is a hypergraph state whose hyperedges all have order 2, so this is the HypergraphStateBlock on this graph’s edges, sized by n_qubits. Works on a base install: hypernetx is not required.

Parameters:

**kwargs – Forwarded to HypergraphStateBlock (name, target_qubits).

Returns:

The (unbuilt) block.

Return type:

HypergraphStateBlock

Raises:

ValueError – On a self-loop, which is no hyperedge (CZ needs two qubits).

class qarp.graphs.SimplicialComplex(simplices: Sequence[Sequence[int]])[source]

Bases: object

add_simplex(simplex: Sequence[int]) None[source]

Adds a simplex and all its faces to the simplicial complex.

Parameters:

simplex (Simplex) – The simplex to add.

boundary_matrix(k: int)[source]

Signed boundary operator ∂_k from k-simplices to (k−1)-simplices.

Rows index the (k−1)-simplices and columns the k-simplices, both in get_simplices() order. The entry for the face obtained by deleting the i-th vertex of a (sorted) k-simplex is (−1)^i. ∂_0 is 0 × f_0 and ∂_{dim+1} is f_dim × 0, so hodge_laplacian needs no end cases.

Returns:

SciPy sparse array of ints. Append .toarray() for dense representation.

cofaces(simplex: Sequence[int]) List[Tuple[int, ...]][source]

Every simplex having simplex as a face — its closed star, so a present simplex is included and an absent one has no cofaces. Exactly the set remove_simplex deletes. Sorted like get_simplices.

euler_characteristic() int[source]

χ = Σ_k (−1)^k f_k, the alternating sum of the f-vector; 0 when empty.

f_vector() List[int][source]

Simplex counts per order, [f_0, …, f_dim]; [] for the empty complex.

get_simplices() List[Tuple[int]][source]

Returns a sorted list of simplices in the complex.

Returns:

The list of simplices.

Return type:

List[Tuple[int]]

hodge_laplacian(k: int)[source]

Combinatorial Hodge Laplacian L_k = ∂_kᵀ ∂_k + ∂_{k+1} ∂_{k+1}ᵀ on the k-simplices (f_k × f_k). L_0 is the graph Laplacian of the 1-skeleton. The matrix only — its spectrum is the caller’s business.

Returns:

SciPy sparse array of ints. Append .toarray() for dense representation.

max_dimension() int[source]

Returns the dimension of the simplicial complex.

Returns:

The highest dimension (i.e., max(len(simplex) - 1)).

Return type:

int

neighbors(node) List[source]

Vertices sharing a simplex with node, sorted. Under closure this is the 1-skeleton neighbourhood: every co-member shares an edge too.

num_simplices(order: int | None = None) int[source]

Number of simplices of the given order, or of all orders when None.

plot(figsize=(4, 3), return_fig=False)[source]

Plots the simplicial complex as points, edges and filled simplices.

Only facets (maximal simplices) are filled, so a tetrahedron reads as one body rather than four separate faces. Faces are implied by the fill and never separately outlined, which is what makes a missing face visible: the body stays filled and the line along the absent edge is simply not drawn. Order 3 is hatched, marking where the simplex is rather than claiming to depict it.

Vertices are placed by a spring layout over the 1-skeleton, seeded from qarp.config.seed — which defaults to None, so figures are reproducible only once the caller sets it.

Parameters:
  • figsize (tuple) – Figure size.

  • return_fig (bool) – If True, return (fig, ax) for saving or further customization.

Returns:

(fig, ax) if return_fig is True, else None.

Raises:

ValueError – If the complex has order above MAX_PLOT_ORDER.

remove_simplex(simplex: Sequence[int]) None[source]

Removes a simplex, and every simplex containing it, from the complex.

Cofaces have to go too: a complex that kept (0, 1, 2) after (0, 1) was removed would no longer be closed under faces. Faces of simplex are left alone, so removing (0, 1) does not remove (0,) or (1,).

The blast radius is the entire coface set, which can be most of the complex — removing a vertex removes every simplex it belongs to.

Parameters:

simplex (Simplex) – The simplex to remove.

Raises:

ValueError – If simplex is not a list or tuple, or is empty. The empty simplex is a face of every simplex, so cascading on it would silently empty the complex.

skeleton(k: int) SimplicialComplex[source]

The k-skeleton: a new complex of the simplices of order at most k (closed automatically). k >= max_dimension() gives a copy.

to_graph() Graph[source]

The 1-skeleton as a native Graph: nodes are the vertices (sorted, so insertion order is label order), edges the 1-simplices.

vertices() List[int][source]

The 0-simplices, as sorted vertex labels (not 1-tuples).

qarp.graphs.community_vector_to_sets(G: Graph, community_vector: list) list[source]

Converts a community vector to a list of sets of node labels, where each set contains the nodes in that community, based on the node ordering in the graph.

Parameters:
  • G (Graph) – A Graph instance whose nodes define the order of the community vector.

  • community_vector (list) – A list where the i-th entry corresponds to the i-th node in G.

Returns:

A list of sets, where each set contains node labels in the same community.

Return type:

list of sets

qarp.graphs.generate_complete_simplicial_complex(n: int) SimplicialComplex[source]

Generates a complete simplicial complex.

Parameters: n (int): The number of nodes.

Returns:

A complete simplicial complex with all possible simplices.

Return type:

SimplicialComplex

qarp.graphs.generate_random_simplicial_complex(n: int, p: float) SimplicialComplex[source]

Generates a random simplicial complex.

Every non-empty subset of range(n) is offered with probability p; closure then adds the faces of whatever was accepted. Draws come from the global random module, so seed with random.seed for reproducibility.

Parameters:
  • n (int) – The number of nodes.

  • p (float) – The probability (0 <= p <= 1) for a simplex to be included.

Returns:

A random simplicial complex with simplices included based on probability p.

Return type:

SimplicialComplex

qarp.graphs.graph_to_cost_hamiltonian(graph, *, linear: Mapping | None = None) QubitOperator[source]

Ising cost Hamiltonian Σ_e w_e Z_i Z_j + Σ_i c_i Z_i of any networkx graph.

This is QAOA’s Ising form, not the MaxCut objective Σ_e w_e (1 Z_i Z_j) / 2: the two differ by a sign and an offset, so minimising this Hamiltonian maximises the cut, with cut = (Σ_e w_e ⟨H⟩) / 2.

Node labels are used directly as qubit indices and are validated as non-negative integers; non-contiguous labels are allowed. The edge weight is the weight attribute, defaulting to 1.0 for an edge without one (whatever other attributes it carries).

Parameters:
  • graph – A networkx graph (qarp.graphs.Graph or plain nx.Graph).

  • linear – Mapping node → coefficient c_i of its Z_i term. None reads the per-node linear attributes, which qubit_operator_to_graph() sets; an explicit mapping replaces them.

Returns:

The cost Hamiltonian.

Return type:

QubitOperator

Raises:
  • TypeError – If a node label (or a linear key) is not a non-negative integer.

  • ValueError – On a self-loop (i, i): Z_i Z_i is the identity, so the edge has no ZZ term — pass a linear term through linear= instead.

qarp.graphs.lists_to_tuples(lst: list) list[source]

Converts a list of lists describing edges or simplices into a list of tuples.

Parameters:

lst (list of lists) – A list of edges or simplices, where each element is a list of nodes.

Returns:

The same elements represented as tuples.

Return type:

list of tuples

qarp.graphs.qubit_operator_to_graph(qubit_op) Graph[source]

Graph of a QubitOperator with only Z and Z Z terms.

Nodes are the qubits 0 count_qubits 1; each Z_i Z_j term becomes the edge (i, j) with attribute weight and each Z_i term the node attribute linear on i, so graph_to_cost_hamiltonian() inverts this up to the constant term, which has no home on a graph and is dropped.

Parameters:

qubit_op – QubitOperator with only Z or ZZ terms (plus a constant).

Returns:

The native graph.

Return type:

Graph

Raises:

ValueError – On a non-Z Pauli or a term acting on more than two qubits.