Likelihood Geometry of the Squared Grassmannian

This page contains auxiliary files to the paper:
Likelihood Geometry of the Squared Grassmannian by Hannah Friedman

Abstract: We study projection determinantal point processes and their connection to the squared Grassmannian. We prove that the log-likelihood function of this statistical model has \((n−1)!/2\) critical points, all of which are real and positive, thereby settling a conjecture of Devriendt, Friedman, Reinke, and Sturmfels.

We provide here the code needed to reproduce the results in Example 4.1 and the Computation Experiments section of the paper. All code is written in \(\verb+Julia+\) and relies heavily on the numerical algebraic geometry package \(\verb+HomotopyContinuation.jl+\).

Once the requisite software is installed, files can be run in a terminal with \(\verb+ julia <filename>.jl+\).

Example 4.1

We claim that for \(d = 3, n = 6\) the parametric log-likelihood function has 17664 critical points and that typically 11904 of them are real. This can be verified by running the code in realsolutions.jl. The parameters \(d, n\) can be changed to count real solution solutions in other situations.

This file provides two functions. The function \(\verb+generate_start_solutions+\) uses the monodromy method implemented in \(\verb+HomotopyContinuation.jl+\) to compute critical points for random complex “data” vector. These start parameters and solutions are contained in a \(\verb+MonodromyResult+\) object. The function \(\verb+nreal_nmax_solutions+\) then uses a parameter homotopy to track the solutions as the parameters are moved to a vector of positive integers, representing real data; the function outputs the number of real solutions and local maxima.

We then compute the number of real solutions for a few random vectors with positive integer entries:

# Example: test a few random real vectors and see how many real critical points and
# local maxima the log-likelihood function has
d = 3
n = 6
println("d = ", d, ", n = ", n)
F, m = generate_start_solutions(n, d);
println("Number of complex critical points: ", nsolutions(m))
for _ in 1:5
    nreal_sols, nlocal_max = nreal_nmax_solutions(F, m, rand(1:1000, binomial(n, d)))
    println("Number of real critical points: ", nreal_sols,
            ", Number of local maxima: ", nlocal_max);
end

Sample output:

d = 3, n = 6
Number of complex critical points: 17664
Number of real critical points: 11904, Number of local maxima: 11904
Number of real critical points: 11904, Number of local maxima: 11904
Number of real critical points: 11904, Number of local maxima: 11904
Number of real critical points: 11904, Number of local maxima: 11904
Number of real critical points: 11904, Number of local maxima: 11904

We also compute a lower bound of 11904 on the number of sign vectors that can arise from Pluecker coordinates in \({\rm Gr}(3, 6)\). The code below will find all 11904 sign vectors with high probability, but it is possible that it might miss some. If this is the case, it can be run again or the number of trials can be increased.

using HomotopyContinuation, LinearAlgebra, Combinatorics

d = 3
n = 6
num_trials = 1000000

@var a[1:d, d+1:n]
A = [I a]

d_minors = [det(A[:, s]) for s in combinations(1:n, d)];

sgns = [sign.(evaluate.(d_minors, vec(a) => randn(d*(n-d)))) for _ in 1:num_trials];

# Checking the norm ensures that there are no zero entries in the sign vectors
println(length([v for v in unique(sgns) if norm(v) == sqrt(binomial(n, d))]))

Comptuational Experiments

The paper concludes with some computational experiments which show the complexity of computing maximum likelihood estimators for projection DPPs. The runtimes are estimated using the \(\verb+Julia+\) package \(\verb+BenchmarkTools.jl+\). The experiments can be recreated by running the code in runtime.jl for various values of \(n\). (For these experiements, \(d\) is always 2.) This file provides a function \(\verb+compute_MLE+\) which computes the maximum likelihood estimate given some data vector. We then test the runtime of this function for small \(n\). Sample output can be found below for \(n = 6\).

# Example: test the runtime for computing the MLE for small values of n
n = 6
println("n = ", n)
# Compute the runtime for random data
@benchmark compute_MLE(n, data) setup=(data=rand(1:1000, binomial(n, 2)))

Sample output:

n = 6
BenchmarkTools.Trial: 6 samples with 1 evaluation per sample.
Range (min  max):  838.224 ms    1.019 s   GC (min  max): 0.00%  0.00%
Time  (median):     954.079 ms               GC (median):    0.00%
Time  (mean ± σ):   953.124 ms ± 63.591 ms   GC (mean ± σ):  0.00% ± 0.00%

                                                       
█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█▁▁█▁▁▁▁▁▁▁▁▁▁▁▁▁▁█▁▁▁▁█ 
838 ms          Histogram: frequency by time          1.02 s <

Memory estimate: 46.40 MiB, allocs estimate: 1008546.
Credits
Project page created: 20/02/2025
Project contributors: Hannah Friedman
Corresponding author of this page: Hannah Friedman, hannahfriedman@berkeley.edu
Software used: Julia (Version 10.4)
System setup used: 2 × 8-Core Intel Xeon Gold 6144 at 3.5 GHz with 64 threads
License for code of this project page: MIT License (https://spdx.org/licenses/MIT.html)
Last updated 26/02/2025.