Skip to content

Commit

Permalink
Merge branch 'master' into feature/vertex-snap-to-edge
Browse files Browse the repository at this point in the history
# Conflicts:
#	zxlive/proof_panel.py
  • Loading branch information
jvdwetering committed Aug 7, 2024
2 parents 7934570 + f1f15a6 commit 2ea856e
Show file tree
Hide file tree
Showing 11 changed files with 98 additions and 145 deletions.
3 changes: 2 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
include zxlive/icons/*.svg
include zxlive/tooltips/*.png
include zxlive/tooltips/*.png
include zxlive/sfx/*
54 changes: 3 additions & 51 deletions zxlive/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ def undo(self) -> None:

def redo(self) -> None:
self.old_g = self.graph_view.graph_scene.g
self.old_selected = set(self.graph_view.graph_scene.selected_vertices)
if not self.old_selected:
self.old_selected = set(self.graph_view.graph_scene.selected_vertices)
self.g = self.new_g
self.update_graph_view(True)

Expand Down Expand Up @@ -364,40 +365,6 @@ def redo(self) -> None:
self.eitem.refresh()


@dataclass
class AddIdentity(BaseCommand):
"""Adds an X or Z identity spider on an edge between two vertices."""
u: VT
v: VT
vty: VertexType

_new_vert: Optional[VT] = field(default=None, init=False)

def undo(self) -> None:
u, v, w = self.u, self.v, self._new_vert
assert w is not None
g = self.g
et = g.edge_type(g.edge(v, w))
g.remove_edge(g.edge(u, w))
g.remove_edge(g.edge(v, w))
g.remove_vertex(w)
g.add_edge(g.edge(u, v), et)
self.update_graph_view()

def redo(self) -> None:
u, v = self.u, self.v
g = self.g
uv = g.edge(u, v)
r = 0.5 * (g.row(u) + g.row(v))
q = 0.5 * (g.qubit(u) + g.qubit(v))
self._new_vert = g.add_vertex(self.vty, q, r, 0)

g.add_edge(g.edge(u, self._new_vert))
g.add_edge(g.edge(v, self._new_vert), g.edge_type(uv))
g.remove_edge(uv)
self.update_graph_view()


@dataclass
class ChangePhase(BaseCommand):
"""Updates the phase of a spider."""
Expand Down Expand Up @@ -425,22 +392,7 @@ def redo(self) -> None:


@dataclass
class ChangeColor(BaseCommand):
"""Applies the color-change rule on a set of vertices.
Changes the spider type using Hadamard conjugation."""
vs: Iterable[VT]

def toggle(self) -> None:
for v in self.vs:
basicrules.color_change(self.g, v)
self.update_graph_view()

undo = redo = toggle


@dataclass
class AddRewriteStep(SetGraph):
class AddRewriteStep(UpdateGraph):
"""Adds a new rewrite to the proof.
The rewrite is inserted after the currently selected step. In particular, it
Expand Down
1 change: 0 additions & 1 deletion zxlive/editor_base_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,6 @@ def delete_selection(self) -> None:
rem_vertices.append(get_w_partner(self.graph_scene.g, v))
if not rem_vertices and not selected_edges: return
new_g = copy.deepcopy(self.graph_scene.g)
self.graph_scene.clearSelection()
new_g.remove_edges(selected_edges)
new_g.remove_vertices(list(set(rem_vertices)))
cmd = SetGraph(self.graph_view,new_g) if len(set(rem_vertices)) > 128 \
Expand Down
2 changes: 1 addition & 1 deletion zxlive/mainwindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ def __init__(self) -> None:
view_menu.addAction(self.show_matrix_action)

new_rewrite_from_file = self._new_action("New rewrite from file", lambda: create_new_rewrite(self), None, "New rewrite from file")
new_rewrite_editor = self._new_action("New rewrite", self.new_rule_editor, None, "New rewrite")
new_rewrite_editor = self._new_action("New rewrite", lambda: self.new_rule_editor(), None, "New rewrite")
self.proof_as_rewrite_action = self._new_action("Save proof as lemma", self.proof_as_lemma, None, "Save proof as lemma")
rewrite_menu = menu.addMenu("&Rewrite")
rewrite_menu.addAction(new_rewrite_editor)
Expand Down
56 changes: 15 additions & 41 deletions zxlive/proof_panel.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
from __future__ import annotations

import copy
import random
from typing import Iterator, Union, cast

import pyzx
from PySide6.QtCore import QPointF, QSize
from PySide6.QtGui import QAction, QFontInfo, QIcon, QVector2D
from PySide6.QtWidgets import (QAbstractItemView, QInputDialog, QToolButton,
QTreeView)
from PySide6.QtGui import QAction, QIcon, QVector2D
from PySide6.QtWidgets import QInputDialog, QToolButton
from pyzx import VertexType, basicrules
from pyzx.graph.jsonparser import string_to_phase
from pyzx.utils import (EdgeType, FractionLike, get_w_partner, get_z_box_label,
Expand All @@ -23,8 +23,7 @@
from .graphscene import GraphScene
from .graphview import GraphTool, ProofGraphView, WandTrace
from .proof import ProofModel, ProofStepView
from .rewrite_action import RewriteActionTreeModel
from .rewrite_data import action_groups, refresh_custom_rules
from .rewrite_action import RewriteActionTreeView
from .settings import display_setting
from .sfx import SFXEnum
from .vitem import SCALE, W_INPUT_OFFSET, DragState, VItem
Expand All @@ -43,9 +42,8 @@ def __init__(self, graph: GraphT, *actions: QAction) -> None:
self.splitter.addWidget(self.graph_view)
self.graph_view.set_graph(graph)

self.rewrites_panel = QTreeView(self)
self.rewrites_panel = RewriteActionTreeView(self)
self.splitter.insertWidget(0, self.rewrites_panel)
self.init_rewrites_bar()

self.graph_view.wand_trace_finished.connect(self._wand_trace_finished)
self.graph_scene.vertex_dragged.connect(self._vertex_dragged)
Expand Down Expand Up @@ -89,37 +87,12 @@ def _toolbar_sections(self) -> Iterator[ToolbarSection]:
self.identity_choice[1].setText("X")
self.identity_choice[1].setCheckable(True)

self.refresh_rules = QToolButton(self)
self.refresh_rules.setText("Refresh rules")
self.refresh_rules.clicked.connect(self._refresh_rewrites_model)

yield ToolbarSection(*self.identity_choice, exclusive=True)
yield ToolbarSection(*self.actions())
yield ToolbarSection(self.refresh_rules)

def init_rewrites_bar(self) -> None:
self.reset_rewrite_panel_style()
self._refresh_rewrites_model()

def reset_rewrite_panel_style(self) -> None:
self.rewrites_panel.setUniformRowHeights(True)
self.rewrites_panel.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection)
self.rewrites_panel.setStyleSheet(
f'''
QTreeView::Item:hover {{
background-color: #e2f4ff;
}}
QTreeView::Item{{
height:{display_setting.font.pointSizeF() * 2.5}px;
}}
QTreeView::Item:!enabled {{
color: #c0c0c0;
}}
''')

def update_font(self) -> None:
self.rewrites_panel.setFont(display_setting.font)
self.reset_rewrite_panel_style()
self.rewrites_panel.reset_rewrite_panel_style()
super().update_font()

def parse_selection(self) -> tuple[list[VT], list[ET]]:
Expand Down Expand Up @@ -175,12 +148,15 @@ def _wand_trace_finished(self, trace: WandTrace) -> None:
elif self._magic_identity(trace):
return
elif self._magic_hopf(trace):
self.play_sound_signal.emit(SFXEnum.THEY_FALL_OFF)
return

def _magic_hopf(self, trace: WandTrace) -> bool:
if not all(isinstance(item, EItem) for item in trace.hit):
return False
edges: list[ET] = [item.e for item in trace.hit] # type: ignore # We know that the type of `item` is `EItem` because of the check above
if len(edges) == 0:
return False
if not all(edge == edges[0] for edge in edges):
return False
source, target = self.graph.edge_st(edges[0])
Expand Down Expand Up @@ -302,6 +278,12 @@ def _remove_id(self, v: VT) -> None:
cmd = AddRewriteStep(self.graph_view, new_g, self.step_view, "id")
self.undo_stack.push(cmd, anim_before=anim)

s = random.choice([
SFXEnum.THATS_JUST_WIRE_1,
SFXEnum.THATS_JUST_WIRE_2
])
self.play_sound_signal.emit(s)

def _unfuse_w(self, v: VT, left_edge_items: list[EItem], mouse_dir: QPointF) -> None:
new_g = copy.deepcopy(self.graph)

Expand Down Expand Up @@ -428,11 +410,3 @@ def _vert_double_clicked(self, v: VT) -> None:
basicrules.color_change(new_g, v)
cmd = AddRewriteStep(self.graph_view, new_g, self.step_view, "color change")
self.undo_stack.push(cmd)

def _refresh_rewrites_model(self) -> None:
refresh_custom_rules()
model = RewriteActionTreeModel.from_dict(action_groups, self)
self.rewrites_panel.setModel(model)
self.rewrites_panel.expand(model.index(0,0))
self.rewrites_panel.clicked.connect(model.do_rewrite)
self.graph_scene.selection_changed_custom.connect(lambda: model.executor.submit(model.update_on_selection))
45 changes: 43 additions & 2 deletions zxlive/rewrite_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@
from PySide6.QtCore import (Qt, QAbstractItemModel, QModelIndex, QPersistentModelIndex,
Signal, QObject, QMetaObject, QIODevice, QBuffer, QPoint, QPointF, QLineF)
from PySide6.QtGui import QPixmap, QColor, QPen
from PySide6.QtWidgets import QAbstractItemView, QMenu, QTreeView


from .animations import make_animation
from .commands import AddRewriteStep
from .common import ET, GraphT, VT, get_data
from .dialogs import show_error_msg
from .rewrite_data import is_rewrite_data, RewriteData, MatchType, MATCHES_VERTICES
from .rewrite_data import is_rewrite_data, RewriteData, MatchType, MATCHES_VERTICES, refresh_custom_rules, action_groups
from .settings import display_setting
from .graphscene import GraphScene
from .graphview import GraphView
Expand Down Expand Up @@ -108,7 +109,7 @@ def update_active(self, g: GraphT, verts: list[VT], edges: list[ET]) -> None:

@property
def tooltip(self) -> str:
if self.picture_path is None:
if self.picture_path is None or display_setting.previews_show == False:
return self.tooltip_str
if self.picture_path == 'custom':
# We will create a custom tooltip picture representing the custom rewrite
Expand Down Expand Up @@ -305,3 +306,43 @@ def update_on_selection(self) -> None:
g = self.proof_panel.graph_scene.g
self.root_item.update_on_selection(g, selection, edges)
QMetaObject.invokeMethod(self.emitter, "finished", Qt.ConnectionType.QueuedConnection) # type: ignore

class RewriteActionTreeView(QTreeView):
def __init__(self, parent: 'ProofPanel'):
super().__init__(parent)
self.proof_panel = parent
self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
self.customContextMenuRequested.connect(self.show_context_menu)
self.reset_rewrite_panel_style()
self.refresh_rewrites_model()

def reset_rewrite_panel_style(self) -> None:
self.setUniformRowHeights(True)
self.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection)
self.setStyleSheet(
f'''
QTreeView::Item:hover {{
background-color: #e2f4ff;
}}
QTreeView::Item{{
height:{display_setting.font.pointSizeF() * 2.5}px;
}}
QTreeView::Item:!enabled {{
color: #c0c0c0;
}}
''')

def show_context_menu(self, position: QPoint) -> None:
context_menu = QMenu(self)
refresh_rules = context_menu.addAction("Refresh rules")
action = context_menu.exec_(self.mapToGlobal(position))
if action == refresh_rules:
self.refresh_rewrites_model()

def refresh_rewrites_model(self) -> None:
refresh_custom_rules()
model = RewriteActionTreeModel.from_dict(action_groups, self.proof_panel)
self.setModel(model)
self.expand(model.index(0,0))
self.clicked.connect(model.do_rewrite)
self.proof_panel.graph_scene.selection_changed_custom.connect(lambda: model.executor.submit(model.update_on_selection))
2 changes: 1 addition & 1 deletion zxlive/rewrite_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ def ocm_rule(_graph: GraphT, _matches: list) -> pyzx.rules.RewriteOutputType[VT,
},
}

rules_basic = {"spider", "to_z", "to_x", "rem_id", "copy", "pauli", "bialgebra", "euler"}
rules_basic = {"spider", "to_z", "to_x", "rem_id", "copy", "pauli", "bialgebra", "bialgebra_op", "euler"}
operations["pauli"]["picture"] = "push_pauli.png"
operations["copy"]["picture"] = "copy_pi.png"
operations["bialgebra"]["picture"] = "bialgebra.png"
Expand Down
8 changes: 6 additions & 2 deletions zxlive/sfx.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
from enum import Enum
from PySide6.QtCore import QDir, QUrl
from PySide6.QtCore import QUrl
from PySide6.QtMultimedia import QSoundEffect

from .common import get_data

class SFXEnum(Enum):
BOOM_BOOM_BOOM = "boom-boom-boom.wav"
IRANIAN_BUS = "iranian-bus.wav"
OK_IM_GONNA_START = "ok-im-gonna-start.wav"
THATS_A_SPIDER = "thats-a-spider.wav"
THATS_JUST_WIRE_1 = "thats-just-wire-1.wav"
THATS_JUST_WIRE_2 = "thats-just-wire-2.wav"
THATS_SPIDER_FUSION = "thats-spider-fusion.wav"
THEY_FALL_OFF = "they-fall-off.wav"
WELCOME_EVERYBODY = "welcome-everybody.wav"
Expand All @@ -15,7 +19,7 @@ class SFXEnum(Enum):
def load_sfx(e: SFXEnum) -> QSoundEffect:
"""Load a sound effect from a file."""
effect = QSoundEffect()
fullpath = QDir.current().absoluteFilePath("zxlive/sfx/" + e.value)
fullpath = get_data("sfx/" + e.value)
url = QUrl.fromLocalFile(fullpath)
effect.setSource(url)

Expand Down
Binary file added zxlive/sfx/thats-just-wire-1.wav
Binary file not shown.
Binary file added zxlive/sfx/thats-just-wire-2.wav
Binary file not shown.
Loading

0 comments on commit 2ea856e

Please sign in to comment.