Skip to content

Commit

Permalink
repair pointer events and add zoom and drag and drop in editor
Browse files Browse the repository at this point in the history
  • Loading branch information
Stefan Werner committed Dec 11, 2023
1 parent 63e3940 commit a6e1a94
Show file tree
Hide file tree
Showing 19 changed files with 549 additions and 161 deletions.
113 changes: 79 additions & 34 deletions .idea/workspace.xml

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion chroma_viz/app.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from langchain.vectorstores import Chroma

vectordb = Chroma.from_documents(data, embeddings, ids)

from chromaviz import visualize_collection
visualize_collection(vectordb._collection)

visualize_collection(vectordb._collection)
3 changes: 3 additions & 0 deletions integrator/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ RUN apt install -y clang python3-dev build-essential uwsgi uwsgi-plugin-gevent-p
#RUN pip install uwsgi==2.0.23 gevent
#RUN pip install gevent-websocket
#CMD ["pypy3", "server.py"]
RUN pip install spacy
RUN python -m spacy download en_core_web_sm

ENTRYPOINT ["gunicorn", "-k", "geventwebsocket.gunicorn.workers.GeventWebSocketWorker", "-w", "1", "--threads", "100", "-b", "0.0.0.0:5000", "--timeout", "2000" , "--graceful-timeout", "200", "server:app"]

#ENTRYPOINT uwsgi --plugins-list
Expand Down
12 changes: 9 additions & 3 deletions integrator/cluster_hierarchy.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import numpy as np


def single_linkage_clustering(data, n_clusters):
# Calculate the Euclidean distance matrix
distance_matrix = np.sqrt(((data[:, np.newaxis] - data[np.newaxis, :]) ** 2).sum(axis=2))
distance_matrix = np.sqrt(
((data[:, np.newaxis] - data[np.newaxis, :]) ** 2).sum(axis=2)
)

# Initialize clusters as each individual point
clusters = [{i} for i in range(len(data))]
Expand All @@ -11,8 +14,10 @@ def single_linkage_clustering(data, n_clusters):
# Find the pair of clusters with minimum distance
min_dist = np.inf
for i in range(len(clusters)):
for j in range(i+1, len(clusters)):
dist = min(distance_matrix[x, y] for x in clusters[i] for y in clusters[j])
for j in range(i + 1, len(clusters)):
dist = min(
distance_matrix[x, y] for x in clusters[i] for y in clusters[j]
)
if dist < min_dist:
min_dist = dist
closest_pair = (i, j)
Expand All @@ -23,6 +28,7 @@ def single_linkage_clustering(data, n_clusters):

return clusters


# Example usage
data = np.array([[1, 2], [2, 3], [3, 4], [10, 10], [11, 11], [12, 12]])
n_clusters = 2
Expand Down
22 changes: 19 additions & 3 deletions integrator/combination_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def __next__(self):
i_a, i_b = self.on_indices[a], self.on_indices[b]
else:
i_a, i_b = a, b
if not self.on [i_a, i_b]:
if not self.on[i_a, i_b]:
self._advance_generator()
self.yielded_count += 1
valid = False
Expand Down Expand Up @@ -68,10 +68,26 @@ def get_percentage(self):
return round(self.yielded_count / math.comb(self.n, self.r), 2)

def __getstate__(self):
return self.pool, self.r, self.indices, self.exhausted, self.yielded_count, self.on, self.on_indices
return (
self.pool,
self.r,
self.indices,
self.exhausted,
self.yielded_count,
self.on,
self.on_indices,
)

def __setstate__(self, state):
self.pool, self.r, self.indices, self.exhausted, self.yielded_count, self.on, self.on_indices = state
(
self.pool,
self.r,
self.indices,
self.exhausted,
self.yielded_count,
self.on,
self.on_indices,
) = state
self.n = len(self.pool)


Expand Down
20 changes: 11 additions & 9 deletions integrator/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import itertools
import logging
from pprint import pprint

from classifier.result.predict import MODELS
from integrator.serialize import serialize_graph_to_structure
from integrator.states import states
Expand Down Expand Up @@ -37,14 +38,15 @@ def infer(model_name, t, valid_labels, tree=None, on=None, on_indices=None):
)
elif isinstance(t, list):
if len(t) >= 1:

if isinstance(t[0], tuple):
texts = []
keys = []
pre_computed_scores = []
for (n1, n2), score in t:
if n1 not in tree.index_text or n2 not in tree.index_text:
logging.error(f"node {n1} or {n2} not in tree index: {tree.index_text}")
logging.error(
f"node {n1} or {n2} not in tree index: {tree.index_text}"
)
continue
texts.append((tree.index_text[n1], tree.index_text[n2]))
keys.append((n1, n2))
Expand All @@ -63,11 +65,7 @@ def infer(model_name, t, valid_labels, tree=None, on=None, on_indices=None):
), list(score.view(-1, MODELS[model_name].config.n_samples).tolist())

if pre_computed_scores:
score = [
[s, s]
for s in
pre_computed_scores
]
score = [[s, s] for s in pre_computed_scores]
lsk = [
(l, s, k)
for l, s, k in zip(labels, score, keys)
Expand Down Expand Up @@ -139,14 +137,18 @@ def score(model_name, t, relation, hash_id="default"):
scores = [score for _, score in results]
max_score = max(scores)
min_score = min(scores)
results = [(key, (score - min_score) / (max_score - min_score)) for key, score in results]
results = [
(key, (score - min_score) / (max_score - min_score)) for key, score in results
]

return results


# classify thesis, antithesis, synthesis
def classifier(t, on=None, on_indices=None):
return infer("thesis_antithesis_synthesis", t, [1, 2, 3], on=on, on_indices=on_indices)
return infer(
"thesis_antithesis_synthesis", t, [1, 2, 3], on=on, on_indices=on_indices
)


# classify hypernym, hyponym
Expand Down
24 changes: 15 additions & 9 deletions integrator/reader/__init__.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,32 @@
from .reader_dialogue import parse_dialogue
from .reader_enumerated import parse_enumerated
from .reader_sentences import parse_sentences


# Parse the text
def parse_text(text):

result = parse_enumerated(text)

if not result:
result = parse_sentences(text)
if not result:
result = parse_dialogue(text)

return result


def get_inputs(filename):
with open(filename) as f:
text = f.read()
return parse_dialogue(text)


if __name__ == "__main__":

from pprint import pprint


if __name__ == "__main__":
# Test
text = """
# Test
text = """
6.1265 Logic can always be conceived to be such that every proposition is its own proof.
Expand All @@ -35,8 +41,8 @@ def get_inputs(filename):
6.13 Logic is not a theory but a reflexion of the world.
"""

parsed_data = parse_text(text)
pprint(parsed_data)
parsed_data = parse_text(text)
pprint(parsed_data)

pprint(get_inputs("texts/euclid.txt"))
pprint(get_inputs("texts/phaedrus.1b.txt"))
pprint(get_inputs("texts/euclid.txt"))
pprint(get_inputs("texts/phaedrus.1b.txt"))
35 changes: 35 additions & 0 deletions integrator/reader/reader_sentences.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from pprint import pprint

import spacy

# Load the spaCy model
nlp = spacy.load("en_core_web_sm")

def parse_sentences(text):
# Use spaCy's pipeline for sentence segmentation
doc = nlp(text)
sentences = [sent.text.strip() for sent in doc.sents]
return sentences

def get_inputs(filename):
with open(filename, 'r') as f:
text = f.read()
return parse_sentences(text)

if __name__ == "__main__":
dialogue_text = """
My dear Phaedrus, whence come you, and whither are you going?
I come from Lysias the son of Cephalus, and I am going to
take a walk outside the wall,
for I have been sitting with him the
whole morning; and our common friend Acumenus tells me that it is
much more refreshing to walk in the open air than to be shut up in
a cloister.
There he is right. Lysias then, I suppose, was in the town?
"""

parsed_dialogue = parse_sentences(dialogue_text)
pprint(parsed_dialogue)

# Example of reading from a file
# pprint(get_inputs("texts/phaedrus.1b.txt"))
11 changes: 8 additions & 3 deletions integrator/tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,15 +194,17 @@ def draw_graph(self, graph=None, root=None, path="graph.png", text_relation=True
except Exception as e:
logging.error(f"Error in drawing graph {e}", exc_info=True)

def pull(self, n_samples, relations, on=None, on_indices =None):
def pull(self, n_samples, relations, on=None, on_indices=None):
if not tuple(relations) in self.iterators:
self.iterators[tuple(relations)] = CustomCombinations(
list(self.node_index.keys()), n_samples, on=on, on_indices=on_indices
)
for k in self.iterators[tuple(relations)]:
yield [(i, self.index_text[i]) for i in k]

def pull_batch(self, batch_size, n_samples, relations=None, on=None ,on_indices =None):
def pull_batch(
self, batch_size, n_samples, relations=None, on=None, on_indices=None
):
"""
:param batch_size:
:param n_samples:
Expand All @@ -215,7 +217,10 @@ def pull_batch(self, batch_size, n_samples, relations=None, on=None ,on_indices
*[
([i for i, _ in samples], [s for _, s in samples])
for samples in maxislice(
self.pull(n_samples, relations, on=on, on_indices=on_indices), batch_size
self.pull(
n_samples, relations, on=on, on_indices=on_indices
),
batch_size,
)
]
)
Expand Down
1 change: 1 addition & 0 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-markdown": "^8.0.7",
"react-pan-zoom": "^1.0.2",
"react-query": "^3.39.3",
"react-scripts": "5.0.1",
"react-zoom-pan-pinch": "^3.0.8",
Expand Down
18 changes: 18 additions & 0 deletions web/src/lib/position.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,21 @@ export function postProcessTitle(title) {
if (!title || typeof title == 'object') return null
return title?.replace('.md', '')?.replace('_.', '')
}

export const pointInTriangle = (px, py, ax, ay, bx, by, cx, cy) => {
const v0 = [cx - ax, cy - ay]
const v1 = [bx - ax, by - ay]
const v2 = [px - ax, py - ay]

const dot00 = v0[0] * v0[0] + v0[1] * v0[1]
const dot01 = v0[0] * v1[0] + v0[1] * v1[1]
const dot02 = v0[0] * v2[0] + v0[1] * v2[1]
const dot11 = v1[0] * v1[0] + v1[1] * v1[1]
const dot12 = v1[0] * v2[0] + v1[1] * v2[1]

const invDenom = 1 / (dot00 * dot11 - dot01 * dot01)
const u = (dot11 * dot02 - dot01 * dot12) * invDenom
const v = (dot00 * dot12 - dot01 * dot02) * invDenom

return u >= 0 && v >= 0 && u + v < 1
}
44 changes: 42 additions & 2 deletions web/src/lib/useMuuriGrid.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useEffect } from 'react'
import Muuri from 'muuri'
import { idToSize, idToZIndex, positionToId } from '../ui/editor/Puzzle'
import { pointInTriangle } from './position'

const useMuuriGrid = (gridRef, options, size, props) => {
useEffect(() => {
Expand All @@ -9,7 +10,29 @@ const useMuuriGrid = (gridRef, options, size, props) => {

const grid = new Muuri(gridRef.current, options)
grid.size = size
grid.on('dragStart', (item, event) => {
console.log('DRAG START', item, event)
const element = item.getElement()
const rect = element.getBoundingClientRect()

// Triangle vertices based on the rectangle
const ax = rect.left
const ay = rect.bottom
const bx = rect.right
const by = rect.bottom
const cx = rect.left + rect.width / 2
const cy = rect.top

// Get the mouse position
const mouseX = event.clientX
const mouseY = event.clientY

// Check if the click is inside the triangle
if (!pointInTriangle(mouseX, mouseY, ax, ay, bx, by, cx, cy)) {
event.srcEvent.preventDefault()
return
}
})
grid.on('dragEnd', (item, event) => {
console.log('DRAG RELEASE END', item, event)

Expand All @@ -30,6 +53,18 @@ const useMuuriGrid = (gridRef, options, size, props) => {
props.action(newId)
return
}

// Find the triangle-text sub-divs
const elementTriangleText = element.querySelector('.triangle-text')
const targetTriangleText = targetElement.querySelector('.triangle-text')

// Swap their font sizes
if (elementTriangleText && targetTriangleText) {
const tempFontSize = elementTriangleText.style.fontSize
elementTriangleText.style.fontSize = targetTriangleText.style.fontSize
targetTriangleText.style.fontSize = tempFontSize
}

targetElement.setAttribute('id', oldId)
element.setAttribute('id', newId)

Expand All @@ -38,8 +73,10 @@ const useMuuriGrid = (gridRef, options, size, props) => {
element.style.width = idToSize(newId)
element.style.height = idToSize(newId)

targetElement.style.zIndex = idToZIndex(oldId)
element.style.zIndex = idToZIndex(newId)
const oldZIndex = targetElement.style.zIndex

targetElement.style.zIndex = element.style.zIndex
element.style.zIndex = oldZIndex

props.socket.timeout(3000).emit(
'save_params',
Expand Down Expand Up @@ -67,6 +104,9 @@ const useMuuriGrid = (gridRef, options, size, props) => {
return () => {
// Remove event listener for dragReleaseEnd event
grid.off('dragReleaseEnd')

grid.off('dragStart')
grid.off('dragEnd')
try {
grid.destroy()
} catch (e) {
Expand Down
Loading

0 comments on commit a6e1a94

Please sign in to comment.