Skip to content

Commit

Permalink
regen
Browse files Browse the repository at this point in the history
  • Loading branch information
Strilanc committed Feb 5, 2024
1 parent e7bb558 commit ede3d30
Show file tree
Hide file tree
Showing 7 changed files with 210 additions and 14 deletions.
70 changes: 70 additions & 0 deletions doc/python_api_reference_vDev.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ API references for stable versions are kept on the [stim github wiki](https://gi
- [`stim.Circuit.compile_sampler`](#stim.Circuit.compile_sampler)
- [`stim.Circuit.copy`](#stim.Circuit.copy)
- [`stim.Circuit.count_determined_measurements`](#stim.Circuit.count_determined_measurements)
- [`stim.Circuit.decomposed`](#stim.Circuit.decomposed)
- [`stim.Circuit.detector_error_model`](#stim.Circuit.detector_error_model)
- [`stim.Circuit.diagram`](#stim.Circuit.diagram)
- [`stim.Circuit.explain_detector_error_model_errors`](#stim.Circuit.explain_detector_error_model_errors)
Expand Down Expand Up @@ -1259,6 +1260,75 @@ def count_determined_measurements(
"""
```

<a name="stim.Circuit.decomposed"></a>
```python
# stim.Circuit.decomposed

# (in class stim.Circuit)
def decomposed(
self,
) -> stim.Circuit:
"""Recreates the circuit using (mostly) the {H,S,CX,M,R} gate set.
The intent of this method is to simplify the circuit to use fewer gate types,
so it's easier for other tools to consume. Currently, this method performs the
following simplifications:
- Single qubit cliffords are decomposed into {H,S}.
- Multi-qubit cliffords are decomposed into {H,S,CX}.
- Single qubit dissipative gates are decomposed into {H,S,M,R}.
- Multi-qubit dissipative gates are decomposed into {H,S,CX,M,R}.
Currently, the following types of gate *aren't* simplified, but they may be
in the future:
- Noise instructions (like X_ERROR, DEPOLARIZE2, and E).
- Annotations (like TICK, DETECTOR, and SHIFT_COORDS).
- The MPAD instruction.
- Repeat blocks are not flattened.
Returns:
A `stim.Circuit` whose function is equivalent to the original circuit,
but with most gates decomposed into the {H,S,CX,M,R} gate set.
Examples:
>>> import stim
>>> stim.Circuit('''
... SWAP 0 1
... ''').decomposed()
stim.Circuit('''
CX 0 1 1 0 0 1
''')
>>> stim.Circuit('''
... ISWAP 0 1 2 1
... TICK
... CPP X2*X1 !Z1*Z2
... ''').decomposed()
stim.Circuit('''
H 0
CX 0 1 1 0
H 1
S 1 0
H 2
CX 2 1 1 2
H 1
S 1 2
TICK
H 1 2
CX 2 1
H 2 2
CX 1 2
H 2
S 1 1
H 2
CX 2 1
H 1 2
''')
"""
```

<a name="stim.Circuit.detector_error_model"></a>
```python
# stim.Circuit.detector_error_model
Expand Down
62 changes: 62 additions & 0 deletions doc/stim.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,68 @@ class Circuit:
>>> circuit.num_detectors + circuit.num_observables
217
"""
def decomposed(
self,
) -> stim.Circuit:
"""Recreates the circuit using (mostly) the {H,S,CX,M,R} gate set.
The intent of this method is to simplify the circuit to use fewer gate types,
so it's easier for other tools to consume. Currently, this method performs the
following simplifications:
- Single qubit cliffords are decomposed into {H,S}.
- Multi-qubit cliffords are decomposed into {H,S,CX}.
- Single qubit dissipative gates are decomposed into {H,S,M,R}.
- Multi-qubit dissipative gates are decomposed into {H,S,CX,M,R}.
Currently, the following types of gate *aren't* simplified, but they may be
in the future:
- Noise instructions (like X_ERROR, DEPOLARIZE2, and E).
- Annotations (like TICK, DETECTOR, and SHIFT_COORDS).
- The MPAD instruction.
- Repeat blocks are not flattened.
Returns:
A `stim.Circuit` whose function is equivalent to the original circuit,
but with most gates decomposed into the {H,S,CX,M,R} gate set.
Examples:
>>> import stim
>>> stim.Circuit('''
... SWAP 0 1
... ''').decomposed()
stim.Circuit('''
CX 0 1 1 0 0 1
''')
>>> stim.Circuit('''
... ISWAP 0 1 2 1
... TICK
... CPP X2*X1 !Z1*Z2
... ''').decomposed()
stim.Circuit('''
H 0
CX 0 1 1 0
H 1
S 1 0
H 2
CX 2 1 1 2
H 1
S 1 2
TICK
H 1 2
CX 2 1
H 2 2
CX 1 2
H 2
S 1 1
H 2
CX 2 1
H 1 2
''')
"""
def detector_error_model(
self,
*,
Expand Down
62 changes: 62 additions & 0 deletions glue/python/src/stim/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,68 @@ class Circuit:
>>> circuit.num_detectors + circuit.num_observables
217
"""
def decomposed(
self,
) -> stim.Circuit:
"""Recreates the circuit using (mostly) the {H,S,CX,M,R} gate set.
The intent of this method is to simplify the circuit to use fewer gate types,
so it's easier for other tools to consume. Currently, this method performs the
following simplifications:
- Single qubit cliffords are decomposed into {H,S}.
- Multi-qubit cliffords are decomposed into {H,S,CX}.
- Single qubit dissipative gates are decomposed into {H,S,M,R}.
- Multi-qubit dissipative gates are decomposed into {H,S,CX,M,R}.
Currently, the following types of gate *aren't* simplified, but they may be
in the future:
- Noise instructions (like X_ERROR, DEPOLARIZE2, and E).
- Annotations (like TICK, DETECTOR, and SHIFT_COORDS).
- The MPAD instruction.
- Repeat blocks are not flattened.
Returns:
A `stim.Circuit` whose function is equivalent to the original circuit,
but with most gates decomposed into the {H,S,CX,M,R} gate set.
Examples:
>>> import stim
>>> stim.Circuit('''
... SWAP 0 1
... ''').decomposed()
stim.Circuit('''
CX 0 1 1 0 0 1
''')
>>> stim.Circuit('''
... ISWAP 0 1 2 1
... TICK
... CPP X2*X1 !Z1*Z2
... ''').decomposed()
stim.Circuit('''
H 0
CX 0 1 1 0
H 1
S 1 0
H 2
CX 2 1 1 2
H 1
S 1 2
TICK
H 1 2
CX 2 1
H 2 2
CX 1 2
H 2
S 1 1
H 2
CX 2 1
H 1 2
''')
"""
def detector_error_model(
self,
*,
Expand Down
4 changes: 1 addition & 3 deletions src/stim/circuit/circuit.pybind.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2223,9 +2223,7 @@ void stim_pybind::pybind_circuit_methods(pybind11::module &, pybind11::class_<Ci
... SWAP 0 1
... ''').decomposed()
stim.Circuit('''
CX 0 1
CX 1 0
CX 0 1
CX 0 1 1 0 0 1
''')
>>> stim.Circuit('''
Expand Down
17 changes: 9 additions & 8 deletions src/stim/circuit/gate_decomposition.cc
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,8 @@ struct Simplifier {
used[t.qubit_value()] = true;
}
}
simplify_disjoint_1q_instruction(CircuitInstruction{inst.gate_type, inst.args, inst.targets.sub(start, inst.targets.size())});
simplify_disjoint_1q_instruction(
CircuitInstruction{inst.gate_type, inst.args, inst.targets.sub(start, inst.targets.size())});
}

void simplify_potentially_overlapping_2q_instruction(const CircuitInstruction &inst) {
Expand All @@ -496,8 +497,7 @@ struct Simplifier {
for (size_t k = 0; k < inst.targets.size(); k += 2) {
auto a = inst.targets[k];
auto b = inst.targets[k + 1];
if ((a.has_qubit_value() && used[a.qubit_value()])
|| (b.has_qubit_value() && used[b.qubit_value()])) {
if ((a.has_qubit_value() && used[a.qubit_value()]) || (b.has_qubit_value() && used[b.qubit_value()])) {
CircuitInstruction disjoint = CircuitInstruction{inst.gate_type, inst.args, inst.targets.sub(start, k)};
simplify_disjoint_2q_instruction(disjoint);
used.clear();
Expand All @@ -510,7 +510,8 @@ struct Simplifier {
used[b.qubit_value()] = true;
}
}
simplify_disjoint_2q_instruction(CircuitInstruction{inst.gate_type, inst.args, inst.targets.sub(start, inst.targets.size())});
simplify_disjoint_2q_instruction(
CircuitInstruction{inst.gate_type, inst.args, inst.targets.sub(start, inst.targets.size())});
}

void simplify_disjoint_1q_instruction(const CircuitInstruction &inst) {
Expand Down Expand Up @@ -852,7 +853,7 @@ struct Simplifier {
const Gate &g = GATE_DATA[inst.gate_type];

switch (inst.gate_type) {
case GateType::MPP:
case GateType::MPP:
decompose_mpp_operation(inst, num_qubits, [&](const CircuitInstruction sub) {
simplify_instruction(sub);
});
Expand Down Expand Up @@ -903,7 +904,8 @@ struct Simplifier {
} else if (g.flags & GATE_TARGETS_PAIRS) {
simplify_potentially_overlapping_2q_instruction(inst);
} else {
throw std::invalid_argument("Unhandled in simplify_potentially_overlapping_instruction: " + inst.str());
throw std::invalid_argument(
"Unhandled in simplify_potentially_overlapping_instruction: " + inst.str());
}
}
}
Expand All @@ -918,8 +920,7 @@ Circuit stim::simplified_circuit(const Circuit &circuit) {
for (auto inst : circuit.operations) {
if (inst.gate_type == GateType::REPEAT) {
output.append_repeat_block(
inst.repeat_block_rep_count(),
simplified_circuit(inst.repeat_block_body(circuit)));
inst.repeat_block_rep_count(), simplified_circuit(inst.repeat_block_body(circuit)));
} else {
simplifier.simplify_instruction(inst);
}
Expand Down
6 changes: 4 additions & 2 deletions src/stim/circuit/gate_decomposition.test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,8 @@ TEST(gate_decomposition, decompose_cpp_operation_bad) {
std::invalid_argument);
}

static std::pair<std::vector<PauliString<64>>, std::vector<PauliString<64>>> circuit_output_eq_val(const Circuit &circuit) {
static std::pair<std::vector<PauliString<64>>, std::vector<PauliString<64>>> circuit_output_eq_val(
const Circuit &circuit) {
// CAUTION: this is not 100% reliable when measurement count is larger than 1.
TableauSimulator<64> sim1(INDEPENDENT_TEST_RNG(), circuit.count_qubits(), -1);
TableauSimulator<64> sim2(INDEPENDENT_TEST_RNG(), circuit.count_qubits(), +1);
Expand All @@ -491,7 +492,8 @@ static std::pair<std::vector<PauliString<64>>, std::vector<PauliString<64>>> cir

bool is_simplification_correct(const Gate &gate) {
std::vector<double> args;
while (args.size() < gate.arg_count && gate.arg_count != ARG_COUNT_SYGIL_ANY && gate.arg_count != ARG_COUNT_SYGIL_ZERO_OR_ONE) {
while (args.size() < gate.arg_count && gate.arg_count != ARG_COUNT_SYGIL_ANY &&
gate.arg_count != ARG_COUNT_SYGIL_ZERO_OR_ONE) {
args.push_back(args.empty() ? 1 : 0);
}

Expand Down
3 changes: 2 additions & 1 deletion src/stim/gates/gates.test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ TEST(gate_data, hash_matches_storage_location) {
}

template <size_t W>
static std::pair<std::vector<PauliString<W>>, std::vector<PauliString<W>>> circuit_output_eq_val(const Circuit &circuit) {
static std::pair<std::vector<PauliString<W>>, std::vector<PauliString<W>>> circuit_output_eq_val(
const Circuit &circuit) {
// CAUTION: this is not 100% reliable when measurement count is larger than 1.
TableauSimulator<W> sim1(INDEPENDENT_TEST_RNG(), circuit.count_qubits(), -1);
TableauSimulator<W> sim2(INDEPENDENT_TEST_RNG(), circuit.count_qubits(), +1);
Expand Down

0 comments on commit ede3d30

Please sign in to comment.