Hi @movingwaters , welcome to the Forum 
This is a really good question! The answer depends on whether or not your matrix A is unitary.
First, remember that applying a gate to a quantum state is applying a matrix to that state, which itself is represented as a vector.
You would start with encoding your vector x into the quantum computer, for example using qp.AmplitudeEmbedding.
If the matrix A is unitary then you can directly implement it as a quantum gate. If the matrix’s dimensions are a power of two you can add it to your circuit with qp.QubitUnitary.
If your matrix A is non-unitary (general case) then you can use block encodings. This means embedding A as a sub-block of a larger unitary U, acting on a larger system which includes ancilla qubits. In practice we actually embed A/\alpha (where \alpha \geq |A|) so that the final unitary is actually unitary. You can learn more about Block Encodings and how to use them in PennyLane in the PennyLane Codebook.
The procedure is:
- Prepare ancilla qubits in |0\rangle
- Encode |x\rangle in the system register
- Apply the unitary U
- Measure the ancilla.
If you get |0\rangle, the system register holds A|x\rangle / \alpha (up to normalization)
The success probability is |A|x\rangle|^2 / \alpha^2, so amplitude amplification can boost this.
Here’s a full code example you can use.
import pennylane as qp
import numpy as np
# Define your matrix A and vector x
A = np.array([
[0.3, 0.5],
[0.1, 0.4]
])
x = np.array([0.6, 0.8])
# Normalize x (required for AmplitudeEmbedding)
x_norm = np.linalg.norm(x)
x_normalized = x / x_norm
# Create the block encoding operator
block_op = qp.BlockEncode(A, wires=[0, 1])
# Check PennyLane's auto-normalization factor
norm_factor = block_op.hyperparameters["norm"]
effective_norm = max(norm_factor, 1.0)
# Build the circuit
dev = qp.device("default.qubit")
@qp.qnode(dev)
def apply_A_to_x():
# Wire 0 = ancilla, Wire 1 = system
qp.AmplitudeEmbedding(x_normalized, wires=1)
qp.BlockEncode(A, wires=[0, 1])
return qp.state()
# Execute and post-select
statevector = apply_A_to_x()
post_selected = statevector[:2] # ancilla = |0> subspace
# Recover A @ x
Ax_result = effective_norm * x_norm * post_selected
print(f"Quantum result: {Ax_result.real}") # [0.58, 0.38]
print(f"Classical A@x: {(A @ x).real}") # [0.58, 0.38]
I hope this helps!