Skip to content
28 changes: 22 additions & 6 deletions cirq-core/cirq/circuits/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -1712,12 +1712,28 @@ def __init__(
together. This option does not affect later insertions into the
circuit.
"""
self._moments: List['cirq.Moment'] = []
with _compat.block_overlapping_deprecation('.*'):
if strategy == InsertStrategy.EARLIEST:
self._load_contents_with_earliest_strategy(contents)
else:
self.append(contents, strategy=strategy)
self._moments: List[Moment] = []
if len(contents) == 1 and isinstance(contents[0], AbstractCircuit):
self._moments.extend(contents[0].moments)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could produce two circuits with shared Moments.
Are Moment immutable? If so this should be OK...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, moments are immutable.

elif all(isinstance(item, Moment) for item in contents):
self._moments.extend(cast(Iterable[Moment], contents))
else:
with _compat.block_overlapping_deprecation('.*'):
if strategy == InsertStrategy.EARLIEST:
self._load_contents_with_earliest_strategy(contents)
else:
self.append(contents, strategy=strategy)

@classmethod
def from_moments(cls, *moments: 'cirq.OP_TREE') -> 'Circuit':
"""Create a circuit from moments.

Args:
*moments: Op tree for each moment.
"""
new_circuit = Circuit()
new_circuit._moments = list(Moment(moment) for moment in moments)
return new_circuit

def _load_contents_with_earliest_strategy(self, contents: 'cirq.OP_TREE'):
"""Optimized algorithm to load contents quickly.
Expand Down
17 changes: 17 additions & 0 deletions cirq-core/cirq/circuits/circuit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,23 @@ def validate_moment(self, moment):
moment_and_op_type_validating_device = _MomentAndOpTypeValidatingDeviceType()


def test_from_moments():
a, b, c, d = cirq.LineQubit.range(4)
assert cirq.Circuit.from_moments(
[cirq.X(a), cirq.Y(b)],
[cirq.X(c)],
[],
cirq.Z(d),
[cirq.measure(a, b, key='ab'), cirq.measure(c, d, key='cd')],
) == cirq.Circuit(
cirq.Moment(cirq.X(a), cirq.Y(b)),
cirq.Moment(cirq.X(c)),
cirq.Moment(),
cirq.Moment(cirq.Z(d)),
cirq.Moment(cirq.measure(a, b, key='ab'), cirq.measure(c, d, key='cd')),
)


def test_alignment():
assert repr(cirq.Alignment.LEFT) == 'cirq.Alignment.LEFT'
assert repr(cirq.Alignment.RIGHT) == 'cirq.Alignment.RIGHT'
Expand Down
23 changes: 20 additions & 3 deletions cirq-core/cirq/circuits/frozen_circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.
"""An immutable version of the Circuit data structure."""
from typing import TYPE_CHECKING, FrozenSet, Iterable, Iterator, Sequence, Tuple, Union
from typing import cast, FrozenSet, Iterable, Iterator, Sequence, Tuple, TYPE_CHECKING, Union

import numpy as np

from cirq import protocols, _compat
from cirq.circuits import AbstractCircuit, Alignment, Circuit
from cirq.circuits.insert_strategy import InsertStrategy
from cirq.circuits.moment import Moment
from cirq.type_workarounds import NotImplementedType

if TYPE_CHECKING:
Expand Down Expand Up @@ -48,8 +49,24 @@ def __init__(
from `contents`, this determines how the operations are packed
together.
"""
base = Circuit(contents, strategy=strategy)
self._moments = tuple(base.moments)
if len(contents) == 1 and isinstance(contents[0], AbstractCircuit):
moments = contents[0].moments
elif all(isinstance(item, Moment) for item in contents):
moments = cast(Sequence[Moment], contents)
else:
moments = Circuit(*contents, strategy=strategy).moments
self._moments: Tuple[Moment, ...] = tuple(moments)

@classmethod
def from_moments(cls, *moments: 'cirq.OP_TREE') -> 'FrozenCircuit':
"""Create a frozen circuit from moments.

Args:
*moments: Op tree for each moment.
"""
new_circuit = FrozenCircuit()
new_circuit._moments = tuple(Moment(moment) for moment in moments)
return new_circuit

@property
def moments(self) -> Sequence['cirq.Moment']:
Expand Down
17 changes: 17 additions & 0 deletions cirq-core/cirq/circuits/frozen_circuit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,23 @@
import cirq


def test_from_moments():
a, b, c, d = cirq.LineQubit.range(4)
assert cirq.FrozenCircuit.from_moments(
[cirq.X(a), cirq.Y(b)],
[cirq.X(c)],
[],
cirq.Z(d),
[cirq.measure(a, b, key='ab'), cirq.measure(c, d, key='cd')],
) == cirq.FrozenCircuit(
cirq.Moment(cirq.X(a), cirq.Y(b)),
cirq.Moment(cirq.X(c)),
cirq.Moment(),
cirq.Moment(cirq.Z(d)),
cirq.Moment(cirq.measure(a, b, key='ab'), cirq.measure(c, d, key='cd')),
)


def test_freeze_and_unfreeze():
a, b = cirq.LineQubit.range(2)
c = cirq.Circuit(cirq.X(a), cirq.H(b))
Expand Down