Skip to content

Commit ec63e58

Browse files
Magnetization functions (v1.1.0)
1 parent e69ef11 commit ec63e58

File tree

7 files changed

+75
-32
lines changed

7 files changed

+75
-32
lines changed

PyQrackIsing/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
from .generate_tfim_samples import generate_tfim_samples
2+
from .tfim_magnetization import tfim_magnetization
3+
from .tfim_square_magnetization import tfim_square_magnetization

PyQrackIsing/_pyqrack_ising.cpp

Lines changed: 59 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ static inline double closeness_like_bits(BigInteger perm, size_t n_rows, size_t
4242
}
4343

4444
static inline double expected_closeness_weight(size_t n_rows, size_t n_cols, size_t hamming_weight) {
45-
size_t L = n_rows * n_cols;
45+
const size_t L = n_rows * n_cols;
4646
auto comb = [](size_t n, size_t k) {
4747
if ((k < 0) || (k > n)) {
4848
return 0ULL;
@@ -56,46 +56,30 @@ static inline double expected_closeness_weight(size_t n_rows, size_t n_cols, siz
5656
}
5757
return res;
5858
};
59-
double same_pairs = comb(hamming_weight, 2U) + comb(L - hamming_weight, 2U);
60-
double total_pairs = comb(L, 2U);
61-
double mu_k = same_pairs / total_pairs;
59+
const double same_pairs = comb(hamming_weight, 2U) + comb(L - hamming_weight, 2U);
60+
const double total_pairs = comb(L, 2U);
61+
const double mu_k = same_pairs / total_pairs;
62+
6263
return 2.0 * mu_k - 1.0;
6364
}
6465

65-
std::vector<std::string> generate_tfim_samples_cpp(double J, double h, double theta, double t, size_t n_qubits, size_t shots) {
66-
// lattice dimensions
67-
size_t n_rows = 1U;
68-
size_t n_cols = n_qubits;
69-
for (size_t c = std::floor(std::sqrt(n_qubits)); c >= 1; --c) {
70-
if ((n_qubits % c) == 0U) {
71-
n_rows = n_qubits / c;
72-
n_cols = c;
73-
break;
74-
}
75-
}
76-
66+
static inline std::vector<double> probability_by_hamming_weight(double J, double h, double theta, double t, size_t n_qubits)
67+
{
7768
// critical angle
78-
size_t z = 4U;
79-
double theta_c = std::asin(h / (z * J));
80-
double delta_theta = theta - theta_c;
81-
82-
// compute bias vector
69+
const size_t z = 4U;
70+
const double theta_c = std::asin(h / (z * J));
71+
const double delta_theta = theta - theta_c;
8372
std::vector<double> bias;
8473
if (std::abs(h) < 1e-12) {
8574
bias.assign(n_qubits + 1, 0.0);
8675
bias[0] = 1.0;
8776
} else if (std::abs(J) < 1e-12) {
8877
bias.assign(n_qubits + 1, 1.0 / (n_qubits + 1));
8978
} else {
90-
double sin_delta = std::sin(delta_theta);
91-
double omega = 1.5 * M_PI;
92-
double t2 = 1.0;
93-
double p = ((std::pow(2.0, std::abs(J / h) - 1.0)) *
94-
(1.0 + sin_delta * std::cos(J * omega * t + theta) /
95-
((1.0 + std::sqrt(t / t2))))) - 0.5;
96-
if (t2 <= 0) {
97-
p = std::pow(2.0, std::abs(J / h));
98-
}
79+
const double sin_delta = std::sin(delta_theta);
80+
const double omega = 1.5 * M_PI;
81+
const double t2 = 1.0;
82+
const double p = std::pow(2.0, std::abs(J / h) - 1.0) * (1.0 + sin_delta * std::cos(J * omega * t + theta) / (1.0 + std::sqrt(t / t2))) - 0.5;
9983
if (p >= 1024) {
10084
bias.assign(n_qubits + 1U, 0.0);
10185
bias[0] = 1.0;
@@ -116,6 +100,23 @@ std::vector<std::string> generate_tfim_samples_cpp(double J, double h, double th
116100
std::reverse(bias.begin(), bias.end());
117101
}
118102

103+
return bias;
104+
}
105+
106+
std::vector<std::string> generate_tfim_samples_cpp(double J, double h, double theta, double t, size_t n_qubits, size_t shots) {
107+
// lattice dimensions
108+
size_t n_rows = 1U;
109+
size_t n_cols = n_qubits;
110+
for (size_t c = std::floor(std::sqrt(n_qubits)); c >= 1; --c) {
111+
if ((n_qubits % c) == 0U) {
112+
n_rows = n_qubits / c;
113+
n_cols = c;
114+
break;
115+
}
116+
}
117+
118+
const std::vector<double> bias = probability_by_hamming_weight(J, h, theta, t, n_qubits);
119+
119120
// thresholds
120121
std::vector<double> thresholds(n_qubits + 1);
121122
double tot_prob = 0.0;
@@ -188,10 +189,38 @@ std::vector<std::string> generate_tfim_samples_cpp(double J, double h, double th
188189
return output;
189190
}
190191

192+
double tfim_magnetization(double J, double h, double theta, double t, size_t n_qubits) {
193+
const std::vector<double> bias = probability_by_hamming_weight(J, h, theta, t, n_qubits);
194+
double magnetization = 0.0;
195+
for (size_t q = 0U; q < bias.size(); ++q) {
196+
const double mag = (n_qubits - 2.0 * q) / n_qubits;
197+
magnetization += bias[q] * mag;
198+
}
199+
200+
return magnetization;
201+
}
202+
203+
double tfim_square_magnetization(double J, double h, double theta, double t, size_t n_qubits) {
204+
const std::vector<double> bias = probability_by_hamming_weight(J, h, theta, t, n_qubits);
205+
double square_magnetization = 0.0;
206+
for (size_t q = 0U; q < bias.size(); ++q) {
207+
const double mag = (n_qubits - 2.0 * q) / n_qubits;
208+
square_magnetization += bias[q] * mag * mag;
209+
}
210+
211+
return square_magnetization;
212+
}
213+
191214
PYBIND11_MODULE(tfim_sampler, m) {
192215
m.doc() = "PyQrackIsing TFIM sample generator";
193216
m.def("_generate_tfim_samples", &generate_tfim_samples_cpp,
194217
py::arg("J"), py::arg("h"), py::arg("theta"), py::arg("t"),
195218
py::arg("n_qubits"), py::arg("shots"));
219+
m.def("_tfim_magnetization", &tfim_magnetization,
220+
py::arg("J"), py::arg("h"), py::arg("theta"), py::arg("t"),
221+
py::arg("n_qubits"));
222+
m.def("_tfim_square_magnetization", &tfim_square_magnetization,
223+
py::arg("J"), py::arg("h"), py::arg("theta"), py::arg("t"),
224+
py::arg("n_qubits"));
196225
}
197226

PyQrackIsing/tfim_magnetization.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import tfim_sampler
2+
3+
4+
def tfim_magnetization(J=-1.0, h=2.0, theta=0.174532925199432957, t=5, n_qubits=56):
5+
return tfim_sampler._tfim_magnetization(J, h, theta, t, n_qubits)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import tfim_sampler
2+
3+
4+
def tfim_square_magnetization(J=-1.0, h=2.0, theta=0.174532925199432957, t=5, n_qubits=56):
5+
return tfim_sampler._tfim_square_magnetization(J, h, theta, t, n_qubits)

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ samples = generate_tfim_samples(
3737
)
3838
```
3939

40+
There are two more functions, `tfim_magnetization()` and `tfim_square_magnetization()`, that follow the same function signature except without the `shots` argument.
41+
4042
## About
4143
Transverse field Ising model (TFIM) is the basis of most claimed algorithmic "quantum advantage," circa 2025, with the notable exception of Shor's integer factoring algorithm.
4244

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta"
88

99
[project]
1010
name = "PyQrackIsing"
11-
version = "1.0.1"
11+
version = "1.1.0"
1212
requires-python = ">=3.8"
1313
description = "Near-ideal closed-form solutions for transverse field Ising model (TFIM)"
1414
readme = {file = "README.txt", content-type = "text/markdown"}

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def build_extension(self, ext):
4040

4141
setup(
4242
name='PyQrackIsing',
43-
version='1.0.1',
43+
version='1.1.0',
4444
author='Dan Strano',
4545
author_email='stranoj@gmail.com',
4646
description='Near-ideal closed-form solutions for transverse field Ising model (TFIM)',

0 commit comments

Comments
 (0)