quantum_ruby 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +20 -0
- data/LICENSE.txt +21 -0
- data/README.md +151 -0
- data/Rakefile +8 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/examples/general.rb +266 -0
- data/examples/readme_examples.rb +62 -0
- data/lib/quantum_ruby.rb +298 -0
- data/lib/quantum_ruby/version.rb +3 -0
- data/quantum_ruby.gemspec +26 -0
- metadata +84 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 68be46a736a6bdd4f752c9232f65cddf546ca4dbf8b9b72f41fb6237860871e2
|
4
|
+
data.tar.gz: b0a51c8d0e885817db849e60f87a187e20a90dce5113d0e2c4a9b8962c6a2f85
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: edbe804cdc48d763966e8b0f343aa8ab4d9dd00595f84b5951289311ca5f81623465b5ade7c5462b263b6402a4d81f4c86370a6360fd1e75db6b906462be1415
|
7
|
+
data.tar.gz: 72c0d4713f3025e9702c45aa05a9c56746e59f715322c769db7df1fe4a95c8b60bff536149e460500ad470e43a4e0fc0be27230dfabfe903a40996a1bf9f0658
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2020 Alessandro
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
|
2
|
+
# QuantumRuby
|
3
|
+
|
4
|
+
This is a quantum computer simulator in under 300 lines of ruby. Meaning you can build arbitrarily large quantum circuits programmatically and simulate their behaviours.
|
5
|
+
|
6
|
+
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/6/65/Qcircuit_CNOTfromSQRTSWAP.svg/1024px-Qcircuit_CNOTfromSQRTSWAP.svg.png" />
|
7
|
+
|
8
|
+
Learning about quantum computing isn't that difficult. Trust me! I learned and built this simulator within five days with no prior quantum computing knowledge. This gem can also be a great tool to facilitate learning about quantum computing.
|
9
|
+
|
10
|
+
[Check out the many examples in this repo.](https://github.com/AlessandroMinali/quantum_ruby/examples)
|
11
|
+
|
12
|
+
## How to learn about Quantum Computers
|
13
|
+
1. Have an understanding of Linear Algebra. This [series of youtube lectures](https://www.youtube.com/playlist?list=PLZHQObOWTQDPD3MizzM2xVFitgF8hE_ab) can get you up to speed on everything you need to know
|
14
|
+
2. Read this great intro to quantum computing. This is the single resource that I read before I began implementing this simulator: [Quantum computing for the very curious](https://quantum.country/qcvc)
|
15
|
+
3. Experiment with this simulator as you learn about quantum computing
|
16
|
+
4. Read the [other articles](https://quantum.country) about quantum computing. Read wiki pages about [qubits](https://en.wikipedia.org/wiki/Qubit) and [quantum gates](https://en.wikipedia.org/wiki/Quantum_logic_gate). Both should be understandable to you now!
|
17
|
+
5. If you ever get stuck start back at the top of this list or reach out to me with questions: `alessandro.minali AT gmail DOT com`
|
18
|
+
|
19
|
+
## Installation
|
20
|
+
Add this line to your application's Gemfile:
|
21
|
+
```ruby
|
22
|
+
gem 'quantum_ruby'
|
23
|
+
```
|
24
|
+
|
25
|
+
And then execute:
|
26
|
+
|
27
|
+
$ bundle install
|
28
|
+
|
29
|
+
Or install it yourself as:
|
30
|
+
|
31
|
+
$ gem install quantum_ruby
|
32
|
+
In your code:
|
33
|
+
```ruby
|
34
|
+
require 'quantum_ruby`
|
35
|
+
```
|
36
|
+
|
37
|
+
## Usage
|
38
|
+
|
39
|
+
**This gem adds `Matrix#kronecker` and `Complex#round` onto ruby base classes**
|
40
|
+
|
41
|
+
There are three objects that this gem uses to simulate any quantum circuitry.
|
42
|
+
|
43
|
+
---
|
44
|
+
### Qubit
|
45
|
+
Quantum computing is achieved by manipulating quantum bits(ie. qubits). For simulation purposes we can arbitrarily create qubits, manipulate them and read their results.
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
qubit_1 = Qubit.new(1, 0)
|
49
|
+
qubit_1.measure # returns 0 bit
|
50
|
+
qubit_2 = Qubit.new(0, 1)
|
51
|
+
qubit_2.measure # returns 1 bit
|
52
|
+
```
|
53
|
+
|
54
|
+
---
|
55
|
+
|
56
|
+
### Gate
|
57
|
+
Quantum gates perform operations on single or multi qubits to produce a variety of classical and non-classical behaviours(such as superpositions and entanglement). Gates are simulated as matrices and interact with qubits via multiplication.
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
# single qubit gate example (X_GATE ie. quantum NOT gate)
|
61
|
+
qubit_1 = Qubit.new(0, 1) # equivalent to classical 1 bit
|
62
|
+
state = X_GATE * qubit_1
|
63
|
+
state.measure # returns 0 bit
|
64
|
+
|
65
|
+
# multi qubit gate example (C_NOT_GATE ie. controlled NOT gate)
|
66
|
+
control_qubit = Qubit.new(1, 0) # 0 bit
|
67
|
+
target_qubit = Qubit.new(0, 1) # 1 bit
|
68
|
+
# note multi params syntax: "GATE.*(param1, param2, etc.)"
|
69
|
+
state = C_NOT_GATE.*(control_qubit, target_qubit)
|
70
|
+
state.measure_partial(target_qubit) # return 1 bit since control is 0
|
71
|
+
|
72
|
+
# again with control bit ON
|
73
|
+
control_qubit = Qubit.new(0, 1) # 1 bit
|
74
|
+
target_qubit = Qubit.new(0, 1) # 1 bit
|
75
|
+
state = C_NOT_GATE.*(control_qubit, target_qubit)
|
76
|
+
state.measure_partial(target_qubit) # return 0 bit since control is 1
|
77
|
+
```
|
78
|
+
|
79
|
+
#### Provided Gates
|
80
|
+
X_GATE
|
81
|
+
Y_GATE
|
82
|
+
Z_GATE
|
83
|
+
H_GATE
|
84
|
+
T_GATE
|
85
|
+
C_NOT_GATE
|
86
|
+
SWAP_GATE
|
87
|
+
TOFFOLI_GATE
|
88
|
+
Information about any of these gates behaviours can be found [here on wiki](https://en.wikipedia.org/wiki/Quantum_logic_gate).
|
89
|
+
|
90
|
+
#### Advanced Gate Usage
|
91
|
+
|
92
|
+
#### 1. Making your own gates
|
93
|
+
This can be done simply like creating a ruby `Matrix`:
|
94
|
+
```ruby
|
95
|
+
x = Matrix[[0, 1], [1, 0]]
|
96
|
+
y = Gate[[0, 1], [1, 0]]
|
97
|
+
```
|
98
|
+
Note: A true quantum gate must be [unitary](https://en.wikipedia.org/wiki/Unitary_matrix). You can verify if your creation is unitary with the built-in `Matrix#unitary?`
|
99
|
+
###### [some versions of ruby have a broken implementation of this function](https://github.com/ruby/matrix/pull/14)
|
100
|
+
#### 2. `Matrix#kronecker(matrix)`
|
101
|
+
This method allows you to parallelize and scale gates. Examples follow:
|
102
|
+
|
103
|
+
Parallel Gates:
|
104
|
+
<img src="https://upload.wikimedia.org/wikipedia/commons/d/d5/Parallel_quantum_logic_gates.png" />
|
105
|
+
```ruby
|
106
|
+
Y_X_GATE = Y_GATE.kronecker(X_GATE)
|
107
|
+
```
|
108
|
+
|
109
|
+
Scaling Gate:
|
110
|
+
<img src="https://upload.wikimedia.org/wikipedia/commons/d/d2/Shows_the_application_of_a_hadamard_gate_on_a_state_that_span_two_qubits.png" />
|
111
|
+
```ruby
|
112
|
+
state = State.new(Matrix.column_vector([0, 0, 0, 1]))
|
113
|
+
# H_GATE normally only works on 1 qubit ie. 2x1 matrix
|
114
|
+
# the following will auto scale H_GATE with kronecker
|
115
|
+
new_state = H_GATE.*(state, scale: :down)
|
116
|
+
```
|
117
|
+
|
118
|
+
---
|
119
|
+
|
120
|
+
### State
|
121
|
+
|
122
|
+
Generally once a qubit enters a circuit we no longer care about it. We are instead interested in the combine state of the whole circuit where multiple qubits are being processed by gates. After sending the qubits through a circuit this object will hold their results in a probabilistic model. We can take a measurement to get classical information back out of the qubits.
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
qubit_1 = Qubit.new(0, 1) # equivalent to classical 1 bit
|
126
|
+
state = H_GATE * qubit_1
|
127
|
+
state.measure # returns 0 bit or 1 bit with equal probability
|
128
|
+
```
|
129
|
+
|
130
|
+
#### `State#measure` vs `State#measure_partial(qubits)`
|
131
|
+
In multi-qubit systems gates put the qubits into a combined state. We can either measure the entire state to determine an outcome or try to extract information about specific qubits at the expense of information of the total system. In most cases we are interested in `State#measure`. Make sure you understand which one you intend to do in any situation. [Resource](https://quantum.country/teleportation#background_partial_measurement)
|
132
|
+
|
133
|
+
## Other Resources
|
134
|
+
For some trickier aspects of quantum computing these online resources really helped me grasp deeper concepts:
|
135
|
+
- https://quantumcomputing.stackexchange.com/questions/9614/how-to-interpret-a-4-qubit-quantum-circuit-as-a-matrix/9615#9615
|
136
|
+
- https://quantumcomputing.stackexchange.com/questions/4252/how-to-derive-the-cnot-matrix-for-a-3-qbit-system-where-the-control-target-qbi
|
137
|
+
- https://cs.stackexchange.com/questions/71462/how-are-partial-measurements-performed-on-a-n-qubit-quantum-circuit
|
138
|
+
|
139
|
+
## Development
|
140
|
+
|
141
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
142
|
+
|
143
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
144
|
+
|
145
|
+
## Contributing
|
146
|
+
|
147
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/AlessandroMinali/quantum_ruby.
|
148
|
+
|
149
|
+
## License
|
150
|
+
|
151
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "quantum_ruby"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/examples/general.rb
ADDED
@@ -0,0 +1,266 @@
|
|
1
|
+
require 'quantum_ruby'
|
2
|
+
# should be added in upcoming ruby/matrix patch
|
3
|
+
class Matrix
|
4
|
+
def adjoint
|
5
|
+
conjugate.transpose
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
# Matrix * test
|
10
|
+
a = Matrix[[1, 2, 3], [4, 5, 6]]
|
11
|
+
b = Matrix[[7, 8], [9, 10], [11, 12]]
|
12
|
+
raise unless a * b == Matrix[[58, 64], [139, 154]]
|
13
|
+
|
14
|
+
# Matrix * test
|
15
|
+
a = Matrix[[1, 2], [3, 4]]
|
16
|
+
b = Matrix[[2, 0], [1, 2]]
|
17
|
+
raise unless a * b == Matrix[[4, 4], [10, 8]]
|
18
|
+
|
19
|
+
# Kronecker product test
|
20
|
+
x = Matrix[[1, 2], [3, 4]]
|
21
|
+
y = Matrix[[0, 5], [6, 7]]
|
22
|
+
raise unless x.kronecker(y) == Matrix[[0, 5, 0, 10], [6, 7, 12, 14], [0, 15, 0, 20], [18, 21, 24, 28]]
|
23
|
+
|
24
|
+
x = Matrix[[1], [2]]
|
25
|
+
y = Matrix[[3], [4]]
|
26
|
+
raise unless x.kronecker(y) == Matrix[[3], [4], [6], [8]]
|
27
|
+
|
28
|
+
# 'Ket' visualization
|
29
|
+
x = Qubit.new(1, 0)
|
30
|
+
raise unless x.to_s == '1|0>'
|
31
|
+
|
32
|
+
# Complex number visualization
|
33
|
+
x = Qubit.new((1 + 1i) / 2, 1i / Math.sqrt(2))
|
34
|
+
raise unless x.to_s == '1/2+1/2i|0> + 0.707i|1>'
|
35
|
+
raise unless x.state == "[1/2+1/2i\n 0.707i]"
|
36
|
+
|
37
|
+
# NOT gate
|
38
|
+
z = Qubit.new(1, 0)
|
39
|
+
raise unless X_GATE * z == State.new(Matrix[[0], [1]])
|
40
|
+
|
41
|
+
# Uniform superposition gate
|
42
|
+
z = Qubit.new(1, 0)
|
43
|
+
raise unless H_GATE * z == State.new(Matrix[[1 / Math.sqrt(2)], [1 / Math.sqrt(2)]])
|
44
|
+
|
45
|
+
# Demonstrate gate reversibility
|
46
|
+
z = Qubit.new(0.6, 0.8)
|
47
|
+
raise unless H_GATE * H_GATE * z == State.new(Matrix[[0.6], [0.8]])
|
48
|
+
|
49
|
+
# Measuring
|
50
|
+
1_000.times do
|
51
|
+
x = Qubit.new(1, 0).measure
|
52
|
+
raise unless x.zero?
|
53
|
+
|
54
|
+
x = Qubit.new(0, 1).measure
|
55
|
+
raise unless x == 1
|
56
|
+
|
57
|
+
# equal chance of result
|
58
|
+
s = (H_GATE * Qubit.new(0, 1))
|
59
|
+
case s.measure[0]
|
60
|
+
when 0
|
61
|
+
raise unless s == State.new(Matrix[[1], [0]])
|
62
|
+
when 1
|
63
|
+
raise unless s == State.new(Matrix[[0], [1]])
|
64
|
+
else
|
65
|
+
raise
|
66
|
+
end
|
67
|
+
|
68
|
+
x = Qubit.new(1, 0)
|
69
|
+
y = Qubit.new(0, 1)
|
70
|
+
a = Array.new(4, 0)
|
71
|
+
a[rand(4)] = 1
|
72
|
+
r = State.new(Matrix.column_vector(a), [x, y]).measure.join.to_i(2)
|
73
|
+
case r
|
74
|
+
when 0
|
75
|
+
raise unless x == Qubit.new(1, 0) && y == Qubit.new(1, 0)
|
76
|
+
when 1
|
77
|
+
raise unless x == Qubit.new(1, 0) && y == Qubit.new(0, 1)
|
78
|
+
when 2
|
79
|
+
raise unless x == Qubit.new(0, 1) && y == Qubit.new(1, 0)
|
80
|
+
when 3
|
81
|
+
raise unless x == Qubit.new(0, 1) && y == Qubit.new(0, 1)
|
82
|
+
end
|
83
|
+
|
84
|
+
x = Qubit.new(1, 0)
|
85
|
+
y = Qubit.new(0, 1)
|
86
|
+
z = Qubit.new(0, 1)
|
87
|
+
a = Array.new(8, 0)
|
88
|
+
a[rand(8)] = 1
|
89
|
+
r = State.new(Matrix.column_vector(a), [x, y, z]).measure.join.to_i(2)
|
90
|
+
case r
|
91
|
+
when 0
|
92
|
+
raise unless x == Qubit.new(1, 0) && y == Qubit.new(1, 0) && z == Qubit.new(1, 0)
|
93
|
+
when 1
|
94
|
+
raise unless x == Qubit.new(1, 0) && y == Qubit.new(1, 0) && z == Qubit.new(0, 1)
|
95
|
+
when 2
|
96
|
+
raise unless x == Qubit.new(1, 0) && y == Qubit.new(0, 1) && z == Qubit.new(1, 0)
|
97
|
+
when 3
|
98
|
+
raise unless x == Qubit.new(1, 0) && y == Qubit.new(0, 1) && z == Qubit.new(0, 1)
|
99
|
+
when 4
|
100
|
+
raise unless x == Qubit.new(0, 1) && y == Qubit.new(1, 0) && z == Qubit.new(1, 0)
|
101
|
+
when 5
|
102
|
+
raise unless x == Qubit.new(0, 1) && y == Qubit.new(1, 0) && z == Qubit.new(0, 1)
|
103
|
+
when 6
|
104
|
+
raise unless x == Qubit.new(0, 1) && y == Qubit.new(0, 1) && z == Qubit.new(1, 0)
|
105
|
+
when 7
|
106
|
+
raise unless x == Qubit.new(0, 1) && y == Qubit.new(0, 1) && z == Qubit.new(0, 1)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
x = Qubit.new(1 / Math.sqrt(2), 1 / Math.sqrt(2))
|
111
|
+
raise unless (H_GATE * x).measure[0].zero?
|
112
|
+
|
113
|
+
x = Qubit.new(1 / Math.sqrt(2), -1 / Math.sqrt(2))
|
114
|
+
raise unless (H_GATE * x).measure[0] == 1
|
115
|
+
|
116
|
+
# Backwards control gate!
|
117
|
+
x = Qubit.new(1, 0)
|
118
|
+
y = Qubit.new(0, 1)
|
119
|
+
h_big = H_GATE.kronecker(H_GATE)
|
120
|
+
z = h_big * (C_NOT_GATE * h_big)
|
121
|
+
raise unless z.*(x, y) == State.new(Matrix[[0.0], [0.0], [0.0], [1.0]])
|
122
|
+
|
123
|
+
# Bell state
|
124
|
+
x = Qubit.new(1, 0)
|
125
|
+
y = Qubit.new(1, 0)
|
126
|
+
raise unless C_NOT_GATE.*(H_GATE * x, y) == State.new(Matrix[[0.7071067811865475], [0.0], [0.0], [0.7071067811865475]])
|
127
|
+
|
128
|
+
# Unitary inversion of gates
|
129
|
+
raise unless (C_NOT_GATE * H_GATE.kronecker(Matrix.identity(2))).adjoint == H_GATE.kronecker(Matrix.identity(2)) * C_NOT_GATE
|
130
|
+
|
131
|
+
# Auto scale gates
|
132
|
+
z = C_NOT_GATE.*(H_GATE * x, y)
|
133
|
+
raise unless H_GATE.*(z, scale: :down) == State.new(0.5 * Matrix[[1], [1], [1], [-1]])
|
134
|
+
|
135
|
+
# TOFFOLI_GATE test
|
136
|
+
raise unless TOFFOLI_GATE.*(Qubit.new(0, 1), Qubit.new(0, 1), Qubit.new(0, 1)) == State.new(Matrix.column_vector([0, 0, 0, 0, 0, 0, 1, 0]))
|
137
|
+
|
138
|
+
# Build a TOFFOLI_GATE from simple gates (C_NOT, H_GATE, and T_GATE's only)
|
139
|
+
T_3GATE_ADJOINT = I2.kronecker(I2).kronecker(T_GATE).adjoint
|
140
|
+
C_3NOT_GATE = Gate[*Matrix[[1, 0], [0, 0]].kronecker(I2.kronecker(I2)) + Matrix[[0, 0], [0, 1]].kronecker(I2.kronecker(X_GATE))]
|
141
|
+
raise unless TOFFOLI_GATE == C_NOT_GATE.*(T_GATE.kronecker(T_GATE.adjoint).*(C_NOT_GATE.kronecker(H_GATE).*(I2.kronecker(T_GATE).kronecker(T_GATE).*(C_3NOT_GATE.*(T_3GATE_ADJOINT.*(C_NOT_GATE.*(T_GATE.*(C_3NOT_GATE.*(T_3GATE_ADJOINT.*(C_NOT_GATE.*(I2.kronecker(I2).kronecker(H_GATE), scale: :up))), scale: :up), scale: :up))))), scale: :down), scale: :down).round
|
142
|
+
|
143
|
+
# Partial Measure
|
144
|
+
x = Qubit.new(0, 1)
|
145
|
+
y = Qubit.new(0, 1)
|
146
|
+
raise unless x == y
|
147
|
+
|
148
|
+
State.new(Matrix.column_vector([0, Math.sqrt(0.8), Math.sqrt(0.2), 0]), [x, y]).measure_partial(y)
|
149
|
+
raise if x == y
|
150
|
+
|
151
|
+
x = Qubit.new(0, 1)
|
152
|
+
y = Qubit.new(0, 1)
|
153
|
+
z = Qubit.new(0, 1)
|
154
|
+
State.new(Matrix.column_vector([0, 0.5, 0.5, 0, 0, 0.5, 0.5, 0]), [x, y, z]).measure_partial(y)
|
155
|
+
raise if (y == x) || (z == y)
|
156
|
+
|
157
|
+
1_000.times do
|
158
|
+
x = Qubit.new(0, 1)
|
159
|
+
y = Qubit.new(0, 1)
|
160
|
+
z = Qubit.new(0, 1)
|
161
|
+
a = Array.new(8, 0)
|
162
|
+
a[rand(8)] = 1
|
163
|
+
r = State.new(Matrix.column_vector(a), [x, y, z]).measure_partial(y, z).join.to_i(2)
|
164
|
+
case r
|
165
|
+
when 0
|
166
|
+
raise unless y == Qubit.new(1, 0) && z == Qubit.new(1, 0)
|
167
|
+
when 1
|
168
|
+
raise unless y == Qubit.new(1, 0) && z == Qubit.new(0, 1)
|
169
|
+
when 2
|
170
|
+
raise unless y == Qubit.new(0, 1) && z == Qubit.new(1, 0)
|
171
|
+
when 3
|
172
|
+
raise unless y == Qubit.new(0, 1) && z == Qubit.new(0, 1)
|
173
|
+
else
|
174
|
+
raise
|
175
|
+
end
|
176
|
+
|
177
|
+
i = Qubit.new(0, 1)
|
178
|
+
y = Qubit.new(0, 1)
|
179
|
+
z = Qubit.new(0, 1)
|
180
|
+
a = Array.new(8, 0)
|
181
|
+
a[rand(8)] = 1
|
182
|
+
r = State.new(Matrix.column_vector(a), [y, z, i]).measure_partial(i, y).join.to_i(2)
|
183
|
+
case r
|
184
|
+
when 0
|
185
|
+
raise unless y == Qubit.new(1, 0) && i == Qubit.new(1, 0)
|
186
|
+
when 1
|
187
|
+
raise unless y == Qubit.new(1, 0) && i == Qubit.new(0, 1)
|
188
|
+
when 2
|
189
|
+
raise unless y == Qubit.new(0, 1) && i == Qubit.new(1, 0)
|
190
|
+
when 3
|
191
|
+
raise unless y == Qubit.new(0, 1) && i == Qubit.new(0, 1)
|
192
|
+
else
|
193
|
+
raise
|
194
|
+
end
|
195
|
+
|
196
|
+
x = Qubit.new(0, 1)
|
197
|
+
y = Qubit.new(0, 1)
|
198
|
+
z = Qubit.new(0, 1)
|
199
|
+
i = Qubit.new(0, 1)
|
200
|
+
a = Array.new(16, 0)
|
201
|
+
a[rand(16)] = 1
|
202
|
+
r = State.new(Matrix.column_vector(a), [x, i, y, z]).measure_partial(x, y, z).join.to_i(2)
|
203
|
+
case r
|
204
|
+
when 0
|
205
|
+
raise unless x == Qubit.new(1, 0) && y == Qubit.new(1, 0) && z == Qubit.new(1, 0)
|
206
|
+
when 1
|
207
|
+
raise unless x == Qubit.new(1, 0) && y == Qubit.new(1, 0) && z == Qubit.new(0, 1)
|
208
|
+
when 2
|
209
|
+
raise unless x == Qubit.new(1, 0) && y == Qubit.new(0, 1) && z == Qubit.new(1, 0)
|
210
|
+
when 3
|
211
|
+
raise unless x == Qubit.new(1, 0) && y == Qubit.new(0, 1) && z == Qubit.new(0, 1)
|
212
|
+
when 4
|
213
|
+
raise unless x == Qubit.new(0, 1) && y == Qubit.new(1, 0) && z == Qubit.new(1, 0)
|
214
|
+
when 5
|
215
|
+
raise unless x == Qubit.new(0, 1) && y == Qubit.new(1, 0) && z == Qubit.new(0, 1)
|
216
|
+
when 6
|
217
|
+
raise unless x == Qubit.new(0, 1) && y == Qubit.new(0, 1) && z == Qubit.new(1, 0)
|
218
|
+
when 7
|
219
|
+
raise unless x == Qubit.new(0, 1) && y == Qubit.new(0, 1) && z == Qubit.new(0, 1)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
# # Quantum Teleportation
|
224
|
+
1_000.times do
|
225
|
+
# alice will teleport 'a' to bob
|
226
|
+
a = Qubit.new(0, 1)
|
227
|
+
b = Qubit.new(1, 0)
|
228
|
+
c = Qubit.new(1, 0)
|
229
|
+
# dup so we have copy of the original state to verify transportation.
|
230
|
+
# In practice neither Alice nor Bob knows these values and our final check
|
231
|
+
# cannot be preformed. Through the maths we can understand that this holds
|
232
|
+
# regardless of not knowing initial values.
|
233
|
+
a_dup = a.dup
|
234
|
+
|
235
|
+
# first entangle 'b' and 'c'
|
236
|
+
s = C_NOT_GATE.*(H_GATE * b, c)
|
237
|
+
# bob takes 'c' far away
|
238
|
+
|
239
|
+
# alice continues
|
240
|
+
g = H_GATE.kronecker(I2).kronecker(I2) * C_NOT_GATE.kronecker(I2)
|
241
|
+
s = g.*(a, s)
|
242
|
+
|
243
|
+
# alice measure her two qubits and sends classical bits to bob
|
244
|
+
z, x = s.measure_partial(a, b)
|
245
|
+
case [z, x].join.to_i(2)
|
246
|
+
when 0
|
247
|
+
raise unless a == Qubit.new(1, 0) && b == Qubit.new(1, 0)
|
248
|
+
when 1
|
249
|
+
raise unless a == Qubit.new(1, 0) && b == Qubit.new(0, 1)
|
250
|
+
when 2
|
251
|
+
raise unless a == Qubit.new(0, 1) && b == Qubit.new(1, 0)
|
252
|
+
when 3
|
253
|
+
raise unless a == Qubit.new(0, 1) && b == Qubit.new(0, 1)
|
254
|
+
else
|
255
|
+
raise
|
256
|
+
end
|
257
|
+
|
258
|
+
# depending on what bobs gets, he applies gates to his qubit
|
259
|
+
# and is able to regain alice's 'a' qubit's original state instantly!
|
260
|
+
c = X_GATE * c if x == 1
|
261
|
+
c = Z_GATE * c if z == 1
|
262
|
+
|
263
|
+
# p [z, x]
|
264
|
+
# p a_dup, c
|
265
|
+
raise unless a_dup == c
|
266
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'quantum_ruby'
|
2
|
+
# All the readme examples tested
|
3
|
+
|
4
|
+
# qubit_1 = Qubit.new(1, 0)
|
5
|
+
# qubit_1.measure # returns 0 bit
|
6
|
+
# qubit_2 = Qubit.new(0, 1)
|
7
|
+
# qubit_2.measure # returns 1 bit
|
8
|
+
qubit_1 = Qubit.new(1, 0)
|
9
|
+
raise unless qubit_1.measure.zero?
|
10
|
+
qubit_2 = Qubit.new(0, 1)
|
11
|
+
raise unless qubit_2.measure == 1
|
12
|
+
|
13
|
+
# # single qubit gate example (X_GATE ie. quantum NOT gate)
|
14
|
+
# qubit_1 = Qubit.new(0, 1) # equivalent to classical 1 bit
|
15
|
+
# state = X_GATE * qubit_1
|
16
|
+
# state.measure # returns 0 bit
|
17
|
+
# # multi qubit gate example (C_NOT_GATE ie. controlled NOT gate)
|
18
|
+
# control_qubit = Qubit.new(1, 0) # 0 bit
|
19
|
+
# target_qubit = Qubit.new(0, 1) # 1 bit
|
20
|
+
# # note multi params syntax: "GATE.*(param1, param2, etc.)"
|
21
|
+
# state = C_NOT_GATE.*(control_qubit, target_qubit)
|
22
|
+
# state.measure_partial(target_qubit) # return 1 bit since control is 0
|
23
|
+
# # again with control bit ON
|
24
|
+
# control_qubit = Qubit.new(0, 1) # 1 bit
|
25
|
+
# target_qubit = Qubit.new(0, 1) # 1 bit
|
26
|
+
# state = C_NOT_GATE.*(control_qubit, target_qubit)
|
27
|
+
# state.measure_partial(target_qubit) # return 0 bit since control is 1
|
28
|
+
# single qubit gate example (X_GATE ie. quantum NOT gate)
|
29
|
+
qubit_1 = Qubit.new(0, 1)
|
30
|
+
state = X_GATE * qubit_1
|
31
|
+
raise unless state.measure[0].zero?
|
32
|
+
control_qubit = Qubit.new(1, 0)
|
33
|
+
target_qubit = Qubit.new(0, 1)
|
34
|
+
state = C_NOT_GATE.*(control_qubit, target_qubit)
|
35
|
+
raise unless state.measure_partial(target_qubit)[0] == 1
|
36
|
+
# again with control bit ON
|
37
|
+
control_qubit = Qubit.new(0, 1) # 1 bit
|
38
|
+
target_qubit = Qubit.new(0, 1) # 1 bit
|
39
|
+
state = C_NOT_GATE.*(control_qubit, target_qubit)
|
40
|
+
raise unless state.measure_partial(target_qubit)[0].zero?
|
41
|
+
|
42
|
+
# x = Matrix[[0, 1], [1, 0]]
|
43
|
+
# y = Gate[[0, 1], [1, 0]]
|
44
|
+
x = Matrix[[0, 1], [1, 0]]
|
45
|
+
y = Gate[[0, 1], [1, 0]]
|
46
|
+
|
47
|
+
# Y_X_GATE = Y_GATE.kronecker(X_GATE)
|
48
|
+
Y_X_GATE = Y_GATE.kronecker(X_GATE)
|
49
|
+
|
50
|
+
# state = State.new(Matrix.column_vector([0, 0, 0, 1]))
|
51
|
+
# # H_GATE normally only works on 1 qubit ie. 2x1 matrix
|
52
|
+
# # the following will auto scale H_GATE with kronecker
|
53
|
+
# new_state = H_GATE.*(state, scale: :down)
|
54
|
+
state = State.new(Matrix.column_vector([0, 0, 0, 1]))
|
55
|
+
new_state = H_GATE.*(state, scale: :down)
|
56
|
+
|
57
|
+
# qubit_1 = Qubit.new(0, 1) # equivalent to classical 1 bit
|
58
|
+
# state = H_GATE * qubit_1
|
59
|
+
# state.measure # returns 0 bit or 1 bit with equal probability
|
60
|
+
qubit_1 = Qubit.new(0, 1)
|
61
|
+
state = H_GATE * qubit_1
|
62
|
+
raise unless state == State.new(Matrix.column_vector([1/Math.sqrt(2), -1/Math.sqrt(2)]))
|
data/lib/quantum_ruby.rb
ADDED
@@ -0,0 +1,298 @@
|
|
1
|
+
require "quantum_ruby/version"
|
2
|
+
require 'matrix'
|
3
|
+
|
4
|
+
# Overrides for base Ruby classes
|
5
|
+
class Matrix
|
6
|
+
def kronecker(m)
|
7
|
+
raise ErrOperationNotDefined, [__method__, self.class, m.class] unless m.is_a?(Matrix)
|
8
|
+
|
9
|
+
a = Array.new(row_count * m.row_count) { Array.new(column_count * m.column_count) }
|
10
|
+
|
11
|
+
count_ver = 0
|
12
|
+
row_count.times do |i|
|
13
|
+
count_hor = 0
|
14
|
+
column_count.times do |j|
|
15
|
+
m.row_count.times do |p|
|
16
|
+
m.column_count.times do |q|
|
17
|
+
a[i + p + count_ver][j + q + count_hor] = self[i, j] * m[p, q]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
count_hor += m.column_count - 1
|
21
|
+
end
|
22
|
+
count_ver += m.row_count - 1
|
23
|
+
end
|
24
|
+
self.class[*a]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Complex
|
29
|
+
def round(digits)
|
30
|
+
Complex(real.round(digits), imag.round(digits))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
# End of Overrides for base Ruby classes
|
34
|
+
|
35
|
+
ZERO_KET = '|0>'
|
36
|
+
ONE_KET = '|1>'
|
37
|
+
ADD_SYM = ' + '
|
38
|
+
ADD_SYM_SQUISH = '+'
|
39
|
+
I_SYM = 'i'
|
40
|
+
|
41
|
+
module ExceptionForQuantum
|
42
|
+
class NormalizationConstraint < StandardError
|
43
|
+
def initialize
|
44
|
+
super('Normalization constraint failed. Amplitudes must equal 1')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class ColumnMatrxiConstraint < StandardError
|
49
|
+
def initalize
|
50
|
+
super('Vector supplied must be a Matrix with a single column.')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
module QuantumComplexPrinter
|
56
|
+
refine Complex do
|
57
|
+
def to_s
|
58
|
+
out = ''
|
59
|
+
out << real.round(3).to_s if real.positive?
|
60
|
+
out << ADD_SYM_SQUISH if real.positive? && imag
|
61
|
+
out << imag.round(3).to_s << I_SYM if imag
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
module QuantumVector
|
67
|
+
include ExceptionForQuantum
|
68
|
+
using QuantumComplexPrinter
|
69
|
+
|
70
|
+
|
71
|
+
PRECISION = 14
|
72
|
+
|
73
|
+
def state
|
74
|
+
out = '['
|
75
|
+
out << @vector[0, 0].to_s
|
76
|
+
@vector.drop(1).each do |i|
|
77
|
+
out << "\n " << i.to_s
|
78
|
+
end
|
79
|
+
out << ']'
|
80
|
+
end
|
81
|
+
|
82
|
+
def ==(other)
|
83
|
+
@vector.map { |i| i.round(PRECISION) } == other.vector.map { |i| i.round(PRECISION) }
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def column_vector?
|
89
|
+
raise ColumnMatrxiConstraint unless @vector.is_a?(Matrix) && (@vector.column_count == 1)
|
90
|
+
end
|
91
|
+
|
92
|
+
def normalized?
|
93
|
+
raise NormalizationConstraint unless @vector.reduce(0) { |i, v| i + v.abs2 }.round(PRECISION) == 1
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class Qubit
|
98
|
+
attr_reader :vector
|
99
|
+
attr_accessor :entangled
|
100
|
+
include QuantumVector
|
101
|
+
using QuantumComplexPrinter
|
102
|
+
|
103
|
+
ENTANGLED_WARNING = "Alert: This qubit is entangled.\n\tPlease locate and measure the State\n\tthat contains this qubit's superposition."
|
104
|
+
|
105
|
+
def initialize(zero, one)
|
106
|
+
@vector = Matrix[[zero], [one]]
|
107
|
+
column_vector?
|
108
|
+
normalized?
|
109
|
+
end
|
110
|
+
|
111
|
+
def to_s
|
112
|
+
return if entangled?
|
113
|
+
|
114
|
+
first = el(0)
|
115
|
+
last = el(1)
|
116
|
+
|
117
|
+
out = ''
|
118
|
+
out << first.to_s << ZERO_KET unless first.zero?
|
119
|
+
out << ADD_SYM unless first.zero? || last.zero?
|
120
|
+
out << last.to_s << ONE_KET unless last.zero?
|
121
|
+
out
|
122
|
+
end
|
123
|
+
|
124
|
+
def state
|
125
|
+
return(puts(ENTANGLED_WARNING)) if entangled?
|
126
|
+
|
127
|
+
super
|
128
|
+
end
|
129
|
+
|
130
|
+
def measure
|
131
|
+
if rand < @vector[0, 0].abs2
|
132
|
+
@vector = [1, 0]
|
133
|
+
0
|
134
|
+
else
|
135
|
+
@vector = [0, 1]
|
136
|
+
1
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
def el(row)
|
143
|
+
out = @vector[row, 0]
|
144
|
+
out.is_a?(Float) || out.is_a?(Complex) ? out.round(PRECISION) : out
|
145
|
+
end
|
146
|
+
|
147
|
+
def vector=(array)
|
148
|
+
@vector = Matrix.column_vector(array)
|
149
|
+
normalized?
|
150
|
+
end
|
151
|
+
|
152
|
+
def entangled?
|
153
|
+
if entangled
|
154
|
+
puts ENTANGLED_WARNING
|
155
|
+
true
|
156
|
+
else
|
157
|
+
false
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
class State
|
163
|
+
attr_reader :vector
|
164
|
+
attr_reader :qubits
|
165
|
+
include QuantumVector
|
166
|
+
|
167
|
+
def initialize(vector, *qubits)
|
168
|
+
@vector = vector
|
169
|
+
column_vector?
|
170
|
+
normalized?
|
171
|
+
|
172
|
+
@qubits = qubits.flatten.tap { |i| i.each { |j| j.entangled = true } }
|
173
|
+
end
|
174
|
+
|
175
|
+
def measure
|
176
|
+
# "determine' 'winner'
|
177
|
+
acc = 0
|
178
|
+
out = nil
|
179
|
+
secret = rand
|
180
|
+
|
181
|
+
@vector.to_a.each_with_index do |probability, index|
|
182
|
+
acc += probability[0].abs2
|
183
|
+
if acc > secret
|
184
|
+
out = index
|
185
|
+
break
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# Reset state
|
190
|
+
@vector = Matrix.column_vector Array.new(@vector.row_count, 0)
|
191
|
+
# Update state
|
192
|
+
@vector.send(:[]=, out, 0, 1)
|
193
|
+
|
194
|
+
# Update each qubit
|
195
|
+
out = out.to_s(2).rjust(size, '0')
|
196
|
+
@qubits.each_with_index do |qubit, index|
|
197
|
+
qubit.entangled = false
|
198
|
+
qubit.send(:vector=, Array.new(2, 0).tap { |vector| vector[out[index].to_i] = 1 })
|
199
|
+
end
|
200
|
+
|
201
|
+
freeze
|
202
|
+
out.split('').map(&:to_i)
|
203
|
+
end
|
204
|
+
|
205
|
+
def measure_partial(*qubit)
|
206
|
+
# find location of our desired qubit(s)
|
207
|
+
qubit_ids = qubit.map { |i| @qubits.find_index { |j| j.hash == i .hash } }.sort
|
208
|
+
|
209
|
+
# collect probabilities for qubit(s) states
|
210
|
+
sub_result = @vector.to_a.flatten.each_with_index.group_by do |_probability, index|
|
211
|
+
qubit_ids.map do |id|
|
212
|
+
index.to_s(2).rjust(size, '0')[id]
|
213
|
+
end.join
|
214
|
+
end
|
215
|
+
|
216
|
+
# calculate final probabilities for qubit(s) state
|
217
|
+
probabilities = sub_result.sort.to_h.transform_values { |v| v.reduce(0) { |i, p| i + p[0].abs2 } }.values
|
218
|
+
acc = 0
|
219
|
+
out = nil
|
220
|
+
secret = rand
|
221
|
+
|
222
|
+
# "determine' 'winner'
|
223
|
+
probabilities.each_with_index do |probability, index|
|
224
|
+
acc += probability
|
225
|
+
if acc > secret
|
226
|
+
out = index
|
227
|
+
break
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
# Renormalize
|
232
|
+
squared_sum_mag = Math.sqrt(probabilities[out])
|
233
|
+
out = out.to_s(2).rjust(qubit.length, '0')
|
234
|
+
new_state = sub_result.fetch(out).map { |i| i[0] / squared_sum_mag }
|
235
|
+
|
236
|
+
# Update each qubit
|
237
|
+
@qubits.each_with_index do |q, i|
|
238
|
+
q.entangled = false
|
239
|
+
if index = qubit_ids.find_index(i)
|
240
|
+
q.send(:vector=, Array.new(2, 0).tap { |vector| vector[out[index].to_i] = 1 })
|
241
|
+
else
|
242
|
+
q.send(:vector=, new_state)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
freeze
|
246
|
+
out.split('').map(&:to_i)
|
247
|
+
end
|
248
|
+
|
249
|
+
private
|
250
|
+
|
251
|
+
def size
|
252
|
+
Math.log(@vector.row_count, 2)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
class Gate < Matrix
|
257
|
+
# can take gate, qubit, or state
|
258
|
+
def *(*args, scale: nil)
|
259
|
+
if scale
|
260
|
+
arg = begin
|
261
|
+
args[0].vector
|
262
|
+
rescue StandardError
|
263
|
+
args[0]
|
264
|
+
end
|
265
|
+
diff = (arg.row_count / row_count)
|
266
|
+
if diff > 1
|
267
|
+
return case scale
|
268
|
+
when :down
|
269
|
+
Gate[*kronecker(Matrix.identity(diff))].*(*args)
|
270
|
+
when :up
|
271
|
+
Gate[*Matrix.identity(diff).kronecker(self)].*(*args)
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
case args[0]
|
277
|
+
when State, Qubit
|
278
|
+
qubits = []
|
279
|
+
args = args.map do |i|
|
280
|
+
qubits << (i.is_a?(Qubit) ? i : i.qubits)
|
281
|
+
i.vector
|
282
|
+
end.reduce(:kronecker)
|
283
|
+
State.new(super(args), qubits)
|
284
|
+
else
|
285
|
+
super(*args)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
I2 = Gate.identity(2)
|
291
|
+
X_GATE = Gate[[0, 1], [1, 0]]
|
292
|
+
Y_GATE = Gate[[0, -1i], [1i, 0]]
|
293
|
+
Z_GATE = Gate[[1, 0], [0, -1]]
|
294
|
+
H_GATE = 1 / Math.sqrt(2) * Gate[[1, 1], [1, -1]]
|
295
|
+
T_GATE = Gate[[1, 0], [0, Math::E**((1i * Math::PI) / 4)]]
|
296
|
+
C_NOT_GATE = Gate[[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]]
|
297
|
+
SWAP_GATE = Gate[[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]
|
298
|
+
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]]
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative 'lib/quantum_ruby/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = "quantum_ruby"
|
5
|
+
spec.version = QuantumRuby::VERSION
|
6
|
+
spec.authors = ["Alessandro"]
|
7
|
+
spec.email = ["alessandro.minali@gmail.com"]
|
8
|
+
|
9
|
+
spec.summary = %q{A Quantum Computer Simulator written in Ruby.}
|
10
|
+
spec.description = %q{A Quantum Computer Simulator written in Ruby.}
|
11
|
+
spec.homepage = "https://github.com/AlessandroMinali/quantum_ruby"
|
12
|
+
spec.license = "MIT"
|
13
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
|
14
|
+
|
15
|
+
# Specify which files should be added to the gem when it is released.
|
16
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
17
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
18
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
19
|
+
end
|
20
|
+
spec.bindir = "exe"
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler"
|
25
|
+
spec.add_development_dependency "rake"
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: quantum_ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alessandro
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-05-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: A Quantum Computer Simulator written in Ruby.
|
42
|
+
email:
|
43
|
+
- alessandro.minali@gmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- ".gitignore"
|
49
|
+
- Gemfile
|
50
|
+
- Gemfile.lock
|
51
|
+
- LICENSE.txt
|
52
|
+
- README.md
|
53
|
+
- Rakefile
|
54
|
+
- bin/console
|
55
|
+
- bin/setup
|
56
|
+
- examples/general.rb
|
57
|
+
- examples/readme_examples.rb
|
58
|
+
- lib/quantum_ruby.rb
|
59
|
+
- lib/quantum_ruby/version.rb
|
60
|
+
- quantum_ruby.gemspec
|
61
|
+
homepage: https://github.com/AlessandroMinali/quantum_ruby
|
62
|
+
licenses:
|
63
|
+
- MIT
|
64
|
+
metadata: {}
|
65
|
+
post_install_message:
|
66
|
+
rdoc_options: []
|
67
|
+
require_paths:
|
68
|
+
- lib
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: 2.3.0
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
requirements: []
|
80
|
+
rubygems_version: 3.1.2
|
81
|
+
signing_key:
|
82
|
+
specification_version: 4
|
83
|
+
summary: A Quantum Computer Simulator written in Ruby.
|
84
|
+
test_files: []
|