Skip to content

Commit 34b4168

Browse files
authored
Remove the constraint that stim.Tableau.from_state_vector inputs must be normalized (#840)
Just taking a stab at fixing #638. Note, I don't fully understand how Tableau should work but I wrote the tests based on the examples, so I think this should be correct. - Adds normalization code by scaling each amplitude by the square root of the inverse of the state vector norm - Adds tests to match the examples from `from_state_vector` including an unnormalized version
1 parent 49531a4 commit 34b4168

File tree

8 files changed

+48
-16
lines changed

8 files changed

+48
-16
lines changed

doc/python_api_reference_vDev.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11665,7 +11665,7 @@ def from_state_vector(
1166511665
Args:
1166611666
state_vector: A list of complex amplitudes specifying a superposition. The
1166711667
vector must correspond to a state that is reachable using Clifford
11668-
operations, and must be normalized (i.e. it must be a unit vector).
11668+
operations, and can be unnormalized.
1166911669
endian:
1167011670
"little": state vector is in little endian order, where higher index
1167111671
qubits correspond to larger changes in the state index.

doc/stim.pyi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9169,7 +9169,7 @@ class Tableau:
91699169
Args:
91709170
state_vector: A list of complex amplitudes specifying a superposition. The
91719171
vector must correspond to a state that is reachable using Clifford
9172-
operations, and must be normalized (i.e. it must be a unit vector).
9172+
operations, and can be unnormalized.
91739173
endian:
91749174
"little": state vector is in little endian order, where higher index
91759175
qubits correspond to larger changes in the state index.

glue/python/src/stim/__init__.pyi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9169,7 +9169,7 @@ class Tableau:
91699169
Args:
91709170
state_vector: A list of complex amplitudes specifying a superposition. The
91719171
vector must correspond to a state that is reachable using Clifford
9172-
operations, and must be normalized (i.e. it must be a unit vector).
9172+
operations, and can be unnormalized.
91739173
endian:
91749174
"little": state vector is in little endian order, where higher index
91759175
qubits correspond to larger changes in the state index.

src/stim/stabilizers/tableau.pybind.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2147,7 +2147,7 @@ void stim_pybind::pybind_tableau_methods(pybind11::module &m, pybind11::class_<T
21472147
Args:
21482148
state_vector: A list of complex amplitudes specifying a superposition. The
21492149
vector must correspond to a state that is reachable using Clifford
2150-
operations, and must be normalized (i.e. it must be a unit vector).
2150+
operations, and can be unnormalized.
21512151
endian:
21522152
"little": state vector is in little endian order, where higher index
21532153
qubits correspond to larger changes in the state index.

src/stim/stabilizers/tableau_pybind_test.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14+
import random
1415
import re
1516

1617
import numpy as np
@@ -57,6 +58,14 @@ def test_from_named_gate():
5758
stim.Tableau.from_named_gate("X_ERROR")
5859

5960

61+
def test_from_state_vector_fuzz():
62+
for n in range(1, 7):
63+
t = stim.Tableau.random(n)
64+
v = t.to_state_vector() * (random.random() + 1j*random.random())
65+
t2 = stim.Tableau.from_state_vector(v, endian='little')
66+
np.testing.assert_array_equal(t.to_stabilizers(canonicalize=True), t2.to_stabilizers(canonicalize=True))
67+
68+
6069
def test_identity():
6170
t = stim.Tableau(3)
6271
assert len(t) == 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)