# Is it bipartite?

## Wolfram Language (Mathematica), 26 25 bytes

Tr[#//.x_:>#.#[email protected]]<1&


Try it online!

### How it works

Given an adjacency matrix A, we find the fixed point of starting with B=A and then replacing B by A2B, occasionally clipping values larger than 1 to 1. The kth step of this process is equivalent up to the Clip to finding powers A2k+1, in which the (i,j) entry counts the number of paths of length 2k+1 from vertex i to j; therefore the fixed point ends up having a nonzero (i,j) entry iff we can go from i to j in an odd number of steps.

In particular, the diagonal of the fixed point has nonzero entries only when a vertex can reach itself in an odd number of steps: if there's an odd cycle. So the trace of the fixed point is 0 if and only if the graph is bipartite.

Another 25-byte solution of this form is Tr[#[email protected]//.x_:>#.#.x]===0&, in case this gives anyone ideas about how to push the byte count even lower.

### Previous efforts

I've tried a number of approaches to this answer before settling on this one.

26 bytes: matrix exponentials

[email protected][#.MatrixExp[#.#]]==0&


Also relies on odd powers of the adjacency matrix. Since x*exp(x2) is x + x3 + x5/2! + x7/4! + ..., when x is a matrix A this has a positive term for every odd power of A, so it will also have zero trace iff A has an odd cycle. This solution is very slow for large matrices.

29 bytes: large odd power

Tr[#.##&@@#~Table~Tr[2#!]]<1&


For an n by n matrix A, finds A2n+1 and then does the diagonal check. Here, #~Table~Tr[2#!] generates 2n copies of the n by n input matrix, and #.##& @@ {a,b,c,d} unpacks to a.a.b.c.d, multiplying together 2n+1 copies of the matrix as a result.

53 bytes: Laplacian matrix

(e=Eigenvalues)[(d=DiagonalMatrix[Tr/@#])+#]==e[d-#]&


Uses an obscure result in spectral graph theory (Proposition 1.3.10 in this pdf).

## Husk, 17 bytes

§V¤=ṁΣṠMSȯDfm¬ṀfΠ


Prints a positive integer if the graph is bipartite, 0 if not. Try it online!

### Explanation

This is a brute force approach: iterate through all subsets S of vertices, and see whether all edges in the graph are between S and its complement.

§V¤=ṁΣṠMSȯDfm¬ṀfΠ  Implicit input: binary matrix M.
Π  Cartesian product; result is X.
Elements of X are binary lists representing subsets of vertices.
If M contains an all-0 row, the corresponding vertex is never chosen,
but it is irrelevant anyway, since it has no neighbors.
All-1 rows do not occur, as the graph is simple.
ṠM           For each list S in X:
Ṁf   Filter each row of M by S, keeping the bits at the truthy indices of S,
S  fm¬     then filter the result by the element-wise negation of S,
ȯD        and concatenate the resulting matrix to itself.
Now we have, for each subset S, a matrix containing the edges
from S to its complement, twice.
§V                 1-based index of the first matrix
¤=               that equals M
ṁΣ             by the sum of all rows, i.e. total number of 1s.
Implicitly print.


## APL (Dyalog Extended), 16 13 bytes

⍱1 1⍉∨.∧⍣2⍣≡⍨


Try it online!

-3 bytes thanks to @H.PWiz.

Uses the algorithm from Misha Lavrov's top Mathematica answer: initialize A = B = M, left-multiply B twice to A and clamp it until it reaches the fixed point, and test if the diagonal entries are all zero.

The regular matrix product A+.×B counts the number of two-step paths from node m to node p passing through any intermediate node n. If we change the code to A∨.∧B, we instead get a boolean matrix indicating if there exists any two-step path from node m to node p. We don't need extra "clamping" operation that way.

### How it works

⍱1 1⍉∨.∧⍣2⍣≡⍨  ⍝ Input: adjacency matrix M
⍣≡⍨  ⍝ Find the fixed point, with
⍝     Starting point A = Left arg B = M...
∨.∧⍣2     ⍝   Left-multiply (matmul) B twice to A
⍝   indicating existence of paths (boolean)
1 1⍉          ⍝ Extract the main diagonal
⍱              ⍝ Test if all elements are zero