import pennylane as qml
import numpy as np
from scipy.linalg import expm
import math

def HHL_4x4() : 
    """
        Implement a quantum circuit that uses the Harrow-Hassidim-Lloyd (HHL) algorithm to solve a linear system Ax = b,
        where:

            A = (1/2) * np.array([[3, 0, 0, -1],
                                [0, 7, -1, 0],
                                [0, -1, 7, 0],
                                [-1, 0, 0, 3]])

            b = np.array([1/√2, 1/√2, 0, 0])  # A normalized 4-dim vector
            t = π/4                            # Evolution time
            The clock register uses 3 qubits.
        return qml.sample() with number of shots = 1000.
    """

Framework: pennylane | Category: QUANTUM_ALGO

Solution:
from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister
import numpy as np
from qiskit.quantum_info import Statevector
from scipy.linalg import expm
from qiskit.circuit.library import UnitaryGate
from qiskit.circuit.library import QFT
from qiskit.circuit.library import RYGate
import math

def HHL_4x4() ->QuantumCircuit: 
    Stateprep = QuantumCircuit(2, name = "State Prep")
    Stateprep.h(0)
    psi  = Statevector(Stateprep)
    clock = QuantumRegister(3, name='clock')
    b = QuantumRegister(2, name='b')
    ancilla = QuantumRegister(1, name='ancilla')
    classica_ancialla = ClassicalRegister(1, name = "anciall classical")
    classica_results = ClassicalRegister(2, name = "result classical")
    circuit = QuantumCircuit(ancilla, clock, b, classica_ancialla, classica_results)
    circuit.append(Stateprep, b)
    circuit.h(clock)
    A = (1/2) * np.array([
        [3,   0,   0,  -1],
        [0,   7,  -1,   0],
        [0,  -1,   7,   0],
        [-1,  0,   0,   3]
    ]) 
    t = np.pi / 4
    for idx,c in enumerate(clock) : 
        circuit.append(UnitaryGate(expm(1j * A * t * (2**idx) ), label = f"$e^{{i A t \; 2^{idx}}}$").control(num_ctrl_qubits=1) , [c , b[0] , b[1]])
    circuit.append(QFT(3, inverse=True), clock)
    Rotation1 = QuantumCircuit(4, name = "Ry(θ1)")
    theta_1 = 2 * math.asin(1/1.0)
    Rotation1.append(RYGate(theta_1).control(3, ctrl_state= "001"), [1, 2, 3, 0])
    circuit.append(Rotation1 , range(4))
    Rotation2 = QuantumCircuit(4, name = "Ry(θ2)")
    theta_2 = 2 * math.asin(1/2.0)
    Rotation2.append(RYGate(theta_2).control(3, ctrl_state= "010"), [1, 2, 3, 0])
    circuit.append(Rotation2 , range(4))
    Rotation3 = QuantumCircuit(4, name = "Ry(θ3)")
    theta_3 = 2 * math.asin(1/3.0)
    Rotation3.append(RYGate(theta_3).control(3, ctrl_state= "011"), [1, 2, 3, 0])
    circuit.append(Rotation3 , range(4))
    Rotation4 = QuantumCircuit(4, name = "Ry(θ4)")
    theta_4 = 2 * math.asin(1/4.0)
    Rotation4.append(RYGate(theta_4).control(3, ctrl_state= "100"), [1, 2, 3, 0])
    circuit.append(Rotation4 , range(4))
    circuit.measure(ancilla, classica_ancialla)
    circuit.append(QFT(3, inverse=False), clock)
    for idx, c in enumerate(clock):
        # If you really want a negative i in the exponent:
        Uin = expm((-1j * t) * A * 2**(2 - idx))  # Adjust indexing as needed
        circuit.append(
            UnitaryGate(Uin, label=f"$e^{{-i A t \, 2^{2-idx}}}$").control(num_ctrl_qubits=1),
            [clock[2 - idx], b[0], b[1]]
        )
    circuit.h(clock)
    circuit.measure(b , classica_results )

    return circuit