Skip to content

Conversation

@MatP1337
Copy link
Contributor

@MatP1337 MatP1337 commented Oct 17, 2025

This PR implements the Childs–Kothari–Somma (CKS) linear systems algorithm using the Chebyshev approach.

To-do:

  • Improve documentation
    • proofread docstrings
    • add examples to CKS function (one for A unitary, one for A as a block encoding tuple)
    • fix the compiled html (CKS.rst)
    • extend the description in CKS.rst
    • add example to inner_CKS showing how to run CKS with post-selection


return operand, in_case, out_case

def inner_CKS_wrapper(qlsp, eps, kappa=None, max_beta=None):
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@positr0nium is there another way to pass numpy arrays without having to use this wrapper workaround?

Copy link
Contributor

Choose a reason for hiding this comment

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

You can pass numpy arrays by using the static feature of RUS and converting the array to a tuple before:

from qrisp import *
import numpy as np

@RUS(static_argnums = 0)
def inner_rus(array):
    
    qv = QuantumVariable(2)
    h(qv[array[0]])
    
    return measure(qv) == 0, qv

@jaspify
def main():
    array = np.ones(2)
    array_tuple = tuple(array)
    res_qv = inner_rus(array_tuple)
    return measure(res_qv)

print(main())

This snippet needs the latest commit to be bug-free 😇

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, but for 2D arrays it's more complicated. Then tuple(arr)will be a tuple of arrays.
Since A can also be a block encoding and b a function, I would keep this as it is. Otherwise, it would require further case distinctions.

@MatP1337 MatP1337 marked this pull request as ready for review October 20, 2025 14:45
@MatP1337
Copy link
Contributor Author

The documentation has been provided for all of the functions, with an emphasis on the main CKS function and the main circuit constructing inner_CKS function. Tests have also been added.

The PR is mature enough for a review @positr0nium @renezander90

Please let me know in case anything else needs to be (re)added to the to-do list above, and/or modified prior to merging into main.

@MatP1337 MatP1337 self-assigned this Oct 20, 2025
@MatP1337 MatP1337 added documentation Improvements or additions to documentation enhancement New feature or request labels Oct 20, 2025
Copy link
Contributor

@renezander90 renezander90 left a comment

Choose a reason for hiding this comment

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

Nice work :)

where $U$ is the block encoding unitary acting on $\mathcal H_a\otimes H_s$ (for some auxiliary Hilbert space $\mathcal H_a$),
and $G$ prepares the block encoding state $\ket{G}_a=G\ket{0}_a$ in the auxiliary variable such that $(\bra{G}_a\otimes\mathbb I_s)U(\ket{G_a}\otimes\mathbb I_s)=H$.
Here $\mathbb I_s$ denotes the identity acting on $\mathcal H_s$.
Copy link
Contributor

@positr0nium positr0nium Oct 24, 2025

Choose a reason for hiding this comment

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

While this defintion is of course mathematically sound, to me it was very helpful to understand that a block encoding is a non-unitary matrix $H$ embedded as a block matrix

$$\begin{pmatrix} H & *\\\ * & * \end{pmatrix}$$

Maybe it could be helpful to elaborate this here.

EDIT: This is only true, if |G> = |0>. But maybe it could still help understanding.

Copy link
Contributor

Choose a reason for hiding this comment

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

Done in b91ed66. I think it is true, if |G> = G|0>, i.e., the state preparation acts on |0>.


@classmethod
def from_matrix(self, matrix):
def from_matrix(self, matrix, reversed=False):
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe rename this parameter to reverse_endianness?

Copy link
Contributor

Choose a reason for hiding this comment

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

Done in 6f9ce42.

H = \sum_{i=0}^{T-1}\alpha_iP_i
where $\alpha_i$ are real coefficients, $P_i$ are Pauli operators, and $T$ is the number of terms.
Copy link
Contributor

Choose a reason for hiding this comment

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

Pauli operator -> Pauli string

Copy link
Contributor

@renezander90 renezander90 Oct 24, 2025

Choose a reason for hiding this comment

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

Done in b91ed66.

return unitaries, np.array(coefficients, dtype=float)

def pauli_block_encoding(self):
r"""
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be good if there is some paper reference here to compare the conventions etc.

Copy link
Contributor

Choose a reason for hiding this comment

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

Done in b91ed66.

Examples
--------
We apply a Hermitian matrix to a quantum state via a Pauli block encoding.
Copy link
Contributor

Choose a reason for hiding this comment

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

It would probably be helpful to explain a bit more why applying RUS here gives the observed behavior

Returns
-------
U : function
A function ``U(operand, case)`` applying the block encoding unitary $U$ to ``operand`` and ``case`` QuantumVariables.
Copy link
Contributor

@positr0nium positr0nium Oct 24, 2025

Choose a reason for hiding this comment

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

Many other functions in Qrisp that have one permeable and one "operated on" type of argument follow the convention that the permeably argument comes first in the signature (for instancee cx or every adder). Could help to continue this :).
Otherwise I really like the way that you abstracted the concept of a Pauli-Block encoding into code. The interface is well designed :)

Copy link
Contributor

Choose a reason for hiding this comment

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

This is a good point. I will change this here. However, note that we also do not follow this convention for the qswitch, and changing this in the future will be a breaking change.



def cheb_coefficients(j0, b):
"""
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be good to have some reference here



def CKS_parameters(A, eps, kappa=None, max_beta=None):
"""
Copy link
Contributor

Choose a reason for hiding this comment

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

Reference would also be nice here

corresponds to, e.g., an Ising or a Heisenberg Hamiltonian, the number of Pauli terms may not scale favorably in general.
For certain sparse matrices, their structure can be exploited to construct more efficient block-encodings.
In this example, we construct a block-encoding representation for a tridiagonal 3-sparse matrix defined by
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be helpful to have an example matrix - seeing the code is also good but an example might help even more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants