Skip to content
2 changes: 2 additions & 0 deletions quantumcat/applications/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from quantumcat.applications.protein_folding import ProteinFolding
37 changes: 37 additions & 0 deletions quantumcat/applications/protein_folding/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
## Protein Structure Prediction
This project contains a specific application of the protein folding algorithm mentioned in the paper "Quantum Speedup for Protein Structure Prediction" by Renata Wong and Weng-Long Chang. The paper can be found [here](https://pubmed.ncbi.nlm.nih.gov/33690123/).

In this specific code of the algorithm represented in the paper, a sequence |a> = |11> was assumed implicitly, while the arbitrary chose conformation was |w> = |110>

This project consists of the following files:
* **proteinfolding.py** - contains the subroutines for the protein folding algorithm
- subroutine 1 - Calculating three-dimensional cartesian coordinates |x>, |y> and |z> for each conformation
- subroutine 2 - Calculating the energy values of each conformation
- subroutine 3 - Uncomputation of the coordinates of all conformations by running subroutine 1 in reverse
- subroutine 4 - Applying Grover's Algorithm
* **ccrca.py** - contains the code for controlled-controlled ripple carry adder, required for implementation of the algorithm. This is a slightly modified version of what is suggested in "A new quantum ripple-carry addition circuit" by Cuccaro et al [[Link to the paper](https://arxiv.org/abs/quant-ph/0410184)]. The controls involve just additional qubits that have been incorporated into the adder. The carry bit in the code (z in Fig. 6 of the paper) is skipped as it was not needed for the application. The general idea is that, for 2 control qubits, we have to use a Toffoli gate with these 2 control qubits and an ancilla qubit as the target. Then, each operation in the adder given in Cuccaro has to be made dependent on this ancilla qubit. Please compare figure 6 in Cuccaro with the modified circuit.
* **ccrca_inverse.py** - contain the inversed version of the controlled-controlled ripple carry adder.

## Executing the algorithm
The application can be executed in the following manner:-

- Importing the application
```python
from quantumcat.applications import ProteinFolding
```

- Running the application
```python
job = ProteinFolding(11) # 11 is currently dummy, placed parameter for future improvements on code
result = job.run(providers.GOOGLE_PROVIDER) # IBM_PROVIDER for executing the file using IBM
print(result)
```
- Understanding the results
```
{'111': 12, '011': 934, '010': 18, '000': 17, '100': 10, '110': 15, '001': 7, '101': 11}
```

The above dictionary is the result after one execution of the code. It shows the number of times each state is counted (to be read from right to left). Clearly, the most number of counts is with the state 110 (our expected state) which is 934.

## Further improvements (suggested)
The code currently is hard-coded for only one particular output state. Also, due to the limitation of the number of qubits, the length of the input sequence is taken to be 2. However, real world protein sequences are very much longer than that. The 11 in the ```ProteinFolding(11)``` function is a dummy, intended to be replaced with the actual user input sequence once we have a more generalised version of the code. The output is also currently the 110 state, and can be further generalised to figure out the conformation with the lowest energy among all the conformational states.
18 changes: 18 additions & 0 deletions quantumcat/applications/protein_folding/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# (C) Copyright Artificial Brain 2021.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


from quantumcat.applications.protein_folding.ccrca import CCRCA
from quantumcat.applications.protein_folding.ccrca_inverse import CCRCA_INVERSE
from quantumcat.applications.protein_folding.proteinfolding import ProteinFolding
94 changes: 94 additions & 0 deletions quantumcat/applications/protein_folding/ccrca.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# (C) Copyright Artificial Brain 2021.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from quantumcat.circuit import QCircuit
from quantumcat.utils import providers


class CCRCA:
def __init__(self, circuit, arglist):
super(CCRCA, self).__init__()
# subcircuit defined for the controlled-controlled ripple-carry adder for 3 bits
# (the sum is stored by overwriting the values of x)
self.arglist = arglist
self.sc = circuit
self.sw = [self.arglist[0]] # control qubits
self.sa = [self.arglist[1], self.arglist[2], self.arglist[3]] # ancilla qubits
self.ss = [self.arglist[4]] # carry
self.sx = [self.arglist[5], self.arglist[6], self.arglist[7]] # summand x
self.sy = [self.arglist[8], self.arglist[9], self.arglist[10]] # summand y


self.sc.cx_gate(self.sw[0],self.sa[0])

self.sc.ccx_gate(self.sa[0],self.sy[2],self.sx[2])
self.sc.ccx_gate(self.sa[0],self.sy[2],self.ss[0])
self.sc.ccx_gate(self.sa[0],self.ss[0],self.sa[1])
self.sc.ccx_gate(self.sa[1],self.sx[2],self.sa[2])
self.sc.ccx_gate(self.sa[1],self.sa[2],self.sy[2])
# uncompute
self.sc.ccx_gate(self.sa[1],self.sx[2],self.sa[2])
self.sc.ccx_gate(self.sa[0],self.ss[0],self.sa[1])

self.sc.ccx_gate(self.sa[0],self.sy[1],self.sx[1])
self.sc.ccx_gate(self.sa[0],self.sy[1],self.sy[2])
self.sc.ccx_gate(self.sa[0],self.sy[2],self.sa[1])
self.sc.ccx_gate(self.sa[1],self.sx[1],self.sa[2])
self.sc.ccx_gate(self.sa[1],self.sa[2],self.sy[1])
# uncompute
self.sc.ccx_gate(self.sa[1],self.sx[1],self.sa[2])
self.sc.ccx_gate(self.sa[0],self.sy[2],self.sa[1])

self.sc.ccx_gate(self.sa[0],self.sy[0],self.sx[0])
self.sc.ccx_gate(self.sa[0],self.sy[0],self.sy[1])
self.sc.ccx_gate(self.sa[0],self.sy[1],self.sa[1])
self.sc.ccx_gate(self.sa[1],self.sx[0],self.sa[2])
self.sc.ccx_gate(self.sa[1],self.sa[2],self.sy[0])
# uncompute
self.sc.ccx_gate(self.sa[1],self.sx[0],self.sa[2])
self.sc.ccx_gate(self.sa[0],self.sy[1],self.sa[1])

self.sc.ccx_gate(self.sa[0],self.sy[1],self.sa[1])
self.sc.ccx_gate(self.sa[1],self.sx[0],self.sa[2])
self.sc.ccx_gate(self.sa[1],self.sa[2],self.sy[0])
# uncompute
self.sc.ccx_gate(self.sa[1],self.sx[0],self.sa[2])
self.sc.ccx_gate(self.sa[0],self.sy[1],self.sa[1])
# continue
self.sc.ccx_gate(self.sa[0],self.sy[0],self.sy[1])
self.sc.ccx_gate(self.sa[0],self.sy[1],self.sx[0])

self.sc.ccx_gate(self.sa[0],self.sy[2],self.sa[1])
self.sc.ccx_gate(self.sa[1],self.sx[1],self.sa[2])
self.sc.ccx_gate(self.sa[1],self.sa[2],self.sy[1])
# uncompute
self.sc.ccx_gate(self.sa[1],self.sx[1],self.sa[2])
self.sc.ccx_gate(self.sa[0],self.sy[2],self.sa[1])
# continue
self.sc.ccx_gate(self.sa[0],self.sy[1],self.sy[2])
self.sc.ccx_gate(self.sa[0],self.sy[2],self.sx[1])

self.sc.ccx_gate(self.sa[0],self.ss[0],self.sa[1])
self.sc.ccx_gate(self.sa[1],self.sx[2],self.sa[2])
self.sc.ccx_gate(self.sa[1],self.sa[2],self.sy[2])
# uncompute
self.sc.ccx_gate(self.sa[1],self.sx[2],self.sa[2])
self.sc.ccx_gate(self.sa[0],self.ss[0],self.sa[1])
# continue
self.sc.ccx_gate(self.sa[0],self.sy[2],self.ss[0])
self.sc.ccx_gate(self.sa[0],self.ss[0],self.sx[2])

self.sc.cx_gate(self.sw[0],self.sa[0])

return
93 changes: 93 additions & 0 deletions quantumcat/applications/protein_folding/ccrca_inverse.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# (C) Copyright Artificial Brain 2021.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from quantumcat.circuit import QCircuit
from quantumcat.utils import providers


class CCRCA_INVERSE:
def __init__(self, circuit, arglist):
super(CCRCA_INVERSE, self).__init__()
# subcircuit defined for the controlled-controlled ripple-carry adder for 3 bits
# (the sum is stored by overwriting the values of x)
self.arglist = arglist
self.sc = circuit
self.sw = [self.arglist[0]] # control qubits
self.sa = [self.arglist[1], self.arglist[2], self.arglist[3]] # ancilla qubits
self.ss = [self.arglist[4]] # carry
self.sx = [self.arglist[5], self.arglist[6], self.arglist[7]] # summand x
self.sy = [self.arglist[8], self.arglist[9], self.arglist[10]] # summand y

self.sc.cx_gate(self.sw[0],self.sa[0])

self.sc.ccx_gate(self.sa[0],self.ss[0],self.sx[2])
self.sc.ccx_gate(self.sa[0],self.sy[2],self.ss[0])
# continue
self.sc.ccx_gate(self.sa[0],self.ss[0],self.sa[1])
self.sc.ccx_gate(self.sa[1],self.sx[2],self.sa[2])
# uncompute
self.sc.ccx_gate(self.sa[1],self.sa[2],self.sy[2])
self.sc.ccx_gate(self.sa[1],self.sx[2],self.sa[2])
self.sc.ccx_gate(self.sa[0],self.ss[0],self.sa[1])

self.sc.ccx_gate(self.sa[0],self.sy[2],self.sx[1])
self.sc.ccx_gate(self.sa[0],self.sy[1],self.sy[2])
# continue
self.sc.ccx_gate(self.sa[0],self.sy[2],self.sa[1])
self.sc.ccx_gate(self.sa[1],self.sx[1],self.sa[2])
# uncompute
self.sc.ccx_gate(self.sa[1],self.sa[2],self.sy[1])
self.sc.ccx_gate(self.sa[1],self.sx[1],self.sa[2])
self.sc.ccx_gate(self.sa[0],self.sy[2],self.sa[1])

self.sc.ccx_gate(self.sa[0],self.sy[1],self.sx[0])
self.sc.ccx_gate(self.sa[0],self.sy[0],self.sy[1])
# continue
self.sc.ccx_gate(self.sa[0],self.sy[1],self.sa[1])
self.sc.ccx_gate(self.sa[1],self.sx[0],self.sa[2])
# uncompute
self.sc.ccx_gate(self.sa[1],self.sa[2],self.sy[0])
self.sc.ccx_gate(self.sa[1],self.sx[0],self.sa[2])
self.sc.ccx_gate(self.sa[0],self.sy[1],self.sa[1])

self.sc.ccx_gate(self.sa[0],self.sy[1],self.sa[1])
self.sc.ccx_gate(self.sa[1],self.sx[0],self.sa[2])
# uncompute
self.sc.ccx_gate(self.sa[1],self.sa[2],self.sy[0])
self.sc.ccx_gate(self.sa[1],self.sx[0],self.sa[2])
self.sc.ccx_gate(self.sa[0],self.sy[1],self.sa[1])
self.sc.ccx_gate(self.sa[0],self.sy[0],self.sy[1])
self.sc.ccx_gate(self.sa[0],self.sy[0],self.sx[0])

self.sc.ccx_gate(self.sa[0],self.sy[2],self.sa[1])
self.sc.ccx_gate(self.sa[1],self.sx[1],self.sa[2])
# uncompute
self.sc.ccx_gate(self.sa[1],self.sa[2],self.sy[1])
self.sc.ccx_gate(self.sa[1],self.sx[1],self.sa[2])
self.sc.ccx_gate(self.sa[0],self.sy[2],self.sa[1])
self.sc.ccx_gate(self.sa[0],self.sy[1],self.sy[2])
self.sc.ccx_gate(self.sa[0],self.sy[1],self.sx[1])

self.sc.ccx_gate(self.sa[0],self.ss[0],self.sa[1])
self.sc.ccx_gate(self.sa[1],self.sx[2],self.sa[2])
# uncompute
self.sc.ccx_gate(self.sa[1],self.sa[2],self.sy[2])
self.sc.ccx_gate(self.sa[1],self.sx[2],self.sa[2])
self.sc.ccx_gate(self.sa[0],self.ss[0],self.sa[1])
self.sc.ccx_gate(self.sa[0],self.sy[2],self.ss[0])
self.sc.ccx_gate(self.sa[0],self.sy[2],self.sx[2])

self.sc.cx_gate(self.sw[0],self.sa[0])

return
Loading