--- title: "Getting Started with qvivid" author: "Sanmi (Oluwasanmi) Adenaiye" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Getting Started with qvivid} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include=FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 4.5, dpi = 96 ) build_gif <- requireNamespace("gifski", quietly = TRUE) && identical(tolower(Sys.getenv("QVIVID_BUILD_GIF")), "true") ``` `qvivid` provides circuit construction, exact statevector simulation, terminal measurement sampling, tidy inspection, and publication-oriented graphics in one R workflow. The simulation core does not require Python. This vignette introduces the workflow with a Bell state and documents the data structures that remain stable in qvivid 0.1.x. ```{r attach} library(qvivid) ``` ## Build and simulate a Bell circuit Gate functions take a circuit as their first argument, so they work naturally with R's base pipe. Qubit indices start at one. ```{r bell-circuit} bell <- quantum_circuit(2, name = "Bell state") |> gate_h(1) |> gate_cx(control = 1, target = 2) |> measure_all() bell ``` `simulate_quantum()` always computes the exact final state. Set `shots` to also sample terminal measurements, `seed` to make those samples reproducible, and `record = TRUE` to retain the state after every operation. ```{r bell-simulation} result <- simulate_quantum( bell, shots = 256, seed = 2026, record = TRUE ) result result$counts ``` Measurement in qvivid 0.1.x is terminal: it samples the final state but does not perform mid-circuit collapse, reset, or conditional execution. If a circuit has no explicit measurement operation, shot sampling uses the default mapping from each available qubit to the matching classical bit. Statevector memory grows exponentially with the number of qubits. Recorded trajectories retain an additional state for every unitary operation; terminal measurement frames reuse the preceding state. Before allocating the state, qvivid estimates peak memory and compares it with `memory_limit_gib` (2 GiB by default, configurable with the `qvivid.memory_limit_gib` option). Raise that guard only after confirming that the machine has enough memory; use `record = FALSE` for large circuits. ## Qubit and basis ordering The R API uses one-based qubits. In the statevector, qubit 1 is the least-significant bit (LSB). Displayed basis strings are written from the highest qubit down to qubit 1. Consequently, applying X to qubit 1 in a two-qubit register populates `|01>`, not `|10>`. ```{r lsb-order} lsb_result <- quantum_circuit(2, name = "LSB example") |> gate_x(1) |> simulate_quantum() state_data(lsb_result, include_zero = FALSE) ``` For two-qubit custom unitaries, the first qubit supplied to `gate_unitary()` is the most-significant qubit in the matrix's local basis order `|00>, |01>, |10>, |11>`. ## Inspect states, counts, and trajectories Use `state_data()` instead of reconstructing labels or phases from the raw statevector. Its stable 0.1.x columns are: | Column | Meaning | |---|---| | `index` | Zero-based integer statevector index | | `basis` | High-to-low binary basis label, ending in qubit 1 | | `real`, `imaginary` | Cartesian components of the amplitude | | `magnitude` | Absolute value of the amplitude | | `probability` | Squared magnitude | | `phase` | Complex phase in radians; `NA` for a numerically zero amplitude | ```{r state-schema} state_data(result, include_zero = FALSE) ``` `result$counts` is always a data frame with `basis`, `count`, and `probability` columns. It has zero rows when `shots = NULL`. Count labels are classical-bit strings ordered from the highest classical bit down to bit 1; the circuit's terminal measurement mapping determines which qubit supplies each bit. When `record = TRUE`, `trajectory_data()` returns the state-data columns above plus `step` and `label`. Step 0 is the initial state, and each subsequent step corresponds to one circuit operation. ```{r trajectory-schema} trajectory <- trajectory_data(result, include_zero = FALSE) trajectory[, c("step", "label", "basis", "probability")] trajectory_bloch(result, qubit = 1) ``` ## Stable data structures in qvivid 0.1.x A `qv_result` is a named list with class `qv_result`. The following named fields and their meanings are stable through the 0.1.x series: | Field | Meaning | |---|---| | `circuit` | The simulated `qv_circuit` | | `state` | Normalized complex vector of length `2^n` in LSB order | | `probabilities` | Numeric vector aligned with `state` | | `counts` | The three-column terminal-shot table described above | | `shots`, `seed` | Requested values, or `NULL` when omitted | | `backend` | Backend actually used: `"native"` or `"reference"` | | `elapsed` | Simulation elapsed time in seconds | | `trajectory` | Recorded frames, or `NULL` when `record = FALSE` | | `schema_version` | Integer version of the result schema; currently `1L` | A `qv_circuit` has stable `name`, `n_qubits`, `n_clbits`, `operations`, and `schema_version` fields. Each operation has stable `type`, `name`, `label`, `qubits`, `clbits`, `matrix`, and `parameters` fields. Use constructors and gate functions to create or modify circuits; treat these named fields as read-only interoperability data. Each raw `result$trajectory` frame has stable `step`, `label`, `operation`, and `state` fields. Prefer `trajectory_data()` for tabular analysis. Its stable columns are the seven `state_data()` columns followed by `step` and `label`. `trajectory_bloch()` returns stable `step`, `label`, `qubit`, `n_qubits`, `x`, `y`, `z`, `radius`, and `purity` columns. Minor 0.1.x releases may append named fields, columns, optional arguments, or new exported functions. They will not remove or rename the fields and columns listed above, change their meaning, or reorder existing tabular columns. Code should select fields and columns by name rather than depend on list position. ## Plot and export `plot()` dispatches a circuit to `plot_circuit()` and a result to `plot_state()`. Explicit plotting functions expose the same visual presets: `"nature"`, `"npj"`, `"colorblind"`, `"dark"`, `"light"`, and `"mono"`. Plot behavior does not change with installed packages: `plot_state()` uses base graphics by default and returns the plotted state data invisibly. `engine = "auto"` is a compatibility alias for base graphics. An explicit `engine = "ggplot2"` requires the suggested `ggplot2` package and returns a ggplot object for printing or composition. `plot_circuit()` returns its circuit invisibly, and `plot_execution()` returns its result invisibly. ```{r state-plot, fig.cap="Exact Bell-state probabilities and phases."} plot(result, theme = "npj", engine = "base") ``` Recorded runs can synchronize the circuit playhead and exact state at any execution step. ```{r execution-plot, fig.height=5.3, fig.cap="Circuit and exact state after the controlled-X gate."} plot_execution(result, step = 2, theme = "nature") ``` `save_quantum_plot()` supports PDF, SVG, PNG, and TIFF. Always provide a path you control; package examples and vignettes should write only to R's temporary directory. ```{r export} figure_file <- tempfile(fileext = ".pdf") artifact <- save_quantum_plot( result, figure_file, view = "execution", step = 2, size = "single", theme = "nature" ) file.exists(artifact$path) unlink(artifact$path) ``` ## Optional GIF animation GIF export requires the suggested `gifski` package. The following chunk is evaluated only when `gifski` is installed **and** the environment variable `QVIVID_BUILD_GIF=true` is set. It is skipped during ordinary package and CRAN vignette builds, and its output is confined to `tempdir()`. ```{r optional-gif, eval=build_gif} gif_file <- tempfile(fileext = ".gif") animation <- animate_state( result, gif_file, fps = 2, width = 640, height = 480, theme = "npj", progress = FALSE ) animation unlink(animation$path) ``` ## Compatibility and deprecation policy The exported functions, required arguments, S3 classes, and data structures documented above will remain compatible throughout qvivid 0.1.x. New optional arguments, functions, fields, and columns may be added. If an interface must be replaced, qvivid will issue a warning for at least one 0.1.x release and keep the old interface available throughout the series. Removal can occur no earlier than 0.2.0. A serious correctness or security defect may require an earlier change; such changes will be recorded in `NEWS.md`. Unexported names (including names beginning with `.qv_`), exact timing values, warning text, and pixel-level plot geometry are implementation details. Plot engine and return-value semantics, theme names, basis ordering, and data meanings remain documented public behavior.