diff --git a/examples/expectation_values/README.md b/examples/expectation_values/README.md new file mode 100644 index 0000000..6f30838 --- /dev/null +++ b/examples/expectation_values/README.md @@ -0,0 +1,28 @@ +## Benchmarking how far can we go simulating on classical computers + +**Problem:** How about computing exact expectation values in the form `<Ψ| Op |Ψ>`? + +### Instructions to run + +This script tries to simulate a 3-qubit system (011) and applies three different gates on the three qubits. Then we take `Op` as the Hadamard gate on the first qubit. + +```sh +python examples/benchmark/speed_test.py +``` + +Parameters: + +1. `--sim-type (-s)`: Select the simulation type for the system. Default: vector + +### Explanation + +In quantum mechanics, the expectation value of any operator for any normalized vector `Ψ` is given by `<Ψ| Op |Ψ>`. Ref: [Expectation value]() + +So, first we need to normalize the state of the quantum system (tensor or vector states). + +To measure the expectation values of the `<Ψ| Op |Ψ>`. We need to prepare `Op` for the 3-qubit system here where the other two qubits can be applied a quantum wire or Identity matrix. The given expression can be understood as two parts: + +1. `<Ψ|` as the hermitian conjugate of `|Ψ>` -> complex conjugate of transpose of `|Ψ>` +2. `Op |Ψ>` as the `Op` multiplied with `psi` -> applying the operator on `|Ψ>` normally + +Once we get these two, we take the inner product of these two matrices we get from the above steps to get the expectation value. diff --git a/examples/expectation_values/expectation_values.py b/examples/expectation_values/expectation_values.py new file mode 100644 index 0000000..aea7fba --- /dev/null +++ b/examples/expectation_values/expectation_values.py @@ -0,0 +1,66 @@ +import qcir_sim, argparse +import numpy as np + + +def parse_args(): + parser = argparse.ArgumentParser(description="Benchmarking the simulation") + parser.add_argument( + "--sim-type", + "-s", + choices=["tensor", "vector"], + default="vector", + help="Type of simulation", + ) + return parser.parse_args() + + +def get_random_state(): + choice = np.random.choice([0, 1]) + if choice == 0: + return qcir_sim.zero() + return qcir_sim.one() + + +def vector_expectation_value(sim: qcir_sim.VectorSimulation, gate: str, gate_idx: int): + psi = sim.vector_state + psi /= np.linalg.norm(psi) + Op = sim._prepare_gate(gate, gate_idx) + + op_mul_psi = Op @ psi + bra_notation = np.conjugate(np.transpose(psi)) + + expectation_value = bra_notation @ op_mul_psi + print(f"Expectation Value: {expectation_value}") + + +def tensor_expectation_value(sim: qcir_sim.TensorSimulation, gate: str, gate_idx: int): + psi = sim.tensor_state + psi /= np.linalg.norm(psi) + Op = sim._prepare_gate(gate, gate_idx) + + op_mul_psi = np.einsum("ikj,ik->ij", Op, psi) + bra_notation = np.conjugate(np.transpose(psi)) + + expectation_value = np.einsum("ij,ji", bra_notation, op_mul_psi) + print(f"Expectation Value: {expectation_value}") + + +if __name__ == "__main__": + args = parse_args() + simulation_type = args.sim_type + print(f"Using {simulation_type} simulation") + + qubits = [qcir_sim.zero(), qcir_sim.one(), qcir_sim.one()] + if args.sim_type == "tensor": + sim = qcir_sim.TensorSimulation(qubits) + else: + sim = qcir_sim.VectorSimulation(qubits) + + sim.add_gate("H", 1) + sim.add_gate("phase", 2) + sim.add_gate("H", 3) + + if args.sim_type == "tensor": + tensor_expectation_value(sim, "H", 1) + else: + vector_expectation_value(sim, "H", 1) diff --git a/examples/sampling/README.md b/examples/sampling/README.md index 9ea6b7b..1de910a 100644 --- a/examples/sampling/README.md +++ b/examples/sampling/README.md @@ -15,7 +15,7 @@ Run the program using: python examples/sampling/sampling.py -n 10 -t ``` -### Explaination +### Explanation In a vector simulation of a n-qubit system, each element describes a possible combination of the state of qubit. For n-qubits, the state vector will have 2^n states.