Repository: quantumlib/Cirq

Issue:
Class methods on GridQubit to create common lattices
Some obvious shapes
```GridQubit.square(3, 3)```
```GirdQubit.rect(2, 4)```
returning list of list with row and column.   Default to 0,0 as one corner, but option to set corner.

Ability to construction from an ASCII diagram like xmon devices
```GridQubit.from_pic```
which operations on maps like
```
...A...
..AAA..
.AAAAA.
AAAAAAAA
````



Discussion:

Resolving patch:
diff --git a/cirq/devices/grid_qubit.py b/cirq/devices/grid_qubit.py
--- a/cirq/devices/grid_qubit.py
+++ b/cirq/devices/grid_qubit.py
@@ -13,7 +13,7 @@
 # limitations under the License.
 
 
-from typing import Dict
+from typing import Dict, List
 
 from cirq import ops
 
@@ -38,6 +38,93 @@ def is_adjacent(self, other: ops.Qid) -> bool:
         return (isinstance(other, GridQubit) and
                 abs(self.row - other.row) + abs(self.col - other.col) == 1)
 
+    @staticmethod
+    def square(diameter: int, top: int = 0, left: int = 0) -> List['GridQubit']:
+        """Returns a square of GridQubits.
+
+        Args:
+            diameter: Length of a side of the square
+            top: Row number of the topmost row
+            left: Column number of the leftmost row
+
+        Returns:
+            A list of GridQubits filling in a square grid
+        """
+        return GridQubit.rect(diameter, diameter, top=top, left=left)
+
+    @staticmethod
+    def rect(rows: int, cols: int, top: int = 0,
+             left: int = 0) -> List['GridQubit']:
+        """Returns a rectangle of GridQubits.
+
+        Args:
+            rows: Number of rows in the rectangle
+            cols: Number of columns in the rectangle
+            top: Row number of the topmost row
+            left: Column number of the leftmost row
+
+        Returns:
+            A list of GridQubits filling in a rectangular grid
+        """
+        return [
+            GridQubit(row, col)
+            for row in range(top, top + rows)
+            for col in range(left, left + cols)
+        ]
+
+    @staticmethod
+    def from_diagram(diagram: str) -> List['GridQubit']:
+        """Parse ASCII art device layout into info about qubits and
+        connectivity. As an example, the below diagram will create a list of
+        GridQubits in a pyramid structure.
+        ---A---
+        --AAA--
+        -AAAAA-
+        AAAAAAA
+
+        You can use any character other than a hyphen to mark a qubit. As an
+        example, the qubits for the Bristlecone device could be represented by
+        the below diagram. This produces a diamond-shaped grid of qubits, and
+        qubits with the same letter correspond to the same readout line.
+
+        .....AB.....
+        ....ABCD....
+        ...ABCDEF...
+        ..ABCDEFGH..
+        .ABCDEFGHIJ.
+        ABCDEFGHIJKL
+        .CDEFGHIJKL.
+        ..EFGHIJKL..
+        ...GHIJKL...
+        ....IJKL....
+        .....KL.....
+
+        Args:
+            diagram: String representing the qubit layout. Each line represents
+                a row. Alphanumeric characters are assigned as qubits.
+                Dots ('.'), dashes ('-'), and spaces (' ') are treated as
+                empty locations in the grid. If diagram has characters other
+                than alphanumerics, spacers, and newlines ('\n'), an error will
+                be thrown. The top-left corner of the diagram will be have
+                coordinate (0,0).
+
+        Returns:
+            A list of GridQubits corresponding to the provided diagram
+
+        Raises:
+            ValueError: If the input string contains an invalid character.
+        """
+        lines = diagram.strip().split('\n')
+        no_qubit_characters = ['.', '-', ' ']
+        qubits = []
+        for row, line in enumerate(lines):
+            for col, c in enumerate(line.strip()):
+                if c not in no_qubit_characters:
+                    if not c.isalnum():
+                        raise ValueError("Input string has invalid character")
+                    qubits.append(GridQubit(row, col))
+        return qubits
+
     def __repr__(self):
         return 'cirq.GridQubit({}, {})'.format(self.row, self.col)
 


Test patch:
diff --git a/cirq/devices/grid_qubit_test.py b/cirq/devices/grid_qubit_test.py
--- a/cirq/devices/grid_qubit_test.py
+++ b/cirq/devices/grid_qubit_test.py
@@ -32,6 +32,53 @@ def test_xmon_qubit_eq():
     eq.make_equality_group(lambda: cirq.GridQubit(50, 25))
 
 
+def test_square():
+    assert cirq.GridQubit.square(2, top=1, left=1) == [
+        cirq.GridQubit(1, 1),
+        cirq.GridQubit(1, 2),
+        cirq.GridQubit(2, 1),
+        cirq.GridQubit(2, 2)
+    ]
+    assert cirq.GridQubit.square(2) == [
+        cirq.GridQubit(0, 0),
+        cirq.GridQubit(0, 1),
+        cirq.GridQubit(1, 0),
+        cirq.GridQubit(1, 1)
+    ]
+
+
+def test_rec():
+    assert cirq.GridQubit.rect(
+        1, 2, top=5, left=6) == [cirq.GridQubit(5, 6),
+                                 cirq.GridQubit(5, 7)]
+    assert cirq.GridQubit.rect(2, 2) == [
+        cirq.GridQubit(0, 0),
+        cirq.GridQubit(0, 1),
+        cirq.GridQubit(1, 0),
+        cirq.GridQubit(1, 1)
+    ]
+
+
+def test_diagram():
+    s = """
+-----AB-----
+----ABCD----
+---ABCDEF---
+--ABCDEFGH--
+-ABCDEFGHIJ-
+ABCDEFGHIJKL
+-CDEFGHIJKL-
+--EFGHIJKL--
+---GHIJKL---
+----IJKL----
+-----KL-----
+"""
+    assert (cirq.GridQubit.from_diagram(s) ==
+            cirq.google.known_devices._parse_device(s)[0])
+    with pytest.raises(ValueError, match="Input string has invalid character"):
+        cirq.GridQubit.from_diagram('@')
+
+
 def test_xmon_qubit_ordering():
     assert cirq.GridQubit(0, 0) < cirq.GridQubit(0, 1)
     assert cirq.GridQubit(0, 0) < cirq.GridQubit(1, 0)