eqc-bio 0.1.2__py3-none-any.whl

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.
@@ -0,0 +1,30 @@
1
+ # (C) Quantum Computing Inc., 2024.
2
+ from .fixed_backbone import ProteinDesignFixed
3
+ from .viewer import (
4
+ build_designed_pdb,
5
+ get_backbone_dihedrals,
6
+ get_rotamer_angles,
7
+ view_pdb,
8
+ )
9
+ from .rotamers import (
10
+ download_rotamer_library,
11
+ load_rotamer_library,
12
+ parse_dunbrack_library,
13
+ resolve_rotamer_library,
14
+ check_rotamer_counts,
15
+ get_chi_angles,
16
+ )
17
+
18
+ __all__ = [
19
+ "ProteinDesignFixed",
20
+ "build_designed_pdb",
21
+ "get_backbone_dihedrals",
22
+ "get_rotamer_angles",
23
+ "view_pdb",
24
+ "download_rotamer_library",
25
+ "load_rotamer_library",
26
+ "parse_dunbrack_library",
27
+ "resolve_rotamer_library",
28
+ "check_rotamer_counts",
29
+ "get_chi_angles",
30
+ ]
@@ -0,0 +1,168 @@
1
+ import os
2
+ import sys
3
+ import warnings
4
+ import logging
5
+ import numpy as np
6
+ import pandas as pd
7
+
8
+ from eqc_models.base import ModelSolver, SolutionResults
9
+ from eqc_models.solvers.qciclient import Dirac3CloudSolver
10
+ from eqc_models import QuadraticModel
11
+ from eqc_bio.utils import timer
12
+ from eqc_bio.utils import metis_partition
13
+
14
+ warnings.filterwarnings("ignore")
15
+ log = logging.getLogger(name=__name__)
16
+
17
+ # Seconds per unit for SolutionResults.time_units
18
+ TIME_UNIT_SECONDS = {"ns": 1e-9, "s": 1.0}
19
+
20
+
21
+ class ProteinDesign:
22
+ def __init__(self, solver: ModelSolver = None):
23
+ # Defaults to a cloud solver configured from QCI_API_URL/QCI_TOKEN
24
+ self.solver = solver
25
+
26
+ def get_dirac3_time(self, results: SolutionResults):
27
+ """Return the device time of a solve in seconds."""
28
+ device_time = results.device_time
29
+ if device_time is None:
30
+ log.warning("Device timing not available for this solve")
31
+ return 0.0
32
+
33
+ return np.sum(device_time) * TIME_UNIT_SECONDS[results.time_units]
34
+
35
+ def run_dirac3(self, J, C, sum_constraint, n_vars, num_samples):
36
+ model = QuadraticModel(C, J)
37
+ model.upper_bound = np.array([1] * n_vars)
38
+
39
+ solver = self.solver
40
+ if solver is None:
41
+ solver = Dirac3CloudSolver()
42
+
43
+ results = solver.solve(
44
+ model,
45
+ sum_constraint=sum_constraint,
46
+ relaxation_schedule=self.relaxation_schedule,
47
+ num_samples=num_samples,
48
+ mean_photon_number=self.mean_photon_number,
49
+ quantum_fluctuation_coefficient=self.quantum_fluctuation_coefficient,
50
+ )
51
+ log.info(results)
52
+
53
+ dirac3_time = self.get_dirac3_time(results)
54
+
55
+ return results, dirac3_time
56
+
57
+ def run_dirac3_iterative(
58
+ self,
59
+ J,
60
+ C,
61
+ sum_constraint,
62
+ n_vars,
63
+ positions,
64
+ position_graph,
65
+ position_index_hash,
66
+ ):
67
+ batch_count = int(round(n_vars / self.batch_size))
68
+
69
+ X = np.random.rand(n_vars)
70
+
71
+ if sum_constraint is not None:
72
+ X = X * (sum_constraint / np.sum(X))
73
+
74
+ tmp_group_list = metis_partition(position_graph, k=batch_count)
75
+ tmp_hash = dict(enumerate(positions))
76
+ position_groups = []
77
+ for tmp_group in tmp_group_list:
78
+ position_groups.append([tmp_hash[item] for item in tmp_group])
79
+
80
+ groups = []
81
+ for position_group in position_groups:
82
+ tmp_list = []
83
+ for position in position_group:
84
+ tmp_list += position_index_hash[position]
85
+ groups.append(tmp_list)
86
+
87
+ sols = []
88
+ energies = []
89
+ dirac3_time = 0.0
90
+ for epoch in range(self.n_epochs):
91
+ order = np.random.permutation(batch_count)
92
+ for i in order:
93
+ idx_p = groups[i]
94
+ idx_q = np.setdiff1d(np.arange(n_vars), idx_p)
95
+ Cp = C[idx_p]
96
+ Cq = C[idx_q]
97
+ Xq = X[idx_q]
98
+ Jpp = J[np.ix_(idx_p, idx_p)]
99
+ Jpq = J[np.ix_(idx_p, idx_q)]
100
+ Jqp = J[np.ix_(idx_q, idx_p)]
101
+ Jqq = J[np.ix_(idx_q, idx_q)]
102
+
103
+ tmp_v = 2 * Jpq @ Xq
104
+ tmp_v = tmp_v.reshape((tmp_v.shape[0], 1))
105
+ tmp_C = Cp + tmp_v
106
+ tmp_J = Jpp
107
+
108
+ if sum_constraint is not None:
109
+ tmp_sum_constraint = sum_constraint - np.sum(Xq)
110
+ else:
111
+ tmp_sum_constraint = None
112
+
113
+ sub_response, sub_dirac3_time = self.run_dirac3(
114
+ tmp_J,
115
+ tmp_C,
116
+ max(1.0, tmp_sum_constraint),
117
+ tmp_C.shape[0],
118
+ self.n_samples,
119
+ )
120
+ dirac3_time += sub_dirac3_time
121
+
122
+ sub_sols = sub_response.solutions
123
+
124
+ glob_loc_hash = {}
125
+ for loc_k, glob_k in enumerate(idx_p):
126
+ glob_loc_hash[glob_k] = loc_k
127
+
128
+ for sub_sol in sub_sols:
129
+ sub_sol = np.array(sub_sol)
130
+ feasible_flag = True
131
+ for position in position_groups[i]:
132
+ glob_indexes = position_index_hash[position]
133
+ loc_indexes = [
134
+ glob_loc_hash[item] for item in glob_indexes
135
+ ]
136
+ if np.sum(sub_sol[loc_indexes]) == 0:
137
+ feasible_flag = False
138
+ break
139
+
140
+ if feasible_flag:
141
+ break
142
+
143
+ Xp = sub_sol
144
+ fct = np.sum(Xp)
145
+ if fct != 0:
146
+ fct = tmp_sum_constraint / fct
147
+ Xp = fct * np.array(Xp)
148
+ X[idx_p] = Xp
149
+
150
+ sol = np.array(X)
151
+ energy = sol.T @ J @ sol + sol.T @ C
152
+ energy = energy[0]
153
+
154
+ sols.append(sol)
155
+ energies.append(energy)
156
+
157
+ # Device time is summed over the sub-solves and returned separately
158
+ results = SolutionResults(
159
+ solutions=np.array(sols),
160
+ energies=np.array(energies),
161
+ counts=np.ones(len(sols)),
162
+ objectives=None,
163
+ run_time=None,
164
+ preprocessing_time=None,
165
+ postprocessing_time=None,
166
+ )
167
+
168
+ return results, dirac3_time