Malcolm Low | Practical AI, Computing & Quantum Research

Singapore-based practical guides, tutorials and experiments in AI, computing, modelling, simulation, optimisation and quantum computing, with research notes and hands-on workflows.

Draw Bloch Spheres with Matplotlib on Android Termux Guide

Yes, Matplotlib renders 3D Bloch spheres. Missing C/C++ wheels prevents full Qiskit installs on Android Termux. See the tested Python visualization code →

·

Written by

Six Qiskit Bloch spheres arranged in X, Hadamard, and Z gate order, showing zero to one, zero to plus, and plus to minus, with plus and minus expanded as superpositions.
Part of the Quantum Computing: A Complete Learning Path series. For the underlying state geometry, begin with Bloch Sphere Explained: Basis States and the X, H and Z Gates.
QUANTUM SERIES 2026
Start with Matplotlib on Android: verify the mathematics, render Bloch spheres or circuit diagrams without a desktop display, preserve a high-resolution master, and prepare a WebP copy for WordPress.

Use Matplotlib first on Android Termux. The full Qiskit installation was attempted on the tested phone, but its build could not complete within the available memory. Matplotlib with the non-interactive Agg backend was the reliable foundation: calculate and verify the state or gate logic first, then render the diagram directly to a file.

For the Bloch spheres in this article, Matplotlib hosts the unmodified qiskit.visualization.bloch.Bloch renderer loaded from the pinned Qiskit 2.5.2 source distribution; the complete Qiskit package is not installed. For quantum-circuit diagrams, Matplotlib can draw the wires, gates, controls, oracle boxes and labels directly, without Qiskit.

Six Qiskit Bloch spheres arranged in X, Hadamard, and Z gate order, showing zero to one, zero to plus, and plus to minus, with plus and minus expanded as superpositions.
Figure 1: The finished Qiskit diagram uses the teaching order X → H → Z and expands the two x-axis states as |+⟩ = (|0⟩ + |1⟩)/√2 and |−⟩ = (|0⟩ − |1⟩)/√2.

1  ·  What Was Tested

The workflow was checked in native Android Termux with Python 3.13.13 and these installed packages:

NumPy 2.4.4  ·  Matplotlib 3.11.0  ·  Pillow 12.2.0  ·  Qiskit Bloch renderer 2.5.2

The complete Qiskit package was not installed in the final Termux environment. Instead, the fallback loaded the unmodified qiskit.visualization.bloch.Bloch implementation from the Qiskit 2.5.2 source distribution and performed the small statevector calculations with NumPy.

Version note: the source-loader path is deliberately pinned to Qiskit 2.5.2. Internal source paths can change, so update both the downloaded version and the path together.

2  ·  Install the Termux Prerequisites

Install the scientific packages from the Termux repositories. This avoids rebuilding large numerical libraries locally:

pkg update
pkg install python python-numpy matplotlib python-pillow

python -m pip show numpy matplotlib pillow

Next, try the standard Qiskit installation:

python -m pip install "qiskit[visualization]"
python -c "import qiskit; print(qiskit.__version__)"
If the import succeeds: use the public-API script in the next section. The fallback is only needed when the complete package cannot be installed on the device.

3  ·  Generate the Diagram with Qiskit

Save this as generate_bloch_sphere.py. The assertions verify X, H, and Z before any plotting occurs. The x-axis labels identify the two coherent superpositions explicitly.

from pathlib import Path

import matplotlib.pyplot as plt
import numpy as np
from qiskit.circuit.library import HGate, XGate, ZGate
from qiskit.quantum_info import Statevector
from qiskit.visualization.bloch import Bloch


def bloch_vector(state):
    alpha, beta = state.data
    overlap = np.conj(alpha) * beta
    return [
        float(2 * np.real(overlap)),
        float(2 * np.imag(overlap)),
        float(abs(alpha) ** 2 - abs(beta) ** 2),
    ]


zero = Statevector.from_label("0")
one = zero.evolve(XGate())
plus = zero.evolve(HGate())
minus = plus.evolve(ZGate())

assert one.equiv(Statevector.from_label("1"))
assert plus.equiv(Statevector.from_label("+"))
assert minus.equiv(Statevector.from_label("-"))

rows = [
    ("X gate", zero, one, r"Input: $|0\rangle$", r"Output: $|1\rangle$"),
    ("H gate", zero, plus, r"Input: $|0\rangle$", r"Output: $|+\rangle$"),
    ("Z gate", plus, minus, r"Input: $|+\rangle$", r"Output: $|-\rangle$"),
]

fig = plt.figure(figsize=(9.6, 12.6), facecolor="white")

for row_index, (gate, before, after, before_title, after_title) in enumerate(rows):
    for column_index, (state, title, color) in enumerate((
        (before, before_title, "#1565C0"),
        (after, after_title, "#D32F2F"),
    )):
        axis = fig.add_subplot(
            3, 2, row_index * 2 + column_index + 1,
            projection="3d",
        )
        sphere = Bloch(fig=fig, axes=axis, font_size=13)
        sphere.xlabel = [r"$|+\rangle$", r"$|-\rangle$"]
        sphere.vector_color = [color]
        sphere.vector_width = 4
        sphere.add_vectors(bloch_vector(state))
        sphere.render(title=title)

    fig.text(
        0.03, 0.805 - row_index * 0.31, gate,
        rotation=90, va="center", ha="center",
        fontweight="bold",
    )

fig.suptitle(
    "Bloch-sphere gate order: X, then H, then Z",
    fontsize=19, fontweight="bold", y=0.995,
)
fig.text(
    0.5, 0.018,
    r"$|+\rangle=(|0\rangle+|1\rangle)/\sqrt{2}$"
    r"    and    $|-\rangle=(|0\rangle-|1\rangle)/\sqrt{2}$",
    ha="center", fontsize=13,
)
fig.tight_layout(rect=(0.055, 0.05, 1, 0.975), h_pad=1.4)

output = Path("bloch-sphere-x-h-z-gates.png")
fig.savefig(output, dpi=220, bbox_inches="tight", facecolor="white")
print(output)

Run it with a non-interactive Matplotlib backend:

MPLBACKEND=Agg python generate_bloch_sphere.py

4  ·  When Full Qiskit Will Not Install

On the tested phone, the complete Qiskit build could not finish within the available memory, and import qiskit remained unavailable. Repeatedly rebuilding the same package was not useful. The smaller solution was to download the matching source distribution and load only the official Bloch renderer.

QISKIT_VERSION=2.5.2
mkdir -p "$PREFIX/tmp/qiskit-source/extracted"

python -m pip download   --no-deps   --no-binary=qiskit   "qiskit==$QISKIT_VERSION"   -d "$PREFIX/tmp/qiskit-source"

tar -xzf   "$PREFIX/tmp/qiskit-source/qiskit-$QISKIT_VERSION.tar.gz"   -C "$PREFIX/tmp/qiskit-source/extracted"

The standalone bloch.py file imports one helper from qiskit.visualization.utils. Loading the file directly therefore needs a minimal module stub providing matplotlib_close_if_inline. The gate calculations remain transparent NumPy matrix multiplications.

5  ·  Lean Termux Fallback

The important part of the fallback is the loader below. It imports Qiskit’s Bloch class from the pinned source tree without importing the complete package:

from pathlib import Path
import importlib.util
import os
import sys
import types

import matplotlib.pyplot as plt
import numpy as np

version = "2.5.2"
root = (
    Path(os.environ["PREFIX"])
    / "tmp"
    / "qiskit-source"
    / "extracted"
    / f"qiskit-{version}"
)
bloch_source = root / "qiskit" / "visualization" / "bloch.py"


def matplotlib_close_if_inline(_figure):
    return None


visualization_pkg = types.ModuleType("qiskit.visualization")
visualization_pkg.__path__ = []

utils_module = types.ModuleType("qiskit.visualization.utils")
utils_module.matplotlib_close_if_inline = matplotlib_close_if_inline

sys.modules["qiskit"] = types.ModuleType("qiskit")
sys.modules["qiskit.visualization"] = visualization_pkg
sys.modules["qiskit.visualization.utils"] = utils_module

spec = importlib.util.spec_from_file_location(
    "qiskit.visualization.bloch",
    bloch_source,
)
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
Bloch = module.Bloch


def bloch_vector(state):
    alpha, beta = state
    overlap = np.conj(alpha) * beta
    return [
        2 * np.real(overlap),
        2 * np.imag(overlap),
        abs(alpha) ** 2 - abs(beta) ** 2,
    ]


zero = np.array([1, 0], dtype=complex)
x_gate = np.array([[0, 1], [1, 0]], dtype=complex)
h_gate = np.array([[1, 1], [1, -1]], dtype=complex) / np.sqrt(2)
z_gate = np.array([[1, 0], [0, -1]], dtype=complex)

one = x_gate @ zero
plus = h_gate @ zero
minus = z_gate @ plus

assert np.allclose(one, [0, 1])
assert np.allclose(plus, np.array([1, 1]) / np.sqrt(2))
assert np.allclose(minus, np.array([1, -1]) / np.sqrt(2))

states = [zero, one, zero, plus, plus, minus]
titles = [
    r"X input: $|0\rangle$",
    r"X output: $|1\rangle$",
    r"H input: $|0\rangle$",
    r"H output: $|+\rangle$",
    r"Z input: $|+\rangle$",
    r"Z output: $|-\rangle$",
]
colors = ["#1565C0", "#D32F2F"] * 3

fig = plt.figure(figsize=(9.6, 12.6), facecolor="white")

for index, (state, title, color) in enumerate(
    zip(states, titles, colors),
    start=1,
):
    axis = fig.add_subplot(3, 2, index, projection="3d")
    sphere = Bloch(fig=fig, axes=axis, font_size=13)
    sphere.xlabel = [r"$|+\rangle$", r"$|-\rangle$"]
    sphere.vector_color = [color]
    sphere.vector_width = 4
    sphere.add_vectors(bloch_vector(state))
    sphere.render(title=title)

fig.suptitle(
    "Bloch-sphere gate order: X, then H, then Z",
    fontsize=19, fontweight="bold", y=0.995,
)
fig.text(
    0.5, 0.018,
    r"$|+\rangle=(|0\rangle+|1\rangle)/\sqrt{2}$"
    r"    and    $|-\rangle=(|0\rangle-|1\rangle)/\sqrt{2}$",
    ha="center", fontsize=13,
)
fig.tight_layout(rect=(0.04, 0.05, 1, 0.975), h_pad=1.4)
fig.savefig(
    "bloch-sphere-x-h-z-gates.png",
    dpi=220,
    bbox_inches="tight",
    facecolor="white",
)
What this workaround preserves: sphere geometry, axes and vectors are rendered by Qiskit’s own Bloch class. Only the small state calculations are performed directly with NumPy.

6  ·  Convert PNG to WebP

A high-resolution PNG is useful as a master file, but WebP is usually much smaller for the WordPress page. The tested conversion used Pillow with quality 88:

python - <<'PY'
from PIL import Image

with Image.open("bloch-sphere-x-h-z-gates.png") as image:
    image.save(
        "bloch-sphere-x-h-z-gates.webp",
        "WEBP",
        quality=88,
        method=6,
    )
PY

file bloch-sphere-x-h-z-gates.webp
sha256sum bloch-sphere-x-h-z-gates.webp

The exact figure used here was 1943 × 2762 pixels and the WebP file was about 180 KiB. WordPress can generate smaller display sizes while retaining the uploaded original.

7  ·  Add the Diagram to WordPress

  1. Upload the WebP file through the WordPress Media Library or the editor’s Image block.
  2. Write alt text that states the gate order and the transformations shown; do not use the filename as alt text.
  3. Use a caption to distinguish input and output colours and to expand |+⟩ and |−⟩.
  4. Keep the image responsive with width 100% and automatic height.
  5. Open the saved post publicly and confirm that both the page and image return HTTP 200.
<img
  src="bloch-sphere-x-h-z-gates.webp"
  alt="Six Bloch spheres showing X, H and Z gate transformations"
  style="width:100%;height:auto"
>
For equations and code on small screens: keep equation panels on one line with white-space:nowrap;overflow:auto, and code blocks with white-space:pre;overflow:auto.

8  ·  Problems Encountered and Their Fixes

ProblemCauseWorking solution
Full Qiskit installation did not completeThe phone ran out of practical build memoryUse Termux NumPy/Matplotlib packages and load the pinned official Bloch source only
Direct bloch.py import failedOne relative utility import was missingProvide the minimal matplotlib_close_if_inline module stub before loading the file
Matplotlib expected a displayTermux runs without a desktop GUIRun with MPLBACKEND=Agg and save directly to a file
Labels were clippedThe title and equations extended beyond the default canvasUse a taller figure, reserve margins with tight_layout, and save with bbox_inches="tight"
Four or six spheres were unreadable on phonesA single wide row becomes tiny in a narrow content columnUse a portrait 3 × 2 layout and a responsive WordPress image
The PNG was unnecessarily largePublication did not require lossless pixelsKeep the PNG master and publish a quality-88 WebP copy

9  ·  Reuse the Method for Quantum Circuits

The same Matplotlib-first workflow was later used for the three reviewed figures in Deutsch’s Algorithm in Quantum Computing: The 4 Cases. Those circuit diagrams were drawn directly with Matplotlib—not Qiskit’s circuit drawer—after the full Qiskit installation failed on Termux.

Small reusable drawing functions produced the wires, X and H gates, controlled operations, Uf oracle boxes, measurement and φ0–φ2 checkpoints. The oracle mappings were checked before rendering: constant zero leaves y unchanged, constant one applies NOT to y, and the balanced cases implement y ⊕ x or y ⊕ x̄.

  • Validate every state transformation or truth-table mapping before drawing.
  • Use mathematical notation such as ⊕ consistently in labels.
  • Render at the final aspect ratio with the Agg backend and sufficient DPI.
  • Review the full-resolution PNG, keep it as the master, and derive the WebP once. Do not enlarge a small raster or repeatedly re-save the WebP, because both can introduce blur.

Frequently Asked Questions

Is the fallback still using Qiskit?

It uses the unmodified Bloch renderer from the pinned Qiskit source distribution. NumPy performs the statevector arithmetic because the complete installed Qiskit package is unavailable in that environment.

Why verify the statevectors before drawing?

A polished diagram can still be physically wrong. Assertions catch an incorrect sign, state order or gate mapping before the image is saved.

Why are |+⟩ and |−⟩ opposite even though both give 50/50 outcomes?

They have the same computational-basis probabilities but opposite relative phase: |+⟩ = (|0⟩ + |1⟩)/√2, whereas |−⟩ = (|0⟩ − |1⟩)/√2.

Should the PNG be deleted after conversion?

No. Keep it as the high-resolution master and upload the smaller WebP derivative to WordPress.

Conclusion: On Android Termux, use Matplotlib as the default drawing layer. Verify the mathematics first, render a high-resolution PNG with Agg, inspect that master at full size, and create the WordPress WebP from it once. Use Qiskit only when the full installation succeeds or when a specific, pinned renderer can be loaded safely from its source distribution.

Quantum Series 2026  ·  Tested in native Android Termux  ·  Bloch sphere rendered with Matplotlib and the Qiskit 2.5.2 source renderer

✦ This tutorial and its diagram were prepared with assistance from OpenAI Codex. The state transformations, Bloch coordinates, package versions and generated files were checked in the documented Termux environment. ✦

Continue the Quantum Series
Quantum Series Recommended Next Reading in Quantum Computing

Deepen your quantum intuition with our step-by-step mathematical and circuit guides:

Comments

Leave a Reply