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

Fix Yao extension by properly transposing matrices #212

Merged
merged 6 commits into from
Oct 1, 2024

Conversation

jofrevalles
Copy link
Member

@jofrevalles jofrevalles commented Sep 26, 2024

This PR resolves #210 by transposing the matrices received from Yao. This has to be done, since Yao's conventions seem to be (outputs, inputs), rather than (inputs, outputs) that we usually do. See that this problem is now fixed:

julia> using Yao; using Tenet

julia> phase = 2*acos(1/sqrt(3))
1.9106332362490184

julia> circuit = chain(n_qubits, put(1=>Yao.Ry(phase)), Yao.control(1, 2=>Yao.H), Yao.control(2, 3=>Yao.X), Yao.control(1, 2=>Yao.X), put(1=>Yao.X))
nqubits: 3
chain
├─ put on (1)
│  └─ rot(Y, 1.9106332362490184)
├─ control(1)
│  └─ (2,) H
├─ control(2)
│  └─ (3,) X
├─ control(1)
│  └─ (2,) X
└─ put on (1)
   └─ X

julia> SV_Yao = apply!(zero_state(n_qubits), circuit) # circuit|000> 
ArrayReg{2, ComplexF64, Array...}
    active qubits: 3/3
    nlevel: 2

julia> statevec(SV_Yao)
8-element Vector{ComplexF64}:
               -0.0 + 0.0im
 0.5773502691896258 + 0.0im
 0.5773502691896257 + 0.0im
                0.0 + 0.0im
 0.5773502691896257 + 0.0im
                0.0 + 0.0im
                0.0 + 0.0im
                0.0 + 0.0im

julia> psi000 = Tenet.Quantum(Tenet.Product(fill([1, 0], n_qubits))) #|000>
Quantum (inputs=0, outputs=3)

julia> merged_circ = merge(psi000, Tenet.Quantum(circuit)) # circuit|000> 
Quantum (inputs=0, outputs=3)

julia> inds_order = find_permutation(merged_circ)
(:G, :K, :M)

julia> SV_Tenet = reshape(permutedims(Tenet.contract(merged_circ), inds_order), 2^n_qubits)
8-element reshape(::Tensor{ComplexF64, 3, Array{ComplexF64, 3}}, 8) with eltype ComplexF64:
                0.0 + 0.0im
 0.5773502691896258 + 0.0im
 0.5773502691896257 + 0.0im
                0.0 + 0.0im
 0.5773502691896257 + 0.0im
                0.0 + 0.0im
                0.0 + 0.0im
                0.0 + 0.0im

julia> psi_100 = Tenet.Quantum(Tenet.Product([[0, 1], [1, 0], [1, 0]]))
Quantum (inputs=0, outputs=3)

julia> Tenet.contract(merge(psi000, quantum_circuit, psi_100'))
0-dimensional Tensor{ComplexF64, 0, Array{ComplexF64, 0}}:
0.5773502691896258 + 0.0im

julia> statevec(ArrayReg(bit"100"))' * statevec(SV_Yao)
0.5773502691896257 + 0.0im

@amrFRE, see that here I computed some expectation values too.

Note that I used the function find_permutation to permute the result tensor so it follows the usual order (site1, site2, site3). @mofeing, shouldn't we add this function too? I think it makes sense, since if we want the state vector we should permute the tensor properly before the reshape.

This is the function I used:

"""
    find_permutation(qtn::Quantum, n_qubits::Int)

Generates a permutation tuple that orders the sites such that all dual (input) sites
are first in ascending qubit order, followed by all regular sites in ascending qubit order.

# Arguments
- `qtn`: A Quantum Tensor Network

# Returns
- A tuple representing the desired permutation of symbols.

"""
function find_permutation(qtn::Quantum)
    sites = qtn.sites # Dict{Site, Symbol}

    # Initialize arrays to hold dual and regular sites
    dual_sites = Vector{Tuple{Int, Symbol}}()
    regular_sites = Vector{Tuple{Int, Symbol}}()

    # Iterate over each site in the dictionary
    for (site_key, sym) in sites
        if Tenet.isdual(site_key)
            qubit = Tenet.id(site_key)

            # Append to dual_sites
            push!(dual_sites, (qubit, sym))
        else
            # Regular site: extract qubit index
            qubit = Tenet.id(site_key)

            # Append to regular_sites
            push!(regular_sites, (qubit, sym))
        end
    end

    # Sort dual_sites by qubit index in ascending order
    sort!(dual_sites, by = x -> x[1])

    # Sort regular_sites by qubit index in ascending order
    sort!(regular_sites, by = x -> x[1])

    # Extract the symbols in the sorted order
    dual_order = [x[2] for x in dual_sites]
    regular_order = [x[2] for x in regular_sites]

    # Combine dual and regular orders into a single tuple based on availability
    if !isempty(dual_order) && !isempty(regular_order)
        return tuple(dual_order..., regular_order...)
    elseif !isempty(dual_order)
        return tuple(dual_order...)
    elseif !isempty(regular_order)
        return tuple(regular_order...)
    else
        # If no sites are present, return an empty tuple
        return ()
    end
end

ext/TenetYaoBlocksExt.jl Outdated Show resolved Hide resolved
test/integration/YaoBlocks_test.jl Show resolved Hide resolved
@jofrevalles
Copy link
Member Author

Also, @mofeing, what do you think about this find_permutation function? I think we should have something like this in master.

@mofeing
Copy link
Member

mofeing commented Sep 30, 2024

mmm I'm not super convinced. the main reason is that when you contract, the out kwarg lets you order the resulting indices how you like, so it would invalidate the result of find_permutation if the user sets out.

mmm it might be a better idea to integrate the code of this function into a specialized function for converting an Ansatz into a Dense state. what do you think? maybe Base.convert(::Type{Dense}, tn::AbstractQuantum)? that way users like @amrFRE will have a way to get the stavector out of any Ansatz state

@jofrevalles jofrevalles merged commit 01ef3cc into master Oct 1, 2024
6 checks passed
@jofrevalles jofrevalles deleted the fix/Yao-extension branch October 1, 2024 07:18
@jofrevalles
Copy link
Member Author

mmm it might be a better idea to integrate the code of this function into a specialized function for converting an Ansatz into a Dense state. what do you think? maybe Base.convert(::Type{Dense}, tn::AbstractQuantum)? that way users like @amrFRE will have a way to get the stavector out of any Ansatz state

What would we gain with the result being Dense? I would like to have something like a view for tensors, where you pass the indices and in some way, we can view the resulting N-dimensional tensor as a Vector.

jofrevalles added a commit that referenced this pull request Oct 8, 2024
* Transpose the matrix from Yao Blocks

* Enhance Yao extension tests

* Format code from tests

* Add two-qubit gate test

* Change permutation of indices instead of array

* Format tests
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.

Unexpected results from Circuit computation
2 participants