Yao.jl

repository·master·Indexed 21 days ago

https://github.com/quantumbfs/yao.jl

An extensible and efficient Julia framework for quantum algorithm design, quantum software 2.0, and quantum computation education. It utilizes the Quantum Block Intermediate Representation (QBIR) to define programs that can be interpreted across various simulation backends and matrix representations. The ecosystem includes components like YaoBlocks for circuit building blocks, YaoPlots for visualization, YaoSym for symbolic computation, and YaoToEinsum for tensor network conversion, as well as a specialized reverse-mode automatic differentiation engine.

Tokens
14.1K
Snippets
62
Records
80
Agent score
74%

What's inside Yao.jl

  1. How QBIR works in the Yao framework

    master

    The core functionality of the framework is built on the Quantum Block Intermediate Representation (QBIR).

    In the Yao workflow:

    1. A quantum program is defined using QBIR.
    2. The QBIR program is then interpreted into specific targets.
    3. Supported targets include various simulation backends and different matrix representations.
  2. Perform symbolic computation in Yao

    master

    Yao's symbolic engine, powered by SymEngine.jl, allows you to define quantum circuits containing symbolic parameters. This is useful for analyzing circuits that depend on variables (like rotation angles) before assigning concrete values.

    Key operations include:

    • Defining symbolic variables using the @vars macro.
    • Substituting symbolic variables with concrete values using the subs function.

    To use these features, ensure you have YaoSym available (typically via using Yao).

    using Yao
    @vars θ
    # Define a circuit with a symbolic parameter θ
    circuit = chain(2, put(1=>H), put(2=>Ry(θ)))
    
    # Get the symbolic matrix representation
    mat(circuit)
    
    # Substitute θ with a concrete value (e.g., π/2)
    new_circuit = subs(circuit, θ=>π/2)
    
    # Get the concrete matrix representation
    mat(new_circuit)
  3. How quantum registers work in Yao

    master

    A quantum register represents a quantum state or a batch of quantum states. Yao uses two primary types of registers: ArrayReg and BatchedArrayReg, both of which use matrices for storage.

    A key concept is the distinction between active and inactive qubits:

    • Active qubits: Only these qubits are visible to quantum operators. Applying an operator to a register only affects the active subset.
    • Inactive (remaining) qubits: These are part of the register but are ignored by quantum operators.

    This allows for efficient computation by focusing on a subset of qubits using focus! and then returning to the full configuration using relax!.

    using Yao
    using YaoArrayRegister
    
    reg = rand_state(3)
    focus!(reg, 1:2)  # Set first two qubits as active
    nactive(reg)     # Returns number of active qubits
    relax!(reg)      # Set all qubits back to active
    nactive(reg)
  4. Overload exist methods for a custom block

    master

    In Yao, every block has two primary methods that can be overloaded to define custom behavior: mat and apply!.

    • Overload mat(block) to define how the block's matrix form is gathered.
    • Overload apply!(reg, block) to define how the block is applied to a quantum register.

    This allows you to create custom block types with specialized matrix representations or application logic.

    # Prototypes for overloading
    apply!(reg, block)
    mat(block)
  5. Simulate noisy circuits using `DensityMatrixMode` or `PauliBasisMode`

    master

    YaoToEinsum supports noisy circuit simulation by specifying a simulation mode:

    1. DensityMatrixMode(): Simulates using the density matrix representation. Useful for general decoherence and noise channels.

      • network = Yao.yao2einsum(noisy_circuit; mode=DensityMatrixMode(), ...)
      • result = Yao.contract(network)[] (returns the density matrix)
    2. PauliBasisMode(): Simulates using the Pauli basis representation.

      • network = Yao.yao2einsum(noisy_circuit; mode=PauliBasisMode(), ...)
      • result = Yao.contract(network) (returns Pauli coefficients)
    # Density Matrix Mode Example
    network_dm = Yao.yao2einsum(noisy_circuit; 
        mode=DensityMatrixMode(),
        initial_state=Dict([i=>0 for i=1:n_small]),
        observable=put(n_small, 1=>Z)
    )
    res_network = contract(network_dm)[]
    
    # Pauli Basis Mode Example
    network_pauli = Yao.yao2einsum(noisy_circuit;
        mode=PauliBasisMode(),
        initial_state=Dict([i=>0 for i=1:n_small]),
        observable=put(n_small, 1=>Z)
    )
    res_pauli = Yao.contract(network_pauli)
  6. Understand the Yao and CuYao architecture

    master
    The Yao ecosystem is organized into two primary meta-packages: Yao and CuYao. The architecture is designed to allow quantum programs to be defined once and then interpreted for various targets, such as different simulation backends or matrix representations.
  7. Choose between `put` and `subroutine` blocks

    master

    When mapping a subblock to a subset of qudits, choose the block type based on the complexity of the operation:

    • put: Applies a gate in-place using a static matrix representation. It is most efficient when the subblock is small.
    • subroutine: Designed for running sub-programs on a subset of qubits. It uses focus! to set target qubits as active and relax! to unset them. This is generally faster for complex circuits applied to a subset of qubits.

    Use subroutine instead of put for larger circuits to improve performance.

    using Yao
    reg = rand_state(20);
    
    # Use put for small subblocks
    @time apply(reg, put(20, 1:6=>EasyBuild.qft_circuit(6)));
    
    # Use subroutine for sub-programs/circuits on subsets
    @time apply(reg, subroutine(20, EasyBuild.qft_circuit(6), 1:6));