Skip to content

Commit 017a2e3

Browse files
committed
adding codes
1 parent d46265b commit 017a2e3

File tree

2 files changed

+249
-0
lines changed

2 files changed

+249
-0
lines changed

doc/Programs/VQEcodes/vqebasic.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import numpy as np
2+
from scipy.optimize import minimize
3+
4+
# --- Generate a random Hermitian Hamiltonian (8x8) ---
5+
np.random.seed(0)
6+
A = np.random.randn(8,8) + 1j*np.random.randn(8,8)
7+
H = (A + A.conj().T) / 2 # Hermitian Hamiltonian
8+
9+
# --- Define single-qubit and two-qubit gates ---
10+
def ry(theta):
11+
"""2x2 rotation around Y by angle theta."""
12+
return np.array([[np.cos(theta/2), -np.sin(theta/2)],
13+
[np.sin(theta/2), np.cos(theta/2)]], dtype=complex)
14+
15+
def apply_single_qubit_gate(state, gate, target, n_qubits=3):
16+
"""Apply a single-qubit gate to 'target' qubit of the statevector."""
17+
# Build full operator as tensor product of identities and the gate
18+
op = 1
19+
for q in range(n_qubits):
20+
op = np.kron(op, gate if q==target else np.eye(2))
21+
return op.dot(state)
22+
23+
def apply_cnot(state, control, target, n_qubits=3):
24+
"""Apply a CNOT gate with given control and target on the statevector."""
25+
new_state = np.zeros_like(state)
26+
for idx, amp in enumerate(state):
27+
bits = list(map(int, format(idx, f'0{n_qubits}b')))
28+
# If control qubit is |1>, flip the target bit
29+
if bits[control] == 1:
30+
bits[target] ^= 1
31+
new_idx = int("".join(str(b) for b in bits), 2)
32+
new_state[new_idx] += amp
33+
return new_state
34+
35+
# --- Ansatz state preparation ---
36+
def prepare_state(params):
37+
"""
38+
Prepare the 3-qubit statevector from parameters.
39+
Ansatz: 2 layers of Ry + chain of CNOTs.
40+
params: list or array of length 6 [θ0,...,θ5].
41+
"""
42+
# Start in |000>
43+
state = np.zeros(8, dtype=complex)
44+
state[0] = 1.0
45+
46+
# Layer 1: RY on each qubit 0,1,2
47+
for q in range(3):
48+
state = apply_single_qubit_gate(state, ry(params[q]), q)
49+
# Entangling CNOTs
50+
state = apply_cnot(state, control=0, target=1)
51+
state = apply_cnot(state, control=1, target=2)
52+
53+
# Layer 2: another RY on each qubit
54+
for q in range(3):
55+
state = apply_single_qubit_gate(state, ry(params[3+q]), q)
56+
# Another round of CNOTs
57+
state = apply_cnot(state, control=0, target=1)
58+
state = apply_cnot(state, control=1, target=2)
59+
60+
return state
61+
62+
# --- Energy expectation function ---
63+
def energy_expectation(params):
64+
"""
65+
Return the expectation value <psi(θ)|H|psi(θ)> for the ansatz state.
66+
"""
67+
psi = prepare_state(params)
68+
# ⟨psi|H|psi⟩ = psi.conj().T @ H @ psi
69+
return np.real(np.vdot(psi, H.dot(psi)))
70+
71+
# --- Optimization with COBYLA and convergence tracking ---
72+
energy_history = [] # to record energy at each iteration
73+
74+
def callback(params):
75+
"""Callback to store energy at each iteration."""
76+
energy_history.append(energy_expectation(params))
77+
78+
# Random initial parameters (e.g., in [0,2π])
79+
init_params = np.random.rand(6) * 2*np.pi
80+
energy_history.append(energy_expectation(init_params)) # record initial energy
81+
82+
res = minimize(energy_expectation, init_params, method='COBYLA',
83+
callback=callback, options={'maxiter': 200, 'tol': 1e-6})
84+
85+
print("Optimization success:", res.success)
86+
print("Minimum energy found:", res.fun)
87+
print("Ground-state eigenvalue (True):", np.min(np.linalg.eigvalsh(H)))
88+
89+
# --- Display convergence curve (matplotlib can be used here) ---
90+
import matplotlib.pyplot as plt
91+
92+
plt.plot(energy_history, marker='o')
93+
plt.title("VQE Convergence (Energy vs Iteration)")
94+
plt.xlabel("Iteration")
95+
plt.ylabel("Energy ⟨ψ|H|ψ⟩")
96+
plt.grid(True)
97+
plt.show()
98+
99+
# --- Print ansatz structure (text) ---
100+
print("\nAnsatz circuit (text form):")
101+
print(" Layer 1: RY(θ0) on qubit 0, RY(θ1) on qubit 1, RY(θ2) on qubit 2")
102+
print(" CNOT(0→1), CNOT(1→2)")
103+
print(" Layer 2: RY(θ3) on qubit 0, RY(θ4) on qubit 1, RY(θ5) on qubit 2")
104+
print(" CNOT(0→1), CNOT(1→2)")

doc/Programs/VQEcodes/vqebasic.txt

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
2+
Variational Quantum Eigensolver (VQE) Implementation (3 Qubits)
3+
4+
5+
The Variational Quantum Eigensolver (VQE) is a hybrid quantum-classical algorithm that approximates the ground-state energy of a Hamiltonian by preparing a parameterized quantum state (ansatz) and minimizing the expectation value ⟨ψ(θ)|H|ψ(θ)⟩ . We represent a system of 3 qubits as an 8-dimensional complex statevector (since n qubits span a 2^n-dimensional Hilbert space ). In code we build a random 8×8 Hermitian matrix H = (A + A†)/2 (so that its eigenvalues are real) and use the initial state |000⟩ = [1,0,…,0].
6+
7+
8+
Variational Ansatz (RY + CNOT)
9+
10+
11+
We manually construct a variational ansatz with layers of single-qubit Ry rotations and CNOT entangling gates. Single-qubit rotations plus CNOT form a universal gate set , meaning any state can in principle be prepared with such gates. Concretely, we use two layers of Ry(θ) on each qubit, with CNOTs connecting qubit 0→1 and qubit 1→2 between layers. In simulation, each gate is represented by a matrix acting on the statevector . For example, the Ry(θ) gate has matrix
12+
Ry(θ) = [[cos(θ/2), -sin(θ/2)],
13+
[sin(θ/2), cos(θ/2)]]
14+
and the CNOT flips the target qubit when the control is |1⟩. We apply these gates by constructing the appropriate multi-qubit operator (via tensor products or index manipulation) and multiplying it with the statevector . The ansatz thus has 6 parameters (three angles per layer).
15+
16+
17+
Energy Expectation and Optimization
18+
19+
20+
For a given parameter vector θ, we prepare the state ψ(θ) by applying the ansatz gates to |000⟩. We compute the energy expectation as
21+
E(θ) = ⟨ψ(θ)| H |ψ(θ)⟩ = ψ(θ)† · (H ψ(θ))
22+
(which returns a real number since H is Hermitian). We then use SciPy’s COBYLA optimizer to minimize E over the parameters . A callback function records the energy at each iteration, so we can track convergence. COBYLA is a suitable choice here (as in many VQE examples ), though any gradient-free optimizer could be used.
23+
24+
25+
Results and Convergence
26+
27+
28+
The optimization yields an estimate of the ground-state energy. We record the energy at each iteration and plot it to check convergence. For illustration, a typical VQE convergence curve (energy vs. iteration) is shown below:
29+
30+
Figure: Example VQE energy convergence (energy expectation ⟨ψ|H|ψ⟩ vs. iteration) during optimization.
31+
32+
Finally, a text summary of the ansatz circuit is printed. For example:
33+
Ansatz circuit (text form):
34+
Layer 1: RY(θ0) on qubit 0, RY(θ1) on qubit 1, RY(θ2) on qubit 2
35+
CNOT(0→1), CNOT(1→2)
36+
Layer 2: RY(θ3) on qubit 0, RY(θ4) on qubit 1, RY(θ5) on qubit 2
37+
CNOT(0→1), CNOT(1→2)
38+
Below is a complete Python implementation. It is modular and documented with comments for clarity.
39+
import numpy as np
40+
from scipy.optimize import minimize
41+
42+
# --- Generate a random Hermitian Hamiltonian (8x8) ---
43+
np.random.seed(0)
44+
A = np.random.randn(8,8) + 1j*np.random.randn(8,8)
45+
H = (A + A.conj().T) / 2 # Hermitian Hamiltonian
46+
47+
# --- Define single-qubit and two-qubit gates ---
48+
def ry(theta):
49+
"""2x2 rotation around Y by angle theta."""
50+
return np.array([[np.cos(theta/2), -np.sin(theta/2)],
51+
[np.sin(theta/2), np.cos(theta/2)]], dtype=complex)
52+
53+
def apply_single_qubit_gate(state, gate, target, n_qubits=3):
54+
"""Apply a single-qubit gate to 'target' qubit of the statevector."""
55+
# Build full operator as tensor product of identities and the gate
56+
op = 1
57+
for q in range(n_qubits):
58+
op = np.kron(op, gate if q==target else np.eye(2))
59+
return op.dot(state)
60+
61+
def apply_cnot(state, control, target, n_qubits=3):
62+
"""Apply a CNOT gate with given control and target on the statevector."""
63+
new_state = np.zeros_like(state)
64+
for idx, amp in enumerate(state):
65+
bits = list(map(int, format(idx, f'0{n_qubits}b')))
66+
# If control qubit is |1>, flip the target bit
67+
if bits[control] == 1:
68+
bits[target] ^= 1
69+
new_idx = int("".join(str(b) for b in bits), 2)
70+
new_state[new_idx] += amp
71+
return new_state
72+
73+
# --- Ansatz state preparation ---
74+
def prepare_state(params):
75+
"""
76+
Prepare the 3-qubit statevector from parameters.
77+
Ansatz: 2 layers of Ry + chain of CNOTs.
78+
params: list or array of length 6 [θ0,...,θ5].
79+
"""
80+
# Start in |000>
81+
state = np.zeros(8, dtype=complex)
82+
state[0] = 1.0
83+
84+
# Layer 1: RY on each qubit 0,1,2
85+
for q in range(3):
86+
state = apply_single_qubit_gate(state, ry(params[q]), q)
87+
# Entangling CNOTs
88+
state = apply_cnot(state, control=0, target=1)
89+
state = apply_cnot(state, control=1, target=2)
90+
91+
# Layer 2: another RY on each qubit
92+
for q in range(3):
93+
state = apply_single_qubit_gate(state, ry(params[3+q]), q)
94+
# Another round of CNOTs
95+
state = apply_cnot(state, control=0, target=1)
96+
state = apply_cnot(state, control=1, target=2)
97+
98+
return state
99+
100+
# --- Energy expectation function ---
101+
def energy_expectation(params):
102+
"""
103+
Return the expectation value <psi(θ)|H|psi(θ)> for the ansatz state.
104+
"""
105+
psi = prepare_state(params)
106+
# ⟨psi|H|psi⟩ = psi.conj().T @ H @ psi
107+
return np.real(np.vdot(psi, H.dot(psi)))
108+
109+
# --- Optimization with COBYLA and convergence tracking ---
110+
energy_history = [] # to record energy at each iteration
111+
112+
def callback(params):
113+
"""Callback to store energy at each iteration."""
114+
energy_history.append(energy_expectation(params))
115+
116+
# Random initial parameters (e.g., in [0,2π])
117+
init_params = np.random.rand(6) * 2*np.pi
118+
energy_history.append(energy_expectation(init_params)) # record initial energy
119+
120+
res = minimize(energy_expectation, init_params, method='COBYLA',
121+
callback=callback, options={'maxiter': 200, 'tol': 1e-6})
122+
123+
print("Optimization success:", res.success)
124+
print("Minimum energy found:", res.fun)
125+
print("Ground-state eigenvalue (True):", np.min(np.linalg.eigvalsh(H)))
126+
127+
# --- Display convergence curve (matplotlib can be used here) ---
128+
import matplotlib.pyplot as plt
129+
130+
plt.plot(energy_history, marker='o')
131+
plt.title("VQE Convergence (Energy vs Iteration)")
132+
plt.xlabel("Iteration")
133+
plt.ylabel("Energy ⟨ψ|H|ψ⟩")
134+
plt.grid(True)
135+
plt.show()
136+
137+
# --- Print ansatz structure (text) ---
138+
print("\nAnsatz circuit (text form):")
139+
print(" Layer 1: RY(θ0) on qubit 0, RY(θ1) on qubit 1, RY(θ2) on qubit 2")
140+
print(" CNOT(0→1), CNOT(1→2)")
141+
print(" Layer 2: RY(θ3) on qubit 0, RY(θ4) on qubit 1, RY(θ5) on qubit 2")
142+
print(" CNOT(0→1), CNOT(1→2)")
143+
Explanation of key steps: We generate a random Hermitian H and start in |000⟩. The ansatz state is built by applying two layers of Ry rotations and CNOTs (as described above) using NumPy arrays and kron/indexing. The energy ⟨ψ|H|ψ⟩ is computed by a vector-matrix-vector multiplication. We minimize this energy using scipy.optimize.minimize(method='COBYLA') , storing the energy after each step. Finally, we plot the recorded energy vs. iteration (as shown) and print a textual description of the ansatz circuit. This fully implements VQE without relying on Qiskit or PennyLane.
144+
145+
Sources: The VQE method and energy-minimization principle are discussed in standard references . Quantum states are vectors and gates are matrices in simulation , and using single-qubit Ry rotations with CNOTs is a universal ansatz . We use the COBYLA optimizer from SciPy as recommended for VQE tasks .

0 commit comments

Comments
 (0)