Skip to main content

Haiqu.fourier_loading(fourier_coefficients, num_qubits=None, min_freqs=None, long_range=False, num_layers=2, truncation_cutoff=1e-06, fine_tuning_iterations=20, max_time=900, name=None, job_description=None)

Generate a quantum circuit that prepares a band-limited multidimensional state from its Fourier coefficients. Given the Fourier coefficients of a state, this method creates a Data Loading job that runs in the Haiqu cloud. The coefficients are synthesized on the smallest register that fits them, and an optimized quantum Fourier transform per dimension expands them onto the output grid. The result of this job is a circuit which can be used to supply the state to a quantum algorithm for processing. It pays off only for spectra concentrated in few frequencies, so the state is assumed to represent a periodic function; symmetrizing a dimension can make a non-periodic target more periodic. The coefficients are normally obtained with compute_fourier_coefficients(), which takes an array of values on a uniform grid and returns fourier_coefficients and min_freqs in exactly the form this method expects. The complexity and quality of the generated circuit can be controlled by the num_layers, truncation_cutoff, and fine_tuning_iterations parameters.

NOTE

Frequency layout. fourier_coefficients[i0, i1, ...] is the weight of the harmonic exp(2j*pi*((min_freqs[0] + i0)*x0 + (min_freqs[1] + i1)*x1 + ...)), so coefficients ascend in frequency along every axis — the layout compute_fourier_coefficients() returns. np.fft.fftn does not produce this order: it wraps the negative frequencies onto the end, returning a length-4 axis as [0, 1, -2, -1] rather than [-2, -1, 0, 1]. Pass a raw FFT result through np.fft.fftshift first, after which index 0 carries -(n // 2) — the min_freqs default. min_freqs is applied as a frequency-shift layer, not just as bookkeeping. A value that does not match how the coefficients were obtained encodes the wrong complex exponential, whose amplitude magnitudes may still look plausible. Slicing a narrower band out of a shifted spectrum moves the first frequency, so pass the min_freqs that compute_fourier_coefficients() returns.

NOTE

Grid indexing. The encoded array is recovered from the statevector by reshaping it onto the per-dimension grid, Statevector(circuit).data.reshape([2**n for n in job.info["num_qubits_per_dimension"]]), up to a global phase left behind by the frequency-shift layer. The first array dimension occupies the highest-index qubits, and grid point m along dimension d corresponds to x_d = m / 2**num_qubits_per_dimension[d]. Unlike vector_loading(), where qubits beyond the data size are dead zero padding, every output qubit above the per-dimension minimum performs band-limited Fourier interpolation onto a finer grid, at the cost of a Fourier layer rather than a wider state preparation. Resolution is therefore almost free: the same 9x9 block of coefficients reaches the same fidelity on a 32x32 grid (10 qubits) as on a 256x256 grid (16 qubits). A dimension holding a single frequency needs no qubit to embed it, so its entry in num_qubits_per_dimension may be 0 when num_qubits is None; such a dimension carries a constant, and giving it output qubits spreads that constant over its grid.

NOTE

“job.quality“ excludes the truncation error. It is the fidelity of synthesizing the coefficients you supplied, which are taken as the exact target, so how they were obtained does not enter it. Discarding frequencies is a separate, purely classical approximation, measured by the fidelity that compute_fourier_coefficients() returns. The fidelity of the final state against your original array is approximately the product of the two.
  • Parameters:
    • fourier_coefficients (Sequence *[*Number ] | np.ndarray) — The multidimensional array of Fourier coefficients, ascending in frequency along every axis, which is not the order np.fft.fftn returns (see the frequency-layout note above). May be real or complex, and is normalized in the process. This is the layout that compute_fourier_coefficients() returns, which is the recommended way to obtain it; an analytically known spectrum can equally be written down directly.
    • num_qubits (int | Sequence *[*int ] | None) — The number of output qubits per dimension. An integer is shared by all dimensions, a sequence sets them individually in the order of the coefficient array’s axes. Each entry must be at least ceil(log2(n)) for a dimension holding n frequencies; every qubit above that minimum interpolates the state onto a finer grid. If None (default), the minimum that fits the coefficients is used, which performs no interpolation.
    • min_freqs (int | Sequence *[*int ] | None) — The integer frequency that the first coefficient of each dimension carries. An integer is shared by all dimensions, a sequence sets them individually in the order of the coefficient array’s axes. If None (default), each dimension is assumed to hold a band centred on zero and takes -(n // 2) for its length n. It is applied as a frequency-shift layer, so a value that disagrees with how the coefficients were obtained silently encodes the wrong state — see the frequency-layout note above.
    • long_range (bool) — Whether to allow long-range gates in the Fourier part of the circuit. The prepared state is the same either way, while False (default) keeps the gates on a linear chain.
    • num_layers (int) — The number of layers used to synthesize the coefficients (from 1 to 100 layers). More layers can improve the quality of the output at the cost of a deeper circuit. Defaults to 2.
    • truncation_cutoff (Real) — The entanglement cutoff for later layers. Increasing this threshold may result in a smaller (but more approximate) circuit. Defaults to 1e-6.
    • fine_tuning_iterations (int) — The maximum number of fine-tuning iterations to perform after each layer is added. Increasing this limit may improve the quality of the circuit by using more classical resources. Defaults to 20, maximal is 500.
    • max_time (int | float) — Soft time limit for the job (in seconds). The data loading job will first always produce the initial result and then limit the fine-tuning stage by the remaining time left. If time limit exceeds during the fine-tuning - the best current result will be returned. Defaults to 900 (15 min). Max allowed job time is 15 min. The job can take more wall clock time than user specified max_time due to latency, initialization overheads or if the initial result already takes more time.
    • name (str | None) — The name for the job and the produced circuit. If None (default), a name will be automatically generated.
    • job_description (str | None) — The description for the job.
  • Returns: The Data Loading job that will generate the circuit for the band-limited state. : Call job.result() to retrieve a Qiskit-compatible gate (HaiquCircuitGate) that prepares the state. job.quality is the achieved state fidelity vs. the supplied coefficients; job.info exposes loader metadata (fidelity, num_qubits_per_dimension). Run help(job.result) for the full description of result and info contents.
  • Return type: DataLoadingJobModel

Examples

An exact finite Fourier series. cos(2*pi*3*x) has only the -3 and +3 harmonics, so retaining the band (-3, 3) discards nothing and both fidelities are 1:
A 2D Gaussian sampled on a 32x32 grid, encoded onto a 256x256 grid. Only 81 coefficients are sent, and the eight output qubits per dimension interpolate the rest:

haiqu.sdk.utils.compute_fourier_coefficients(state, freq_ranges, return_fidelity=False)

Compute truncated, normalized Fourier coefficients of an n-dimensional array. The returned values are the input that haiqu.sdk.quantum_haiqu.Haiqu.fourier_loading() expects: the coefficients ascend in frequency along every axis, which is not the order np.fft.fftn returns, and min_freqs records which frequency the first coefficient of each axis carries. Keeping only a few frequencies per axis is what makes Fourier loading cheap, and it is also the only lossy step performed here. The optional fidelity quantifies exactly that loss. It is a purely classical quantity computed locally and for free, and it is not part of the loading job’s quality, which measures how well the cloud reproduces whatever coefficients it is given. The fidelity of the final state against the original state is approximately the product of the two.
  • Parameters:
    • state (np.ndarray | Sequence) — Values of the target on a (possibly coarse) uniform grid. The array is treated as periodic on that grid, so a non-periodic target may need many frequencies; symmetrizing it along a dimension can help.
    • freq_ranges (Sequence *[*Sequence *[*int ] ]) — One (min_freq, max_freq) pair per dimension, giving the inclusive frequency band to retain. Each band must lie inside the range the grid resolves, [-(n // 2), n - n // 2 - 1] for an axis of length n.
    • return_fidelity (bool) — Whether to also return the retained fraction of the spectral energy. Defaults to False.
  • Returns: The complex coefficient array of shape max_freq - min_freq + 1 per dimension, normalized to unit norm, and the per-dimension min_freq values. With return_fidelity a third value is appended: the squared overlap between the reconstruction from the retained frequencies and state. It is 1.0 when the bands cover the whole resolved spectrum, and also when they cover the entire support of a finite Fourier series, however few frequencies that is.
  • Return type: tuple[np.ndarray, list[int]] | tuple[np.ndarray, list[int], float]
  • Raises: ValueError — If the number of bands does not match the number of dimensions, a band is not a pair, min_freq exceeds max_freq, or a band falls outside the resolved range.

Examples