DEV Community

Cover image for Quantum Computing With Python: 8 Practical Techniques You Can Code Today
Nithin Bharadwaj
Nithin Bharadwaj

Posted on

Quantum Computing With Python: 8 Practical Techniques You Can Code Today

As a best-selling author, I invite you to explore my books on Amazon. Don't forget to follow me on Medium and show your support. Thank you! Your support means the world!

Quantum computing can seem like a distant, complex field, but Python helps bring it within reach. I want to show you how you can start working with quantum concepts today using clear, practical code. We will explore several core techniques, moving from simple circuits to more advanced applications. Let’s begin with the most fundamental building block.

Think of a quantum circuit as a recipe. It’s a sequence of instructions, called gates, that we apply to quantum bits, or qubits. In Python, a library called Qiskit makes this straightforward. You can create a circuit, add gates to it, and visualize the whole process.

from qiskit import QuantumCircuit
import numpy as np

# Let's make a simple 2-qubit circuit.
my_circuit = QuantumCircuit(2)

# Put the first qubit into a superposition state.
my_circuit.h(0)
# Create entanglement between qubit 0 and qubit 1.
my_circuit.cx(0, 1)

# Draw it to see what we built.
print("Here is our circuit:")
print(my_circuit.draw())
Enter fullscreen mode Exit fullscreen mode

When I first started, seeing that text-based drawing was a big moment. It turned an abstract idea into something concrete. I could see the Hadamard gate (H) and the CNOT gate (CX) right there in front of me. This is how we build programs for quantum computers, one gate at a time.

Sometimes you need circuits that can change. You might want to adjust an angle in a gate without redesigning everything. This is where parameters come in handy. You can create a template circuit with placeholders and fill them in later.

from qiskit.circuit import Parameter

# Create a placeholder for an angle.
theta = Parameter('θ')

# Build a circuit using that placeholder.
parametric_circuit = QuantumCircuit(1)
parametric_circuit.ry(theta, 0)  # A rotation gate with a variable angle.

print("Circuit with a parameter:")
print(parametric_circuit.draw())

# Now, let's give theta a specific value.
final_circuit = parametric_circuit.bind_parameters({theta: np.pi / 2})
print("\nCircuit with pi/2 plugged in:")
print(final_circuit.draw())
Enter fullscreen mode Exit fullscreen mode

This feature is powerful for algorithms that need to tweak and optimize their internal settings, which we'll see more of later. Now, building a circuit is one thing. To understand what it does, we need to simulate it.

A quantum state is described by a wavefunction. Instead of just simulating the final measurement, we can use a special simulator to calculate the exact mathematical state of all our qubits after each step. This is called statevector simulation. It’s like pausing the computer and examining the complete quantum configuration.

from qiskit import Aer, execute
from qiskit.quantum_info import Statevector

# Use our simple entangled circuit from before.
simulator = Aer.get_backend('statevector_simulator')
job = execute(my_circuit, simulator)
result = job.result()

# Extract the precise quantum state.
quantum_state = result.get_statevector(my_circuit)
print("\nThe exact quantum state is:")
print(quantum_state)

# We can also calculate the probability of measuring each outcome.
probabilities = np.abs(quantum_state)**2
print("\nProbabilities of measuring |00>, |01>, |10>, |11>:")
for i, prob in enumerate(probabilities):
    print(f"|{i:02b}>: {prob:.4f}")
Enter fullscreen mode Exit fullscreen mode

Seeing that output was a revelation for me. The statevector showed [0.707+0.j, 0.+0.j, 0.+0.j, 0.707+0.j]. This means there's a 50% chance (0.707^2 ≈ 0.5) of measuring 00 and a 50% chance of measuring 11. The 01 and 10 states have zero probability. This is the signature of a Bell state, a perfectly entangled pair. The simulation proves our two qubits are now linked; measuring one tells you instantly about the other.

With the basics of circuits and simulation down, we can look at real algorithms. These are procedures designed to solve problems, sometimes faster than a regular computer could. Let's look at two important ones.

Grover's search algorithm helps you find a specific item in an unsorted list. Classically, you might have to check every item. Grover's method uses quantum mechanics to find it in roughly the square root of that time. The core idea is to amplify the probability of the correct answer.

from qiskit.circuit.library import GroverOperator
import math

def build_grover_oracle(marked_item, num_qubits):
    """Creates a circuit that marks the item we want to find."""
    oracle = QuantumCircuit(num_qubits, name="Oracle")
    # This is a simplified oracle that marks a single state.
    # We'll mark the binary state '101' (which is 5 in decimal).
    if marked_item == 5:  # '101'
        oracle.x(0)  # Flip qubit 0
        oracle.x(2)  # Flip qubit 2
        oracle.h(2)
        oracle.mcx([0,1], 2)  # Multi-controlled X
        oracle.h(2)
        oracle.x(0)
        oracle.x(2)
    return oracle

# Let's search through 8 possibilities (3 qubits).
search_qubits = 3
my_oracle = build_grover_oracle(5, search_qubits)

# Create the full Grover circuit.
grover_circuit = QuantumCircuit(search_qubits)
# Start in a uniform superposition.
grover_circuit.h(range(search_qubits))
# Apply the Grover operator the optimal number of times.
optimal_iterations = round(math.pi/4 * math.sqrt(2**search_qubits))
for _ in range(optimal_iterations):
    grover_circuit.compose(my_oracle, inplace=True)
    grover_circuit.compose(GroverOperator(my_oracle), inplace=True)

# Measure to see the result.
grover_circuit.measure_all()
print(f"\nGrover's circuit to find |101>, with {optimal_iterations} iterations:")
print(grover_circuit.draw(fold=-1))
Enter fullscreen mode Exit fullscreen mode

Another cornerstone algorithm is the Quantum Fourier Transform (QFT). It's the quantum version of the discrete Fourier transform and is a key part of many other algorithms, like Shor's famous factoring algorithm.

def build_qft(n_qubits):
    """Builds the Quantum Fourier Transform circuit."""
    qft_circuit = QuantumCircuit(n_qubits, name="QFT")
    for target_qubit in range(n_qubits):
        # Apply a Hadamard gate.
        qft_circuit.h(target_qubit)
        # Apply controlled rotations with increasing precision.
        for control_qubit in range(target_qubit + 1, n_qubits):
            angle = math.pi / (2 ** (control_qubit - target_qubit))
            qft_circuit.cp(angle, control_qubit, target_qubit)
    # Swap the order of the qubits at the end.
    for i in range(n_qubits // 2):
        qft_circuit.swap(i, n_qubits - i - 1)
    return qft_circuit

# Build a 3-qubit QFT.
qft3 = build_qft(3)
print("\nA 3-qubit Quantum Fourier Transform circuit:")
print(qft3.draw())
Enter fullscreen mode Exit fullscreen mode

Running these algorithms on a simulator lets you verify they work. You'll see Grover's circuit output 101 with high probability, and the QFT will transform input states in predictable ways. This moves us from theory to practice.

Today's quantum hardware is imperfect. Qubits are fragile and can lose their information due to noise. Quantum Error Correction (QEC) is the set of techniques that protect information by spreading it across multiple physical qubits to create one more reliable "logical" qubit.

The simplest code is the repetition code. It encodes one logical bit across several physical qubits to catch bit-flip errors. While it doesn't correct all quantum errors, it introduces the key concepts.

from qiskit.ignis.verification.topological_codes import RepetitionCode
from qiskit.providers.aer.noise import NoiseModel
from qiskit.providers.aer.noise.errors import pauli_error

# Create a distance-3 repetition code. This uses 3 physical qubits.
d = 3
repetition_code = RepetitionCode(d, 0)

print(f"\nA repetition code protecting 1 logical qubit with {d} physical qubits.")
circuits = repetition_code.get_circuit_list()
print("We have two circuits: one for logical '0' and one for logical '1'.")

# Let's create a simple noise model.
noise_model = NoiseModel()
# Add a 5% chance of an X (bit-flip) error on any gate.
bit_flip_error = pauli_error([('X', 0.05), ('I', 0.95)])
noise_model.add_all_qubit_quantum_error(bit_flip_error, ['h', 'cx', 'x'])

# Simulate the logical '0' circuit with noise.
from qiskit import execute, Aer
backend = Aer.get_backend('qasm_simulator')
job = execute(circuits[0], backend, noise_model=noise_model, shots=1024)
counts = job.result().get_counts()

print("\nSample noisy results (first 5 outcomes):")
for key in list(counts.keys())[:5]:
    # The output is 'physical_bits syndrome_bits'.
    physical_bits = key.split()[0]
    print(f"  Physical qubits read as {physical_bits}.")
    # We can decode by majority vote: more 0s means logical 0, more 1s means logical 1.
    logical_guess = '0' if physical_bits.count('0') > d/2 else '1'
    print(f"    After decoding, we guess logical {logical_guess}.")
Enter fullscreen mode Exit fullscreen mode

The real workhorse for near-term quantum devices is a class of algorithms called Variational Quantum Algorithms. They combine a quantum computer with a classical optimizer. The quantum part prepares a state using a circuit with tunable parameters. The classical part adjusts those parameters to minimize a cost function.

Two major examples are the Variational Quantum Eigensolver (VQE) for finding molecular ground states and the Quantum Approximate Optimization Algorithm (QAOA) for combinatorial problems.

Here is a simplified VQE example looking for the lowest energy state of a simple Hamiltonian.

from qiskit.algorithms import VQE
from qiskit.algorithms.optimizers import COBYLA
from qiskit.opflow import I, Z, X
from qiskit.circuit.library import TwoLocal

# Define a simple Hamiltonian (H = a*I + b*Z + c*X)
hamiltonian = 0.5 * I - 0.8 * Z + 0.3 * X

# Create a parameterized circuit (ansatz).
ansatz = TwoLocal(num_qubits=1, rotation_blocks='ry', entanglement_blocks='', reps=2)

# Choose a classical optimizer.
optimizer = COBYLA(maxiter=50)

# Set up VQE.
vqe = VQE(ansatz=ansatz, optimizer=optimizer, quantum_instance=Aer.get_backend('statevector_simulator'))

# Run it.
result = vqe.compute_minimum_eigenvalue(hamiltonian)
print(f"\nVQE found a ground state energy of: {result.eigenvalue.real:.5f}")
print(f"The optimal circuit parameters are: {result.optimal_parameters}")
Enter fullscreen mode Exit fullscreen mode

For QAOA, imagine you have a problem like finding the best way to cut a graph. You encode the problem into a cost Hamiltonian. The algorithm alternates between applying this cost Hamiltonian and a simpler "mixer" Hamiltonian, with parameters that are optimized classically.

def build_qaoa_circuit(cost_operator, mixer_operator, beta, gamma):
    """Builds one layer of a QAOA circuit."""
    qc = QuantumCircuit(2)
    # Start in a uniform superposition.
    qc.h([0, 1])
    # Apply the cost unitary with parameter gamma.
    # For a simple cost Hamiltonian like Z⊗Z, this is an RZZ gate.
    qc.rzz(2 * gamma, 0, 1)
    # Apply the mixer unitary with parameter beta.
    qc.rx(2 * beta, 0)
    qc.rx(2 * beta, 1)
    return qc

# Example parameters.
beta_val = 0.1
gamma_val = 0.3
qaoa_example = build_qaoa_circuit(None, None, beta_val, gamma_val)
print("\nA single-layer QAOA circuit for a 2-qubit problem:")
print(qaoa_example.draw())
Enter fullscreen mode Exit fullscreen mode

These hybrid methods are particularly promising because they can be run on quantum hardware with limited depth, making them a focus of current research.

Another exciting area is Quantum Machine Learning (QML). Here, we use quantum circuits as models for data. One approach is to use a quantum circuit to compute a special "kernel" for a Support Vector Machine (SVM). This quantum kernel can potentially recognize patterns in data that are hard for classical kernels to see.

from qiskit_machine_learning.kernels import QuantumKernel
from qiskit.circuit.library import ZZFeatureMap

# Create a circuit to map classical data into quantum state space.
feature_map = ZZFeatureMap(feature_dimension=2, reps=2)

# Use it to create a quantum kernel.
quantum_kernel = QuantumKernel(feature_map=feature_map, quantum_instance=Aer.get_backend('statevector_simulator'))

# We need some data. Let's create a simple, non-linear dataset.
from sklearn.datasets import make_circles
import numpy as np
X, y = make_circles(n_samples=50, noise=0.1, factor=0.3, random_state=42)

# The kernel computes similarity between data points.
# Let's compute the kernel matrix for the first 3 points.
kernel_matrix = quantum_kernel.evaluate(x_vec=X[:3])
print("\nQuantum kernel matrix (similarities) for 3 data points:")
print(kernel_matrix.round(4))
Enter fullscreen mode Exit fullscreen mode

The kernel matrix shows how "close" the data points are in the high-dimensional quantum feature space. A classical SVM can then use this matrix to find a separating boundary.

Finally, one of the most anticipated applications is Quantum Chemistry. Simulating molecules exactly is enormously difficult for classical computers. VQE, which we saw earlier, is a primary method for finding a molecule's ground state energy on a quantum processor.

While a full chemistry simulation is complex, the workflow is clear: describe the molecule, convert its electronic structure into a qubit Hamiltonian, and use VQE to find the minimum energy.

# This is a conceptual outline, as full setup requires specific chemistry packages.
print("""
Quantum Chemistry Simulation Workflow:
1. Define the molecule (e.g., H2 at a specific bond length).
2. Choose a basis set (e.g., 'sto-3g') to describe electron orbitals.
3. Generate the electronic structure Hamiltonian.
4. Use a mapper (e.g., Jordan-Wigner) to convert fermionic operators to qubit operators (Pauli strings).
5. This produces a Hamiltonian like: H = c1*II + c2*IZ + c3*ZI + c4*XX + ...
6. Feed this Hamiltonian into the VQE algorithm to find the ground state energy.
""")

# The output would be an energy value in Hartrees, allowing us to plot
# how energy changes with bond length, creating a dissociation curve.
Enter fullscreen mode Exit fullscreen mode

This process allows researchers to model chemical reactions and material properties in new ways.

Each of these eight areas—circuit construction, state simulation, algorithm implementation (Grover, QFT), error correction, variational algorithms (VQE, QAOA), machine learning, and chemistry—forms a critical part of the quantum computing toolkit in Python. I find that starting with small, runnable examples like these demystifies the subject. You build intuition by modifying the code, changing parameters, and observing the results. The path from a single qubit to simulating a molecule is long, but it's built one understandable step at a time. The code provides a direct, hands-on way to interact with the strange and powerful rules of quantum mechanics.

📘 Checkout my latest ebook for free on my channel!

Be sure to like, share, comment, and subscribe to the channel!


101 Books

101 Books is an AI-driven publishing company co-founded by author Aarav Joshi. By leveraging advanced AI technology, we keep our publishing costs incredibly low—some books are priced as low as $4—making quality knowledge accessible to everyone.

Check out our book Golang Clean Code available on Amazon.

Stay tuned for updates and exciting news. When shopping for books, search for Aarav Joshi to find more of our titles. Use the provided link to enjoy special discounts!

Our Creations

Be sure to check out our creations:

Investor Central | Investor Central Spanish | Investor Central German | Smart Living | Epochs & Echoes | Puzzling Mysteries | Hindutva | Elite Dev | Java Elite Dev | Golang Elite Dev | Python Elite Dev | JS Elite Dev | JS Schools


We are on Medium

Tech Koala Insights | Epochs & Echoes World | Investor Central Medium | Puzzling Mysteries Medium | Science & Epochs Medium | Modern Hindutva

Top comments (0)