Skip to content

Commit c9607db

Browse files
committed
Move normalization to the C++ layer
1 parent 3cf6434 commit c9607db

File tree

5 files changed

+39
-29
lines changed

5 files changed

+39
-29
lines changed

src/stim/stabilizers/tableau.pybind.cc

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2130,16 +2130,8 @@ void stim_pybind::pybind_tableau_methods(pybind11::module &m, pybind11::class_<T
21302130
}
21312131

21322132
std::vector<std::complex<float>> v;
2133-
double weight = 0;
21342133
for (const auto &obj : state_vector) {
21352134
v.push_back(pybind11::cast<std::complex<float>>(obj));
2136-
weight += std::norm(v.back());
2137-
}
2138-
if (weight != 1.0 && weight > 0.0) {
2139-
std::complex<float> scale = (float)sqrt(1.0 / weight);
2140-
for (auto &amplitude : v) {
2141-
amplitude *= scale;
2142-
}
21432135
}
21442136

21452137
return circuit_to_tableau<MAX_BITWORD_WIDTH>(
@@ -2155,7 +2147,7 @@ void stim_pybind::pybind_tableau_methods(pybind11::module &m, pybind11::class_<T
21552147
Args:
21562148
state_vector: A list of complex amplitudes specifying a superposition. The
21572149
vector must correspond to a state that is reachable using Clifford
2158-
operations.
2150+
operations, and can be unnormalized.
21592151
endian:
21602152
"little": state vector is in little endian order, where higher index
21612153
qubits correspond to larger changes in the state index.

src/stim/stabilizers/tableau_pybind_test.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# Copyright 2021 Google LLC
2+
#
13
# Licensed under the Apache License, Version 2.0 (the "License");
24
# you may not use this file except in compliance with the License.
35
# You may obtain a copy of the License at
@@ -54,6 +56,7 @@ def test_from_named_gate():
5456
with pytest.raises(IndexError, match="not unitary"):
5557
stim.Tableau.from_named_gate("X_ERROR")
5658

59+
5760
def test_from_state_vector():
5861
t = stim.Tableau.from_state_vector([
5962
0.5**0.5,
@@ -67,14 +70,6 @@ def test_from_state_vector():
6770
assert t.z_output(0) == stim.PauliString("XX")
6871
assert t.z_output(1) == stim.PauliString("ZZ")
6972

70-
unnormalized = stim.Tableau.from_state_vector([
71-
1,
72-
0,
73-
0,
74-
1
75-
], endian='little')
76-
assert unnormalized == t
77-
7873

7974
def test_identity():
8075
t = stim.Tableau(3)

src/stim/util_top/circuit_vs_amplitudes.cc

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,6 @@ Circuit stim::stabilizer_state_vector_to_circuit(
4040
}
4141

4242
uint8_t num_qubits = floor_lg2(state_vector.size());
43-
double weight = 0;
44-
for (const auto &c : state_vector) {
45-
weight += std::norm(c);
46-
}
47-
if (abs(weight - 1) > 0.125) {
48-
throw std::invalid_argument(
49-
"The given state vector wasn't a unit vector. It had a length of " + std::to_string(weight) + ".");
50-
}
51-
5243
VectorSimulator sim(num_qubits);
5344
sim.state = state_vector;
5445

@@ -73,7 +64,7 @@ Circuit stim::stabilizer_state_vector_to_circuit(
7364
{});
7465
};
7566

76-
// Move biggest amplitude to start of state vector..
67+
// Move biggest amplitude to start of state vector.
7768
size_t pivot = biggest_index(state_vector);
7869
for (size_t q = 0; q < num_qubits; q++) {
7970
if ((pivot >> q) & 1) {

src/stim/util_top/circuit_vs_amplitudes.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ namespace stim {
88
/// Synthesizes a circuit to generate the given state vector.
99
///
1010
/// Args:
11-
/// stabilizer_state_vector: The vector of amplitudes to produce using a circuit.
11+
/// stabilizer_state_vector: The vector of amplitudes to produce using a circuit. Does not need to be a unit vector,
12+
/// but must be non-zero.
1213
/// little_endian: Whether the vector is using little endian or big endian ordering.
1314
/// inverted_circuit: If false, returns a circuit that sends |000...0> to the state vector.
1415
/// If true, returns a circuit that sends the state vector to |000...0> instead of a cir.

src/stim/util_top/circuit_vs_amplitudes.test.cc

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88
using namespace stim;
99

1010
TEST(conversions, stabilizer_state_vector_to_circuit_basic) {
11+
ASSERT_THROW(stabilizer_state_vector_to_circuit({}, false), std::invalid_argument);
12+
1113
ASSERT_THROW(
1214
stabilizer_state_vector_to_circuit(
1315
{
14-
{0.5},
16+
{0},
1517
},
1618
false),
1719
std::invalid_argument);
@@ -175,6 +177,35 @@ TEST(conversions, stabilizer_state_vector_to_circuit_fuzz_round_trip) {
175177
}
176178
}
177179

180+
TEST(conversions, stabilizer_state_vector_to_circuit_unnormalized_fuzz_round_trip) {
181+
auto rng = INDEPENDENT_TEST_RNG();
182+
auto little_endian = true;
183+
184+
for (size_t i = 0; i < 100; i++) {
185+
// Pick a random stabilizer state.
186+
size_t n = i % 5;
187+
TableauSimulator<64> sim(INDEPENDENT_TEST_RNG(), n);
188+
sim.inv_state = Tableau<64>::random(n, rng);
189+
auto desired_vec = sim.to_state_vector(little_endian);
190+
191+
// Unnormalize by multiplying by a random non-zero factor.
192+
auto scaled_vec = desired_vec;
193+
std::uniform_real_distribution<float> dist(-1000.0, +1000.0);
194+
std::complex<float> scale = {dist(rng), dist(rng)};
195+
while (std::norm(scale) < 0.01) {
196+
scale = {dist(rng), dist(rng)};
197+
}
198+
for (auto &c : scaled_vec) {
199+
c *= scale;
200+
}
201+
202+
// Round trip through a circuit.
203+
auto circuit = stabilizer_state_vector_to_circuit(scaled_vec, little_endian);
204+
auto actual_vec = circuit_to_output_state_vector(circuit, little_endian);
205+
ASSERT_EQ(actual_vec, desired_vec) << " scale=" << scale;
206+
}
207+
}
208+
178209
TEST(conversions, circuit_to_output_state_vector) {
179210
ASSERT_EQ(circuit_to_output_state_vector(Circuit(""), false), (std::vector<std::complex<float>>{{1}}));
180211
ASSERT_EQ(

0 commit comments

Comments
 (0)