Skip to content

Pipeline

qsp_proc.pipeline

End-to-end QSP preprocessing pipeline.

This module provides: - data containers for structured pipeline outputs, - helpers for target coercion, convention/method auto-selection, and validation, - the top-level qsp_preprocess orchestration entry point.

QSPConvention = Literal['angle', 'gqsp', 'laurent', 'symmetric'] module-attribute

Supported high-level QSP conventions.

Metadata = dict[str, Any] module-attribute

Free-form metadata attached to pipeline runs.

TargetInput = 'LaurentPoly | ChebyshevPoly | np.ndarray | Callable[[float], float | complex]' module-attribute

Accepted target representations for qsp_preprocess.

U1_SAMPLE_COUNT = 4096 module-attribute

Default sample count for U(1) residual checks.

DECOMPOSE_SUPPORTED_CONVENTIONS = frozenset({'gqsp', 'laurent', 'auto'}) module-attribute

Conventions currently supported by qsp_proc.decomposition.decompose.

QSPResult dataclass

Structured output of an end-to-end QSP pipeline run.

Attributes:

Name Type Description
target_poly LaurentPoly

Input Laurent polynomial, potentially subnormalized.

complementary_poly LaurentPoly

Complementary polynomial Q returned by completion.

decomposition DecompositionResult

Recursive carving output used to derive phases/factors.

circuit QuantumCircuit | None

Constructed Qiskit circuit, or None when circuit generation is disabled.

convention QSPConvention

QSP convention used for the run.

completion_method str

Name of the complementary-polynomial solver used (e.g., "bersun", "wilson").

completion_error float

Sampled infinity-norm residual ||1 - |P|^2 - |Q|^2||_inf over points on U(1).

decomposition_error float | None

Maximum sampled reconstruction error from decomposition, or None when verification is unavailable for the chosen convention.

circuit_error float | None

Circuit verification error, or None when no circuit is built.

metadata Metadata

Additional run information (timings, degree, subnormalization, sampling counts, etc.).

Source code in src\qsp_proc\pipeline.py
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
@dataclass(slots=True)
class QSPResult:
    """Structured output of an end-to-end QSP pipeline run.

    Attributes:
        target_poly (LaurentPoly):
            Input Laurent polynomial, potentially subnormalized.
        complementary_poly (LaurentPoly):
            Complementary polynomial `Q` returned by completion.
        decomposition (DecompositionResult):
            Recursive carving output used to derive phases/factors.
        circuit (QuantumCircuit | None):
            Constructed Qiskit circuit, or ``None`` when circuit generation is
            disabled.
        convention (QSPConvention):
            QSP convention used for the run.
        completion_method (str):
            Name of the complementary-polynomial solver used
            (e.g., ``"bersun"``, ``"wilson"``).
        completion_error (float):
            Sampled infinity-norm residual
            ``||1 - |P|^2 - |Q|^2||_inf`` over points on ``U(1)``.
        decomposition_error (float | None):
            Maximum sampled reconstruction error from decomposition, or
            ``None`` when verification is unavailable for the chosen
            convention.
        circuit_error (float | None):
            Circuit verification error, or ``None`` when no circuit is built.
        metadata (Metadata):
            Additional run information (timings, degree, subnormalization,
            sampling counts, etc.).
    """

    target_poly: LaurentPoly
    complementary_poly: LaurentPoly
    decomposition: DecompositionResult
    circuit: QuantumCircuit | None
    convention: QSPConvention
    completion_method: str
    completion_error: float
    decomposition_error: float | None
    circuit_error: float | None
    metadata: Metadata = field(default_factory=dict)

    def summary(self) -> str:
        """Build a human-readable summary of pipeline outcomes.

        Returns:
            str: Multi-line report describing selected methods, whether a
            circuit was built, and key error metrics.
        """
        circuit_status = "built" if self.circuit is not None else "not built"
        circuit_error_text = (
            f"{self.circuit_error:.3e}"
            if self.circuit_error is not None
            else "N/A"
        )
        decomposition_error_text = (
            f"{self.decomposition_error:.3e}"
            if self.decomposition_error is not None
            else "N/A (not verified for convention)"
        )
        metadata_keys = ", ".join(sorted(self.metadata.keys())) or "none"

        return (
            "QSP Pipeline Result\n"
            "-------------------\n"
            f"Convention:          {self.convention}\n"
            f"Completion method:   {self.completion_method}\n"
            f"Circuit:             {circuit_status}\n"
            f"Completion error:    {self.completion_error:.3e}\n"
            f"Decomposition error: {decomposition_error_text}\n"
            f"Circuit error:       {circuit_error_text}\n"
            f"Metadata keys:       {metadata_keys}"
        )

summary()

Build a human-readable summary of pipeline outcomes.

Returns:

Name Type Description
str str

Multi-line report describing selected methods, whether a

str

circuit was built, and key error metrics.

Source code in src\qsp_proc\pipeline.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
def summary(self) -> str:
    """Build a human-readable summary of pipeline outcomes.

    Returns:
        str: Multi-line report describing selected methods, whether a
        circuit was built, and key error metrics.
    """
    circuit_status = "built" if self.circuit is not None else "not built"
    circuit_error_text = (
        f"{self.circuit_error:.3e}"
        if self.circuit_error is not None
        else "N/A"
    )
    decomposition_error_text = (
        f"{self.decomposition_error:.3e}"
        if self.decomposition_error is not None
        else "N/A (not verified for convention)"
    )
    metadata_keys = ", ".join(sorted(self.metadata.keys())) or "none"

    return (
        "QSP Pipeline Result\n"
        "-------------------\n"
        f"Convention:          {self.convention}\n"
        f"Completion method:   {self.completion_method}\n"
        f"Circuit:             {circuit_status}\n"
        f"Completion error:    {self.completion_error:.3e}\n"
        f"Decomposition error: {decomposition_error_text}\n"
        f"Circuit error:       {circuit_error_text}\n"
        f"Metadata keys:       {metadata_keys}"
    )

qsp_preprocess(target, convention='auto', method='auto', degree=None, epsilon=1e-12, subnorm=0.5, build_circuit=False, oracle=None, num_system_qubits=None, require_stability=False, completion_kwargs=None, decomposition_tol=1e-10, circuit_tol=1e-06)

Run the end-to-end QSP preprocessing pipeline.

This is the main entry point for converting a target representation (Laurent polynomial, Chebyshev polynomial, coefficient array, or callable) into circuit-ready QSP parameters and, optionally, a Qiskit circuit.

Convention and completion-method auto-selection follow Figure 6 of Skelton (2025). In particular, require_stability=True forces the symmetric-QSP convention with an optimization-based solver, providing the strongest numerical stability at the expense of deeper circuits.

The subnorm argument controls the target's sampled L-infinity bound on U(1) during preprocessing/rescaling. The paper-recommended defaults are 0.5 for Wilson-style completion and 1/sqrt(2) for BerSün.

When build_circuit=True, an oracle must be supplied as either a QuantumCircuit or a unitary matrix (np.ndarray), along with any required system-size information.

Parameters:

Name Type Description Default
target TargetInput

Target function/polynomial representation to preprocess.

required
convention str

QSP convention name or "auto".

'auto'
method str

Completion method name or "auto".

'auto'
degree int | None

Interpolation degree when target is callable.

None
epsilon float

Target approximation tolerance.

1e-12
subnorm float

Desired sampled subnormalization bound on U(1).

0.5
build_circuit bool

Whether to synthesize and return a Qiskit circuit.

False
oracle QuantumCircuit | ndarray | None

Oracle circuit or unitary used for circuit construction.

None
num_system_qubits int | None

System-register size used by circuit builders.

None
require_stability bool

If True, force stable symmetric optimization path.

False
completion_kwargs Mapping[str, Any] | None

Optional keyword arguments forwarded to completion.

None
decomposition_tol float

Numerical tolerance for decomposition checks.

1e-10
circuit_tol float

Numerical tolerance for circuit verification.

1e-06

Returns:

Name Type Description
QSPResult QSPResult

Structured preprocessing output (phases/errors/metadata,

QSPResult

and optional circuit).

Raises:

Type Description
ValueError

If circuit construction is requested without an oracle.

NotImplementedError

If symmetric optimization is selected.

TypeError

If Laurent polynomial evaluation is unsupported.

Source code in src\qsp_proc\pipeline.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
def qsp_preprocess(
    target: TargetInput,
    convention: str = "auto",
    method: str = "auto",
    degree: int | None = None,
    epsilon: float = 1e-12,
    subnorm: float = 0.5,
    build_circuit: bool = False,
    oracle: QuantumCircuit | np.ndarray | None = None,
    num_system_qubits: int | None = None,
    require_stability: bool = False,
    completion_kwargs: Mapping[str, Any] | None = None,
    decomposition_tol: float = 1e-10,
    circuit_tol: float = 1e-6,
) -> QSPResult:
    """Run the end-to-end QSP preprocessing pipeline.

    This is the main entry point for converting a target representation
    (Laurent polynomial, Chebyshev polynomial, coefficient array, or callable)
    into circuit-ready QSP parameters and, optionally, a Qiskit circuit.

    Convention and completion-method auto-selection follow Figure 6 of
    Skelton (2025). In particular, ``require_stability=True`` forces the
    symmetric-QSP convention with an optimization-based solver, providing
    the strongest numerical stability at the expense of deeper circuits.

    The ``subnorm`` argument controls the target's sampled L-infinity bound
    on ``U(1)`` during preprocessing/rescaling. The paper-recommended defaults
    are ``0.5`` for Wilson-style completion and ``1/sqrt(2)`` for BerSün.

    When ``build_circuit=True``, an oracle must be supplied as either a
    ``QuantumCircuit`` or a unitary matrix (``np.ndarray``), along with any
    required system-size information.

    Args:
        target: Target function/polynomial representation to preprocess.
        convention: QSP convention name or ``"auto"``.
        method: Completion method name or ``"auto"``.
        degree: Interpolation degree when ``target`` is callable.
        epsilon: Target approximation tolerance.
        subnorm: Desired sampled subnormalization bound on ``U(1)``.
        build_circuit: Whether to synthesize and return a Qiskit circuit.
        oracle: Oracle circuit or unitary used for circuit construction.
        num_system_qubits: System-register size used by circuit builders.
        require_stability: If ``True``, force stable symmetric optimization path.
        completion_kwargs: Optional keyword arguments forwarded to completion.
        decomposition_tol: Numerical tolerance for decomposition checks.
        circuit_tol: Numerical tolerance for circuit verification.

    Returns:
        QSPResult: Structured preprocessing output (phases/errors/metadata,
        and optional circuit).

    Raises:
        ValueError: If circuit construction is requested without an oracle.
        NotImplementedError: If symmetric optimization is selected.
        TypeError: If Laurent polynomial evaluation is unsupported.
    """
    import time

    from qsp_proc.circuits import build_qsp_circuit, verify_qsp_circuit
    from qsp_proc.completion import complete
    from qsp_proc.decomposition import decompose, verify_decomposition

    metadata: Metadata = {}
    completion_args: dict[str, Any] = dict(completion_kwargs or {})

    LOGGER.info("Coercing target to Laurent polynomial.")
    poly = _coerce_to_laurent(target, degree=degree)

    LOGGER.info("Applying subnormalization check/rescaling (subnorm=%g).", subnorm)
    poly, original_norm = _ensure_subnormalized(poly, subnorm=subnorm)
    metadata["original_norm_on_u1"] = float(original_norm)

    LOGGER.info("Selecting QSP convention (requested=%s).", convention)
    conv = _select_convention(poly, convention=convention)
    LOGGER.info("Selected convention: %s", conv)
    _validate_convention_supported_by_decomposition(conv)

    LOGGER.info("Selecting completion method (requested=%s).", method)
    meth = _select_method(
        poly,
        conv,
        method=method,
        epsilon=epsilon,
        require_stability=require_stability,
    )
    LOGGER.info("Selected completion method: %s", meth)

    if meth == "optimization_symmetric":
        raise NotImplementedError(
            "Symmetric-QSP pipeline not yet wired"
        )

    LOGGER.info("Running complementary-polynomial completion.")
    completion_start = time.perf_counter()
    q = complete(poly, method=meth, **completion_args)
    completion_time_s = time.perf_counter() - completion_start
    metadata["completion_time_s"] = completion_time_s
    LOGGER.info("Completion finished in %.6f s.", completion_time_s)

    LOGGER.info(
        "Computing completion residual on U(1) with %d samples.",
        U1_SAMPLE_COUNT,
    )
    completion_error = _completion_residual_on_u1(poly, q, U1_SAMPLE_COUNT)

    LOGGER.info("Running decomposition (convention=%s).", conv)
    decomposition_start = time.perf_counter()
    result = decompose(poly, q, convention=conv, tol=decomposition_tol)
    decomposition_time_s = time.perf_counter() - decomposition_start
    metadata["decomposition_time_s"] = decomposition_time_s
    LOGGER.info("Decomposition finished in %.6f s.", decomposition_time_s)

    if conv == "gqsp":
        LOGGER.info("Verifying decomposition (gqsp).")
        decomp_err = float(
            verify_decomposition(
                poly,
                q,
                result,
                convention=conv,
                tol=decomposition_tol,
            )
        )
    else:
        LOGGER.warning(
            "Decomposition verification for convention '%s' is not yet supported.",
            conv,
        )
        decomp_err = None

    circuit: QuantumCircuit | None = None
    circuit_err: float | None = None
    if build_circuit:
        LOGGER.info("Building QSP circuit.")
        if oracle is None:
            raise ValueError(
                "oracle must be provided when build_circuit=True."
            )
        circuit = build_qsp_circuit(
            result=result,
            oracle=oracle,
            num_system_qubits=num_system_qubits,
        )
        LOGGER.info("Verifying synthesized QSP circuit.")
        circuit_err = float(
            verify_qsp_circuit(circuit, poly, tol=circuit_tol)
        )

    metadata.update(
        {
            "build_circuit": bool(build_circuit),
            "subnorm": float(subnorm),
            "epsilon": float(epsilon),
            "degree": int(poly.degree),
        }
    )

    return QSPResult(
        target_poly=poly,
        complementary_poly=q,
        decomposition=result,
        circuit=circuit,
        convention=conv,
        completion_method=meth,
        completion_error=completion_error,
        decomposition_error=decomp_err,
        circuit_error=circuit_err,
        metadata=metadata,
    )