From 505c4a7cfce0433f112fafddfe84ce02e4a987c1 Mon Sep 17 00:00:00 2001 From: Urdeney Date: Tue, 16 Nov 2021 21:10:11 +0300 Subject: [PATCH 1/2] Add Quantum Gates --- lib/qar.rb | 1 + lib/qgates.rb | 78 ++++++++++++++++++++++++++++++++++++++++ lib/quantum_exception.rb | 6 ++++ test/qar_test.rb | 1 + test/qgates_test.rb | 5 +++ 5 files changed, 91 insertions(+) create mode 100644 lib/qgates.rb create mode 100644 test/qgates_test.rb diff --git a/lib/qar.rb b/lib/qar.rb index 11fce1f..fb41b47 100644 --- a/lib/qar.rb +++ b/lib/qar.rb @@ -3,6 +3,7 @@ require_relative "qar/version" require "qbit" require "entanglement" +require "qgates" require_relative "quantum_exception" module Qar diff --git a/lib/qgates.rb b/lib/qgates.rb new file mode 100644 index 0000000..d1afaad --- /dev/null +++ b/lib/qgates.rb @@ -0,0 +1,78 @@ +require "matrix" +require_relative "qar/extensions/extensions" + +class Gate < Matrix + + include QuantumException + def *(*args) + if ((2*args.count)!=self.column_count) + raise GateArgumentException + end + + case args[0] + when Qubit + qubits = [] + args = args.map do |i| + qubits << i + end.reduce(:kronecker) + State.new(*qubits) + when State + qubits = [] + args = args.map do |i| + qubits << i.qubits + end.reduce(:kronecker) + State.new(qubits) + else + super(*args) + end + end + + # TEMP function for Kronecker Product + def kronecker(m) + res=Matrix[[1,0],[0,1]] + return res + end + +end + +X_GATE = Gate[ + [0, 1], + [1, 0]] + +Y_GATE = Gate[ + [0, -1i], + [1i, 0]] + +Z_GATE = Gate[ + [1, 0], + [0, -1]] + +H_GATE = 1 / Math.sqrt(2) * Gate[ + [1, 1], + [1, -1]] + +T_GATE = Gate[ + [1,0], + [0, Math::E**((1i * Math::PI) / 4)]] + +CNOT_GATE = Gate[ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 0, 1], + [0, 0, 1, 0]] + +SWAP_GATE = Gate[ + [1, 0, 0, 0], + [0, 0, 1, 0], + [0, 1, 0, 0], + [0, 0, 0, 1]] + +TOFFOLI_GATE = Gate[ + [1, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 1], + [0, 0, 0, 0, 0, 0, 1, 0]] diff --git a/lib/quantum_exception.rb b/lib/quantum_exception.rb index 732b44e..b34f9c7 100644 --- a/lib/quantum_exception.rb +++ b/lib/quantum_exception.rb @@ -20,4 +20,10 @@ def initialize(msg = "An entanglement cannot be empty") super(msg) end end + + class GateArgumentException < StandardError + def initialize(msg = "Quantum gates arguments are incorrect") + super(msg) + end + end end diff --git a/test/qar_test.rb b/test/qar_test.rb index 5a6a757..797b756 100644 --- a/test/qar_test.rb +++ b/test/qar_test.rb @@ -3,6 +3,7 @@ require_relative "test_helper" require_relative "qbit_test" require_relative "entanglement_test" +require_relative "qgates_test" class QarTest < Minitest::Test diff --git a/test/qgates_test.rb b/test/qgates_test.rb new file mode 100644 index 0000000..7fc6e92 --- /dev/null +++ b/test/qgates_test.rb @@ -0,0 +1,5 @@ +require_relative "test_helper" + +class QgatesTest < Minitest::Test + # Your code goes here... +end \ No newline at end of file From 2c4a451a9ed46d13d1689f742461b9848aca0d48 Mon Sep 17 00:00:00 2001 From: Urdeney Date: Thu, 23 Dec 2021 23:55:57 +0300 Subject: [PATCH 2/2] Fix quantum gates logic, add tests for gates --- lib/qar/extensions/extensions.rb | 7 --- lib/qbit.rb | 4 ++ lib/qgates.rb | 62 ++++++++++++++------ test/qgates_test.rb | 97 +++++++++++++++++++++++++++++++- test/test_helper.rb | 2 +- 5 files changed, 146 insertions(+), 26 deletions(-) diff --git a/lib/qar/extensions/extensions.rb b/lib/qar/extensions/extensions.rb index 8057f5f..0aa9998 100644 --- a/lib/qar/extensions/extensions.rb +++ b/lib/qar/extensions/extensions.rb @@ -10,13 +10,6 @@ def round(digits) end end -# Integer class round extension -class Integer - def round(num) - num - end -end - # Matrix class product and print extensions class Matrix class << self diff --git a/lib/qbit.rb b/lib/qbit.rb index 8dd1d3e..d8f6d06 100644 --- a/lib/qbit.rb +++ b/lib/qbit.rb @@ -44,6 +44,10 @@ def vector=(vector) raise NormalizationException unless normalized? end + def nil_entanglement() + @entanglement = nil + end + # Measurement give information about current qubit that is # use probabilities of one and zero to give the answer what # qubit contains now diff --git a/lib/qgates.rb b/lib/qgates.rb index d1afaad..4275341 100644 --- a/lib/qgates.rb +++ b/lib/qgates.rb @@ -1,38 +1,66 @@ require "matrix" require_relative "qar/extensions/extensions" +# This class represents quantum logic gates as matrices. +# List of implemented quantum gates: +# X_GATE +# Y_GATE +# Z_GATE +# H_GATE +# T_GATE +# C_NOT_GATE +# SWAP_GATE +# TOFFOLI_GATE class Gate < Matrix - - include QuantumException + # Operation,that applies the effect on both qubits and states containing an array of qubits def *(*args) - if ((2*args.count)!=self.column_count) - raise GateArgumentException - end - case args[0] when Qubit qubits = [] args = args.map do |i| - qubits << i - end.reduce(:kronecker) + qubits << i + i.vector + end.inject{|p,a| Matrix.kronecker(p,a)} + qubits=measure(qubits, super(args)) State.new(*qubits) + + when State qubits = [] - args = args.map do |i| - qubits << i.qubits - end.reduce(:kronecker) - State.new(qubits) + temp=[] + args.map do |i| + i.qubits.map do |j| + qubits << j + temp << j.vector + end + end + args=temp.inject{|p,a| Matrix.kronecker(p,a)} + qubits=measure(qubits, super(args)) + State.new(*qubits) else super(*args) end end - # TEMP function for Kronecker Product - def kronecker(m) - res=Matrix[[1,0],[0,1]] - return res + private + # Function that takes array of qubits and their Kronecker product and calculates new state. + # Works properly only for single qubits. For multiple qubits, Kronecker product of qubits become entangled + def measure(qubits, vector) + cnt=qubits.count() + case cnt + when 1 + vector=vector.to_a.flatten + return [Qubit.new(vector[0],vector[1])] + when 2 + vector=vector.to_a.flatten + return [Qubit.new(vector[0],vector[1]),Qubit.new(vector[2],vector[3])] + when 3 + vector=vector.to_a.flatten + return [Qubit.new(vector[0],vector[1]),Qubit.new(vector[2],vector[3]),Qubit.new(vector[4],vector[5])] + else + return raise GateArgumentException + end end - end X_GATE = Gate[ diff --git a/test/qgates_test.rb b/test/qgates_test.rb index 7fc6e92..7752275 100644 --- a/test/qgates_test.rb +++ b/test/qgates_test.rb @@ -1,5 +1,100 @@ require_relative "test_helper" class QgatesTest < Minitest::Test - # Your code goes here... + + def test_it_does_something_useful + # test that we are not a teapot... just 4 fun + assert self != :i_am_a_teapot + + # test that no errors occur when importing the module + assert_nothing_raised do + require "qar" + end + end + + #test section for single-qubit quantum gates + + # x_gate = [[0, 1], + # [1, 0]] + def test_X_GATE + + q1=Qubit.new(1,0) + s1=X_GATE * q1 + assert_equal s1.qubits.map!{ |i| i.vector },[ Qubit.new(0,1).vector] + end + + # y_gate = [[0, -1i], + # [1i, 0]] + def test_Y_GATE + q1=Qubit.new(1,0) + s1=Y_GATE * q1 + assert_equal s1.qubits.map!{ |i| i.vector },[ Qubit.new(0+0i,0+1i).vector] + end + + # z_gate = [[1, 0], + # [0, -1]] + def test_Z_GATE + q1=Qubit.new(1,0) + s1=Z_GATE*q1 + assert_equal s1.qubits.map!{ |i| i.vector },[Qubit.new(1,0).vector] + end + + # h_gate = 1/sqrt(2)[[1, 1], + # [1, -1]] + def test_H_GATE + q1=Qubit.new(1,0) + s1=H_GATE * q1 + assert_equal s1.qubits.map!{ |i| i.vector },[Qubit.new(1/Math.sqrt(2),1/Math.sqrt(2)).vector] + end + + # t_gate = [[1, 0], + # [0, e^(i*pi/4)]] + def test_T_GATE + q1=Qubit.new(1,0) + s1=T_GATE * q1 + assert_equal s1.qubits.map!{ |i| i.vector },[Qubit.new(1,0).vector] + end + + #test section for states with single qubit in it + + def test_X_GATE_State + + q1=Qubit.new(1,0) + s1=X_GATE*X_GATE * q1 + assert_equal s1.qubits.map!{ |i| i.vector },[q1.vector] + end + + def test_Y_GATE_State + + q1=Qubit.new(1,0) + s1=Y_GATE*Y_GATE * q1 + assert_equal s1.qubits.map!{ |i| i.vector },[q1.vector] + end + + def test_Z_GATE_State + + q1=Qubit.new(1,0) + s1=Z_GATE*Z_GATE * q1 + assert s1.qubits.map!{ |i| i.vector },[q1.vector] + end + + #test need more accuracy due to accumulated error + def test_H_GATE_State + q1=Qubit.new(1,0) + s1=H_GATE*H_GATE * q1 + assert_equal s1.qubits.map!{ |i| i.vector }, [Qubit.new(0.9999999999999998,0).vector] + end + + def test_T_GATE_State + + q1=Qubit.new(1,0) + s1=T_GATE*T_GATE * q1 + assert_equal s1.qubits.map!{ |i| i.vector },[q1.vector] + end + + def test_Muptiple_Gates_Combination + q1=Qubit.new(1,0) + s1=Z_GATE*X_GATE*T_GATE*X_GATE*Y_GATE * q1 + assert_equal s1.qubits.map!{ |i| i.vector },[Qubit.new(0+0i,0-1i).vector] + end end \ No newline at end of file diff --git a/test/test_helper.rb b/test/test_helper.rb index 8a6eec8..b8ca827 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -4,7 +4,7 @@ SimpleCov.start $LOAD_PATH.unshift File.expand_path("../lib", __dir__) -require_relative "qar" +require "qar" require 'simplecov' require "minitest/autorun"