Update plot_gate_map() family to leverage graphviz for visualization (#10208)

* Update gate_map.py

Update the gate_map.py to migrate the visualization modules from matplotlib to rustworkx.graphviz

* Removed has_rustworkx instances

* Added release notes

* Update test_gate_map.py

Updated tests for the modified gate_map.py file

* Formatted gate_map.py

* Format test_gate_map.py

* Added release notes for the fix of #9031

* Update gate_map.py

* Update test_gate_map.py

Test file updated so that all tests can be passed.

* Update test_gate_map.py

* Update gate_map.py for rerunning tests.

* Update test_clifford.py

* Update gate_map.py to reuse rx.draw_graphiz

* Update test_gate_map.py to omit qubit_visualization

* Update test_gate_map.py to fix formatting changes

* Update gate_map.py

* Update gate_map.py

* Update gate_map.py

* Update test_gate_map.py

* Update test_gate_map.py

* Update gate_map.py

* Update gate_map.py

* Update gate_map.py

* Update gate_map.py

* Update gate_map.py

* Update gate_map.py

* Update gate_map.py

* Update test_gate_map.py

* Update test_gate_map.py to add rx import

* Update test_gate_map.py

* Update test_gate_map.py

* Update test_gate_map.py to fix imports

* Update test_gate_map.py to add HAS_GRAPHVIZ to all tests

* Update test_graph_matplotlib_drawer.py to have HAS_GRAPHVIZ

* Update test_graph_matplotlib_drawer.py to add HAS_GRAPHVIZ import to test_font_color function

* Update test_graph_matplotlib_drawer.py to add GRAPHVIZ check to the class

* Update test_graph_matplotlib_drawer.py to add HAS_GRAPHVIZ to both gate_plot_map and test_gate_plot_map import to test_font_color function

* Update test_graph_matplotlib_drawer.py

* Update test_graph_matplotlib_drawer.py

* Update test_graph_matplotlib_drawer.py

* Fixed the `font_color` parameter in `gate_map.py/plot_gate_map()`

The font_color parameter can now accept hex values as well.

* Modify color_edge function to increase speed

* Update gate_map.py to fix formatting

* Update gate_map.py to shift seaborn import to `plot_error_map`

* Add HAS_SEABORN to test_plot_error_map

* Reformat gate_map.py

* Fixed node autoscaling in `gate_map.py` graphs

* Add return type to `test_from_gate_with_cyclic_definition ` in `test_clifford.py`

* Update update-gate_map-visualizations-6ea907a0502fdc1a.yaml

* Rename update-gate_map-visualizations-6ea907a0502fdc1a.yaml to update-gate-map-visualizations-6ea907a0502fdc1a.yaml

* Update update-gate-map-visualizations-6ea907a0502fdc1a.yaml

* Update update-gate-map-visualizations-6ea907a0502fdc1a.yaml

* Update pauli_op.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Update operator.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Update pauli_sum_op.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Update random.py to use np.product

`np.product` is deprecated and is causing test fails.

* Update chi.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Replace np.product with np.prod

`np.product` is deprecated and is causing test fails.

* Update ptm.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Update stinespring.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Update superop.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Update transformations.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Update densitymatrix.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Update random.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Update statevector.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Update local_readout_mitigator.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Update unitary_synthesis.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Update test_random.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Update test_scalar_op.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Update test_random.py to use np.prod

`np.product` is deprecated and is causing test fails.

* Update test_clifford.py to remove debugging code

* Move matplotlib imports to occur at run time

* Apply suggestions from code review

* Move matplotlib_close_if_inline to runtime import too

* Flatten parallel edges

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>

* Fix 1 qubit backend handling

* Fix qubit label and font sizes

* new reference images

* Adjust font scaling

* Update reference images

* Remove unrelated reference file update

* Improve formatting

* Adjust pixel scaling factor

* Update reference images with formatting changes

---------

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>
Co-authored-by: Luciano Bello <bel@zurich.ibm.com>
This commit is contained in:
Mahnoor Fatima 2023-09-23 01:58:36 +05:00 committed by GitHub
parent af242271f8
commit 778acaf32d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 139 additions and 159 deletions

View File

@ -17,12 +17,13 @@ from typing import List
import numpy as np
import rustworkx as rx
from rustworkx.visualization import graphviz_draw
from qiskit.exceptions import QiskitError
from qiskit.utils import optionals as _optionals
from qiskit.providers.exceptions import BackendPropertyError
from qiskit.transpiler.coupling import CouplingMap
from .exceptions import VisualizationError
from .utils import matplotlib_close_if_inline
def _get_backend_interface_version(backend):
@ -42,7 +43,7 @@ def plot_gate_map(
qubit_color=None,
qubit_labels=None,
line_color=None,
font_color="w",
font_color="white",
ax=None,
filename=None,
qubit_coordinates=None,
@ -93,8 +94,6 @@ def plot_gate_map(
"""
qubit_coordinates_map = {}
qubit_coordinates_map[1] = [[0, 0]]
qubit_coordinates_map[5] = [[1, 0], [0, 1], [1, 1], [1, 2], [2, 1]]
qubit_coordinates_map[7] = [[0, 0], [0, 1], [0, 2], [1, 1], [2, 0], [2, 1], [2, 2]]
@ -912,8 +911,6 @@ def plot_gate_map(
backend_version = _get_backend_interface_version(backend)
if backend_version <= 1:
from qiskit.transpiler.coupling import CouplingMap
if backend.configuration().simulator:
raise QiskitError("Requires a device backend, not simulator.")
config = backend.configuration()
@ -927,31 +924,12 @@ def plot_gate_map(
if qubit_coordinates is None and ("ibm" in name or "fake" in name):
qubit_coordinates = qubit_coordinates_map.get(num_qubits, None)
if qubit_coordinates is None:
# Replace with planar_layout() when rustworkx offers it
qubit_coordinates_rx = rx.spring_layout(coupling_map.graph, seed=1234)
scaling_factor = 10 ** int(math.log10(num_qubits) + 1)
qubit_coordinates = [
(
int(scaling_factor * qubit_coordinates_rx[i][0]),
int(scaling_factor * qubit_coordinates_rx[i][1]),
if qubit_coordinates:
if len(qubit_coordinates) != num_qubits:
raise QiskitError(
f"The number of specified qubit coordinates {len(qubit_coordinates)} "
f"does not match the device number of qubits: {num_qubits}"
)
for i in range(num_qubits)
]
if any(x[0] < 0 or x[1] < 0 for x in qubit_coordinates):
min_entry = min(qubit_coordinates, key=lambda x: min(x[0], x[1]))
negative_offset = 0 - min(min_entry)
qubit_coordinates = [
(x[0] + negative_offset, x[1] + negative_offset) for x in qubit_coordinates
]
if len(qubit_coordinates) != num_qubits:
raise QiskitError(
f"The number of specified qubit coordinates {len(qubit_coordinates)} "
f"does not match the device number of qubits: {num_qubits}"
)
return plot_coupling_map(
num_qubits,
qubit_coordinates,
@ -972,6 +950,7 @@ def plot_gate_map(
@_optionals.HAS_MATPLOTLIB.require_in_call
@_optionals.HAS_GRAPHVIZ.require_in_call
def plot_coupling_map(
num_qubits: int,
qubit_coordinates: List[List[int]],
@ -985,7 +964,7 @@ def plot_coupling_map(
qubit_color=None,
qubit_labels=None,
line_color=None,
font_color="w",
font_color="white",
ax=None,
filename=None,
):
@ -1014,7 +993,7 @@ def plot_coupling_map(
Figure: A Matplotlib figure instance.
Raises:
MissingOptionalLibraryError: if matplotlib not installed.
MissingOptionalLibraryError: If matplotlib or graphviz is not installed.
QiskitError: If length of qubit labels does not match number of qubits.
Example:
@ -1030,20 +1009,14 @@ def plot_coupling_map(
plot_coupling_map(num_qubits, qubit_coordinates, coupling_map)
"""
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from .utils import matplotlib_close_if_inline
input_axes = False
if ax:
input_axes = True
if font_size is None:
font_size = 12
if qubit_size is None:
qubit_size = 24
if num_qubits > 20:
qubit_size = 28
font_size = 10
qubit_size = 30
if qubit_labels is None:
qubit_labels = list(range(num_qubits))
@ -1051,128 +1024,96 @@ def plot_coupling_map(
if len(qubit_labels) != num_qubits:
raise QiskitError("Length of qubit labels does not equal number of qubits.")
if qubit_coordinates is not None:
grid_data = qubit_coordinates
else:
if not input_axes:
fig, ax = plt.subplots(figsize=(5, 5))
ax.axis("off")
if filename:
fig.savefig(filename)
return fig
x_max = max(d[1] for d in grid_data)
y_max = max(d[0] for d in grid_data)
max_dim = max(x_max, y_max)
if figsize is None:
if num_qubits == 1 or (x_max / max_dim > 0.33 and y_max / max_dim > 0.33):
figsize = (5, 5)
else:
figsize = (9, 3)
if ax is None:
fig, ax = plt.subplots(figsize=figsize)
ax.axis("off")
if not label_qubits:
qubit_labels = [""] * num_qubits
# set coloring
if qubit_color is None:
qubit_color = ["#648fff"] * num_qubits
if line_color is None:
line_color = ["#648fff"] * len(coupling_map) if coupling_map else []
line_color = ["#648fff"] * len(coupling_map)
# Add lines for couplings
if num_qubits != 1:
for ind, edge in enumerate(coupling_map):
is_symmetric = False
if edge[::-1] in coupling_map:
is_symmetric = True
y_start = grid_data[edge[0]][0]
x_start = grid_data[edge[0]][1]
y_end = grid_data[edge[1]][0]
x_end = grid_data[edge[1]][1]
if num_qubits == 1:
graph = rx.PyDiGraph()
graph.add_node(0)
else:
graph = CouplingMap(coupling_map).graph
if is_symmetric:
if y_start == y_end:
x_end = (x_end - x_start) / 2 + x_start
if not plot_directed:
graph = graph.to_undirected(multigraph=False)
elif x_start == x_end:
y_end = (y_end - y_start) / 2 + y_start
for node in graph.node_indices():
graph[node] = node
else:
x_end = (x_end - x_start) / 2 + x_start
y_end = (y_end - y_start) / 2 + y_start
ax.add_artist(
plt.Line2D(
[x_start, x_end],
[-y_start, -y_end],
color=line_color[ind],
linewidth=line_width,
zorder=0,
)
)
if plot_directed:
dx = x_end - x_start
dy = y_end - y_start
if is_symmetric:
x_arrow = x_start + dx * 0.95
y_arrow = -y_start - dy * 0.95
dx_arrow = dx * 0.01
dy_arrow = -dy * 0.01
head_width = 0.15
else:
x_arrow = x_start + dx * 0.5
y_arrow = -y_start - dy * 0.5
dx_arrow = dx * 0.2
dy_arrow = -dy * 0.2
head_width = 0.2
ax.add_patch(
mpatches.FancyArrow(
x_arrow,
y_arrow,
dx_arrow,
dy_arrow,
head_width=head_width,
length_includes_head=True,
edgecolor=None,
linewidth=0,
facecolor=line_color[ind],
zorder=1,
)
)
for edge_index in graph.edge_indices():
graph.update_edge_by_index(edge_index, edge_index)
# Add circles for qubits
for var, idx in enumerate(grid_data):
_idx = [idx[1], -idx[0]]
ax.add_artist(
mpatches.Ellipse(
_idx,
qubit_size / 48,
qubit_size / 48, # This is here so that the changes
color=qubit_color[var],
zorder=1,
)
) # to how qubits are plotted does
if label_qubits: # not affect qubit size kwarg.
ax.text(
*_idx,
s=qubit_labels[var],
horizontalalignment="center",
verticalalignment="center",
color=font_color,
size=font_size,
weight="bold",
)
ax.set_xlim([-1, x_max + 1])
ax.set_ylim([-(y_max + 1), 1])
ax.set_aspect("equal")
# pixel-to-inch conversion
px = 1.15 / plt.rcParams["figure.dpi"]
if qubit_coordinates:
qubit_coordinates = [coordinates[::-1] for coordinates in qubit_coordinates]
if font_size is None:
max_characters = max(1, max(len(str(x)) for x in qubit_labels))
font_size = max(int(20 / max_characters), 1)
def color_node(node):
if qubit_coordinates:
out_dict = {
"label": str(qubit_labels[node]),
"color": f'"{qubit_color[node]}"',
"fillcolor": f'"{qubit_color[node]}"',
"style": "filled",
"shape": "circle",
"pos": f'"{qubit_coordinates[node][0]},{qubit_coordinates[node][1]}"',
"pin": "True",
}
else:
out_dict = {
"label": str(qubit_labels[node]),
"color": f'"{qubit_color[node]}"',
"fillcolor": f'"{qubit_color[node]}"',
"style": "filled",
"shape": "circle",
}
out_dict["fontcolor"] = f'"{font_color}"'
out_dict["fontsize"] = str(font_size)
out_dict["height"] = str(qubit_size * px)
out_dict["fixedsize"] = "True"
out_dict["fontname"] = '"DejaVu Sans"'
return out_dict
def color_edge(edge):
out_dict = {
"color": f'"{line_color[edge]}"',
"fillcolor": f'"{line_color[edge]}"',
"penwidth": str(line_width),
}
return out_dict
plot = graphviz_draw(
graph,
method="neato",
node_attr_fn=color_node,
edge_attr_fn=color_edge,
filename=filename,
)
if filename:
return None
if not input_axes:
if figsize is None:
width, height = plot.size
figsize = (width * px, height * px)
fig, ax = plt.subplots(figsize=figsize)
ax.axis("off")
ax.imshow(plot)
if not input_axes:
matplotlib_close_if_inline(fig)
if filename:
fig.savefig(filename)
return fig
return None
def plot_circuit_layout(circuit, backend, view="virtual", qubit_coordinates=None):
@ -1232,7 +1173,7 @@ def plot_circuit_layout(circuit, backend, view="virtual", qubit_coordinates=None
cmap_len = cmap.graph.num_edges()
qubits = []
qubit_labels = [None] * num_qubits
qubit_labels = [""] * num_qubits
bit_locations = {
bit: {"register": register, "index": index}
@ -1248,27 +1189,27 @@ def plot_circuit_layout(circuit, backend, view="virtual", qubit_coordinates=None
bit_register = bit_locations[key]["register"]
if bit_register is None or bit_register.name != "ancilla":
qubits.append(val)
qubit_labels[val] = bit_locations[key]["index"]
qubit_labels[val] = str(bit_locations[key]["index"])
elif view == "physical":
for key, val in circuit._layout.initial_layout.get_physical_bits().items():
bit_register = bit_locations[val]["register"]
if bit_register is None or bit_register.name != "ancilla":
qubits.append(key)
qubit_labels[key] = key
qubit_labels[key] = str(key)
else:
raise VisualizationError("Layout view must be 'virtual' or 'physical'.")
qcolors = ["#648fff"] * num_qubits
for k in qubits:
qcolors[k] = "k"
qcolors[k] = "black"
lcolors = ["#648fff"] * cmap_len
for idx, edge in enumerate(cmap):
if edge[0] in qubits and edge[1] in qubits:
lcolors[idx] = "k"
lcolors[idx] = "black"
fig = plot_gate_map(
backend,
@ -1282,7 +1223,7 @@ def plot_circuit_layout(circuit, backend, view="virtual", qubit_coordinates=None
@_optionals.HAS_MATPLOTLIB.require_in_call
@_optionals.HAS_SEABORN.require_in_call
def plot_error_map(backend, figsize=(12, 9), show_title=True, qubit_coordinates=None):
def plot_error_map(backend, figsize=(15, 12), show_title=True, qubit_coordinates=None):
"""Plots the error map of a given backend.
Args:
@ -1300,7 +1241,7 @@ def plot_error_map(backend, figsize=(12, 9), show_title=True, qubit_coordinates=
Raises:
VisualizationError: The backend does not provide gate errors for the 'sx' gate.
MissingOptionalLibraryError: If seaborn is not installed
MissingOptionalLibraryError: If matplotlib or seaborn is not installed.
Example:
.. plot::
@ -1313,10 +1254,11 @@ def plot_error_map(backend, figsize=(12, 9), show_title=True, qubit_coordinates=
backend = FakeVigoV2()
plot_error_map(backend)
"""
import seaborn as sns
import matplotlib
import matplotlib.pyplot as plt
from matplotlib import gridspec, ticker
import seaborn as sns
from .utils import matplotlib_close_if_inline
color_map = sns.cubehelix_palette(reverse=True, as_cmap=True)
@ -1406,7 +1348,7 @@ def plot_error_map(backend, figsize=(12, 9), show_title=True, qubit_coordinates=
single_norm = matplotlib.colors.Normalize(
vmin=min(single_gate_errors), vmax=max(single_gate_errors)
)
q_colors = [color_map(single_norm(err)) for err in single_gate_errors]
q_colors = [matplotlib.colors.to_hex(color_map(single_norm(err))) for err in single_gate_errors]
directed = False
line_colors = []
@ -1417,7 +1359,7 @@ def plot_error_map(backend, figsize=(12, 9), show_title=True, qubit_coordinates=
avg_cx_err = np.mean(cx_errors)
cx_norm = matplotlib.colors.Normalize(vmin=min(cx_errors), vmax=max(cx_errors))
line_colors = [color_map(cx_norm(err)) for err in cx_errors]
line_colors = [matplotlib.colors.to_hex(color_map(cx_norm(err))) for err in cx_errors]
read_err = 100 * np.asarray(read_err)
avg_read_err = np.mean(read_err)
@ -1450,6 +1392,7 @@ def plot_error_map(backend, figsize=(12, 9), show_title=True, qubit_coordinates=
ax=main_ax,
qubit_coordinates=qubit_coordinates,
)
main_ax.axis("off")
main_ax.set_aspect(1)
if cmap:

View File

@ -0,0 +1,19 @@
---
features:
- |
The visualizations from the :func:`~.plot_gate_map`, :func:`~.plot_coupling_map`.
:func:`~.plot_error_map`, and :func:`~.plot_circuit_layout` functions have been significantly
improved for rendering layouts of backends with large numbers of qubits. This was accomplished
by leveraging `graphviz <https://graphviz.org/>`__ through rustworkx's ``graphviz_draw()`` function
to perform a more sophisticated algorithmic graph layout that scales for large numbers of
qubits.
upgrade:
- |
The visualization functions: :func:`~.plot_gate_map`, :func:`~.plot_coupling_map`.
:func:`~.plot_error_map`, and :func:`~.plot_circuit_layout` now depend on
`graphviz <https://graphviz.org/>`__ being installed to function. This change was
necessary to enable visualizing backends with larger numbers of qubits. This
additional external requirement is in addition to the existing optional dependencies
these functions previously required. You find details on how to install
graphviz here: https://graphviz.org/download/

View File

@ -55,6 +55,8 @@ class TestGateMap(QiskitVisualizationTestCase):
)
@data(*backends)
@unittest.skipIf(not optionals.HAS_MATPLOTLIB, "matplotlib not available.")
@unittest.skipUnless(optionals.HAS_GRAPHVIZ, "Graphviz not installed")
def test_plot_gate_map(self, backend):
"""tests plotting of gate map of a device (20 qubit, 16 qubit, 14 qubit and 5 qubit)"""
n = backend.configuration().n_qubits
@ -67,6 +69,8 @@ class TestGateMap(QiskitVisualizationTestCase):
plt.close(fig)
@data(*backends)
@unittest.skipIf(not optionals.HAS_MATPLOTLIB, "matplotlib not available.")
@unittest.skipUnless(optionals.HAS_GRAPHVIZ, "Graphviz not installed")
def test_plot_circuit_layout(self, backend):
"""tests plot_circuit_layout for each device"""
layout_length = int(backend._configuration.n_qubits / 2)
@ -83,9 +87,11 @@ class TestGateMap(QiskitVisualizationTestCase):
with BytesIO() as img_buffer:
fig.savefig(img_buffer, format="png")
img_buffer.seek(0)
self.assertImagesAreEqual(Image.open(img_buffer), img_ref, 0.1)
self.assertImagesAreEqual(Image.open(img_buffer), img_ref, 0.2)
plt.close(fig)
@unittest.skipIf(not optionals.HAS_MATPLOTLIB, "matplotlib not available.")
@unittest.skipUnless(optionals.HAS_GRAPHVIZ, "Graphviz not installed")
def test_plot_gate_map_no_backend(self):
"""tests plotting of gate map without a device"""
n_qubits = 8
@ -101,6 +107,9 @@ class TestGateMap(QiskitVisualizationTestCase):
self.assertImagesAreEqual(Image.open(img_buffer), img_ref, 0.2)
plt.close(fig)
@unittest.skipIf(not optionals.HAS_MATPLOTLIB, "matplotlib not available.")
@unittest.skipUnless(optionals.HAS_GRAPHVIZ, "Graphviz not installed")
@unittest.skipUnless(optionals.HAS_SEABORN, "Seaborn not installed")
def test_plot_error_map_backend_v1(self):
"""Test plotting error map with fake backend v1."""
backend = FakeKolkata()
@ -112,6 +121,9 @@ class TestGateMap(QiskitVisualizationTestCase):
self.assertImagesAreEqual(Image.open(img_buffer), img_ref, 0.2)
plt.close(fig)
@unittest.skipIf(not optionals.HAS_MATPLOTLIB, "matplotlib not available.")
@unittest.skipUnless(optionals.HAS_GRAPHVIZ, "Graphviz not installed")
@unittest.skipUnless(optionals.HAS_SEABORN, "Seaborn not installed")
def test_plot_error_map_backend_v2(self):
"""Test plotting error map with fake backend v2."""
backend = FakeKolkataV2()
@ -123,6 +135,9 @@ class TestGateMap(QiskitVisualizationTestCase):
self.assertImagesAreEqual(Image.open(img_buffer), img_ref, 0.2)
plt.close(fig)
@unittest.skipIf(not optionals.HAS_MATPLOTLIB, "matplotlib not available.")
@unittest.skipUnless(optionals.HAS_GRAPHVIZ, "Graphviz not installed")
@unittest.skipUnless(optionals.HAS_SEABORN, "Seaborn not installed")
def test_plot_error_map_over_100_qubit(self):
"""Test plotting error map with large fake backend."""
backend = FakeWashington()
@ -134,6 +149,9 @@ class TestGateMap(QiskitVisualizationTestCase):
self.assertImagesAreEqual(Image.open(img_buffer), img_ref, 0.2)
plt.close(fig)
@unittest.skipIf(not optionals.HAS_MATPLOTLIB, "matplotlib not available.")
@unittest.skipUnless(optionals.HAS_GRAPHVIZ, "Graphviz not installed")
@unittest.skipUnless(optionals.HAS_SEABORN, "Seaborn not installed")
def test_plot_error_map_over_100_qubit_backend_v2(self):
"""Test plotting error map with large fake backendv2."""
backend = FakeWashingtonV2()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB