Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

random_clifford function distribution is not uniform #13855

Open
Connorpl opened this issue Feb 17, 2025 · 4 comments
Open

random_clifford function distribution is not uniform #13855

Connorpl opened this issue Feb 17, 2025 · 4 comments
Labels
bug Something isn't working

Comments

@Connorpl
Copy link

Environment

qiskit 1.3.1
python 3.13.1
Ubuntu 20.04.6

What is happening?

The distribution of Cliffords produced by the quantum_info.random_clifford() function is not uniform

How can we reproduce the issue?

import numpy as np
from scipy.stats import chisquare
import stim  ###required to canonicalise tableaus

#Count number of n qubit stabilizers
num_qubits_stab_test=2
iter_count_stabstate = [(2**(num_qubits_stab_test-k)) + 1 for k in range(0,num_qubits_stab_test)]
print("num_stabilizers = ",(2**num_qubits_stab_test)*np.prod(iter_count_stabstate))


#generate 10000 random cliffords, use stim to reduce to a unique canonical form for each stabilizer state and store each unique clifford tableau found on produced_cliffs
produced_cliffs = []
for n in range(10000):
    rand_cliff = random_clifford(num_qubits_stab_test)
    reduced_stab_matrix=[list(str(x).replace("_","I"))[1:] + [-1 if y=="-" else 1 for y in list(str(x))[0]]  for x in stim.Tableau.from_stabilizers([stim.PauliString(x) for x in  rand_cliff.to_labels(mode="S")]).to_stabilizers(canonicalize=True)]
    in_produced_cliffs = [np.array_equal(np.array(reduced_stab_matrix),x) for x in produced_cliffs]
    if True not in in_produced_cliffs:
        produced_cliffs.append(np.array(reduced_stab_matrix)) 
print(len(produced_cliffs))

#check we found all cliffords
assert(len(produced_cliffs) == (2**num_qubits_stab_test)*np.prod(iter_count_stabstate))  

#generate set of samples from random_clifford and reduce to canonical form with stim and store
all_produced_cliffs = []
for n in range(400000):
    rand_cliff = random_clifford(num_qubits_stab_test,seed=np.random.randint(1e12))
    reduced_stab_matrix=[list(str(x).replace("_","I"))[1:] + [-1 if y=="-" else 1 for y in list(str(x))[0]]  for x in stim.Tableau.from_stabilizers([stim.PauliString(x) for x in  rand_cliff.to_labels(mode="S")]).to_stabilizers(canonicalize=True)]
    all_produced_cliffs.append(np.array(reduced_stab_matrix))    


#count number of samples generated for each unique clifford, also split into two sets of samples for comparative bar plot
stab_counts= [0]*len(produced_cliffs)
stab_counts1= [0]*len(produced_cliffs)
stab_counts2= [0]*len(produced_cliffs)

for m in range(len(all_produced_cliffs)):
    for n in range(len(produced_cliffs)):
        if np.array_equal(produced_cliffs[n],all_produced_cliffs[m]):
            stab_counts[n]+=1

            ###split into two seperate sets of counts for comparison
            if m<len(all_produced_cliffs)//2:
                stab_counts1[n]+=1
            else:
                stab_counts2[n]+=1    
            
            break

print("counts of stabilizers produced: ",stab_counts) 

#perform chi square test for uniform randomness


# Example: Observed frequencies from a histogram (e.g., dice rolls)
observed_counts = stab_counts
# Expected frequencies assuming uniformity
expected_counts = np.array([(sum(observed_counts)/len(stab_counts))]*len(stab_counts))

# Perform chi-square test
chi_stat, p_value = chisquare(observed_counts, expected_counts)

# Print results
print(f"Chi-Square Statistic: {chi_stat:.8f}")
print(f"P-Value: {p_value:.8f}")

# Interpretation
alpha = 0.05  # Significance level
if p_value < alpha:
    print("Reject the null hypothesis: The data is NOT uniformly random.")
else:
    print("Fail to reject the null hypothesis: The data is consistent with uniform randomness.")
    

#optionally plot bar chart for independent set of samples
# import matplotlib.pyplot as plt
# X_axis = np.arange(len(stab_counts1)) 

# plt.bar(X_axis-0.2,stab_counts1,0.4,alpha=1.)
# plt.bar(X_axis+0.2,stab_counts2,0.4,alpha=1.)         ```

### What should happen?

The distribution produced should pass the chi-squared test for uniform randomness, for an example of expected behaviour the random Clifford generation can be performed with stim:  

``` rand_cliff = stim.Tableau.random(num_qubits_stab_test)
    reduced_stab_matrix=[list(str(x).replace("_","I"))[1:] + [-1 if y=="-" else 1 for y in list(str(x))[0]]  for x in rand_cliff.to_stabilizers(canonicalize=True)]

Any suggestions?

potentially related bug report: #13590

@Connorpl Connorpl added the bug Something isn't working label Feb 17, 2025
@jakelishman
Copy link
Member

The linked bug report has a PR fixing it that was released in 1.3.2. Please try upgrading the patch version and see if that resolves the problem.

@jakelishman
Copy link
Member

Oh sorry, despite the milestone tag on the issue, the PR didn't get correctly backported to the 1.3.2 branch. It's sitting in the 1.3.3 branch waiting to be deployed (later this week) though.

@Connorpl
Copy link
Author

Ok, thanks. I will check if it works when 1.3.3 comes out

@ShellyGarion
Copy link
Member

this should have been fixed in #13606

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants