Skip to content

Conversation

@q-inho
Copy link
Contributor

@q-inho q-inho commented Aug 7, 2025

Part of #369

Summary

This PR add support Qiskit’s PermutationGate and adds tests accordingly.

Spinless Case

For a permutation $\pi$ acting on spinless orbitals, the simulator decomposes $\pi$ into fermionic swaps and applies them sequentially.
On a computational‑basis state $|n_0 n_1 \dots n_{m-1} \rangle$ with occupations $n_i \in \lbrace 0 , 1 \rbrace$, the gate implements

$$ \hat P_{\pi}=\left(\prod_{(k,j)\in T_\pi}\hat S_{k,j}\right) $$

where​ $T_\pi$ is any sequence of transpositions generating $\pi$ and $\hat{S}_{k,j}$ is the fermionic swap exchanging orbitals $k$ and $j$.

Spinful Case

With spinful orbitals split into $\alpha$ and $\beta$ sectors, the simulator enforces that a permutation never mixes spins.
If $\pi$ separates into permutations $\pi_\alpha$​ and $\pi_\beta$​ acting within the $\alpha$ and $\beta$ sectors, then for a basis state $|n^\alpha; n^\beta\rangle = |n_0^\alpha \dots n_{n-1}^\alpha; n_0^\beta \dots n_{n-1}^\beta\rangle$

$$ \hat P_{\pi} = \left(\prod_{(k,j)\in T_{\pi_alpha}}\hat S_{k,j}^\alpha\right)\left(\prod_{(k,j)\in T_{\pi_\beta}}\hat S_{k,j}^\beta\right) $$

@q-inho
Copy link
Contributor Author

q-inho commented Sep 29, 2025

PermutationGate now uses one pass occupation relabeling and does not call swap or fswap gates.
The evolution routes through _apply_permutation_gate_vec, with the spinful path performing the spin sector check up front.
Now it invert the gate pattern to compute the destination qubit for each source, map the state to integer occupation strings, clear the destination bits, copy each source bit into its destination, then convert the permuted strings back to addresses and reorder the amplitudes.
Previously I decomposed the permutation into transpositions and called _apply_swap for each pair, which wrapped fswap with swap defect sign fixes and extra number number phases.
The new approach computes $\psi' = P \psi$ directly by reindexing the basis, so it removes the repeated defect corrections.

@q-inho q-inho requested a review from kevinsung September 30, 2025 05:31
Comment on lines 736 to 741
for src, dest in zip(qs, dests):
if (src < norb) != (dest < norb):
raise ValueError(
f"Gate of type '{op.__class__.__name__}' must be applied on "
"orbitals of the same spin."
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for src, dest in zip(qs, dests):
if (src < norb) != (dest < norb):
raise ValueError(
f"Gate of type '{op.__class__.__name__}' must be applied on "
"orbitals of the same spin."
)
if any(
(src < norb) != (dest < norb) for src, dest in zip(qubit_indices, dests)
):
raise ValueError(
f"Gate of type '{op.__class__.__name__}' must be applied on "
"orbitals of the same spin."
)

removes one level of indentation.

Also, do you mind adding a test that triggers this case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a test_permutation_gate_cross_spin_error to exercise the PermutationGate spin sector guard.
The test prepares a spinful Hartree–Fock state with norb=1, nelec=(1, 0), applies PermutationGate([1, 0]) to swap the alpha/beta qubits, and asserts final_state_vector raises the expected “same spin” ValueError. This directly triggers the (src < norb) != (dest < norb) condition.
However, I have concern that I created this standlone test function, and wonder whether I have better way to merge this test with other test functions.

@q-inho
Copy link
Contributor Author

q-inho commented Oct 20, 2025

This is the bit manipulation routine for propsoed Permutation gate implementation.

Given an $n$-bit occupation string $x \in \lbrace 0,1\rbrace^n$ and subset of bit positions $Q = (q_0, \dots, q_{k-1})$, the gate permutes only those bits according to a permutation $\pi$ of $\lbrace 0, \dots, k-1 \rbrace$:

$$ \begin{equation} y_{q_t} = x_{q_{\pi(t)}}, \qquad (t=0, \dots, k-1), \qquad y_j = x_j \quad \text{for} \quad j \notin Q \end{equation} $$

_apply_permutation_gate function follow such bit manipulation step that

  1. Convert the pull rule above into push assignments by defining where each source bit ends up

$$ d_t = q_{\pi^{-1}(t)}. $$

  1. Build a destination mask

$$ M = \sum^{k-1}_{t=0} 2^{d_t}, $$

then clear those bits in one shot by $x \&amp; \neg M$.

  1. For each source position $q_t$, read the source bit

$$ b_t = \lfloor \frac{x}{2^{q_t}} \rfloor \bmod 2 $$

which is equivalent to

$$ b_t = (x \gg q_t) \& 1), $$

shift it to its destination, and accumulate by

$$ y = (x \& \neg M) \lor \sum^{k-1}_{t=0} b_t 2^{d_t}. $$

This is responsible for clear then fill to avoids write hazards and handles any pattern of $Q$ as long as destinations are distinct.

For spinful case, we treat the $\alpha$ and $\beta$ $n$-bit halves independently then apply the same mask and shift on each half to get $y^{(\alpha)}$ and $y^{(\beta)}$, then recombine by concatenation

$$ y = y^{(\alpha)} \lor (y^{(\beta)} \ll n). $$

This ensures it preserves $N_{\alpha}$ and $N_{\beta}$ separately.

I will summarize this explanation into docstring of _apply_permutation_gate if I get confirmation to proceed.

@q-inho q-inho requested a review from kevinsung October 21, 2025 00:37
permuted_alpha = _permute_bitstrings(alpha_bits, alpha_pairs)
permuted_beta = _permute_bitstrings(beta_bits, beta_pairs)

combined = permuted_alpha + (permuted_beta << norb)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this create integers greater than 64 bits? Either way, please add a test for a large number of bits. For example, you could create a circuit with 64 orbitals but only two electrons.

Copy link
Contributor Author

@q-inho q-inho Nov 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed this part. Thanks. However defining norb >= 64 raise PySCF's cistring NotImplementedError.
I made safer implementation that it rank the permuted alpha and beta strings separately via strings_to_addresses and recombine the sector addresses.
Also I added the test for largest bit. By defining norb=63 and nelec=(1,1), it test with 126 qubits total, split into 63-qubit spin sectors with preparing a state with one lectron in alpha and beta.

@kevinsung
Copy link
Collaborator

This is the bit manipulation routine for propsoed Permutation gate implementation.

Given an n -bit occupation string x ∈ { 0 , 1 } n and subset of bit positions Q = ( q 0 , … , q k − 1 ) , the gate permutes only those bits according to a permutation π of { 0 , … , k − 1 } :

y q t = x q π ( t ) , ( t = 0 , … , k − 1 ) , y j = x j for j ∉ Q

_apply_permutation_gate function follow such bit manipulation step that

1. Convert the pull rule above into push assignments by defining where each source bit ends up

d t = q π − 1 ( t ) .

2. Build a destination mask

M = ∑ t = 0 k − 1 2 d t ,

then clear those bits in one shot by x & ¬ M .

3. For each source position 
     
       q
       t
     
   , read the source bit

b t = ⌊ x 2 q t ⌋ mod 2

which is equivalent to

b t = ( x ≫ q t ) & 1 ) ,

shift it to its destination, and accumulate by

y = ( x & ¬ M ) ∨ ∑ t = 0 k − 1 b t 2 d t .

This is responsible for clear then fill to avoids write hazards and handles any pattern of Q as long as destinations are distinct.

For spinful case, we treat the α and β n -bit halves independently then apply the same mask and shift on each half to get y ( α ) and y ( β ) , then recombine by concatenation

y = y ( α ) ∨ ( y ( β ) ≪ n ) .

This ensures it preserves N α and N β separately.

I will summarize this explanation into docstring of _apply_permutation_gate if I get confirmation to proceed.

That sounds good. I some comments in the code would be helpful too.

@q-inho q-inho requested a review from kevinsung November 1, 2025 23:51
Copy link
Collaborator

@kevinsung kevinsung left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@kevinsung kevinsung enabled auto-merge (squash) November 3, 2025 16:04
@kevinsung kevinsung merged commit 6d761eb into qiskit-community:main Nov 3, 2025
21 checks passed
@q-inho q-inho deleted the PermutationGate branch November 3, 2025 16:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants