Skip to content

Commit

Permalink
many ui updates
Browse files Browse the repository at this point in the history
  • Loading branch information
syedzayyan committed Mar 13, 2024
1 parent 96b52b6 commit ae2aa23
Show file tree
Hide file tree
Showing 8 changed files with 4,614 additions and 13,002 deletions.
Binary file modified .DS_Store
Binary file not shown.
4 changes: 2 additions & 2 deletions app/tools/ml/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,8 @@ export default function ML() {
each fold is tested. This validation technique helps mitigate splitting biases.
</p>
<p>&emsp;Considering <a href="https://pubs.acs.org/doi/10.1021/ci400099q">the error rates in experimental assays and in general with ChEMBL</a>,
the mean absolute error (MAE) value of about 0.45 - 0.85 is a good ballpark range. Anything below 0.45 is freakishly low and possibly overfitting.
The R-squared value of near to one is desirable, but again given error rates, anything above 0.90ish is freakishly high.</p>
the mean absolute error (MAE) value of about 0.45 - 0.85 is a good ballpark range. Anything below 0.45 is low and possibly overfitting.
The R-squared value of near to one is desirable, but again given error rates, anything above 0.90ish is high.</p>
<p>&emsp;Now that you have an idea how the model performs, and how the training data looks like, this trained model could be used, to virtually screen
new molecules against your target. You could supply your own molecule using the CSV loader, or use ZINC (Both of these features are coming soon).
For now you could test one smile at a time.
Expand Down
3 changes: 1 addition & 2 deletions app/tools/scaff_net/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { useContext, useEffect, useState } from "react";
import RDKitContext from "../../../context/RDKitContext";
import LigandContext from "../../../context/LigandContext";

import "@react-sigma/core/lib/react-sigma.min.css";
import { scaffold_net_chunking_method } from "../../../components/utils/rdkit_loader";
import Loader from "../../../components/ui-comps/Loader";
import ScaffoldNetworkWholeGraph from "../../../components/tools/toolComp/ScaffoldNetworkWholeGraph";
Expand All @@ -25,7 +24,7 @@ export default function DisplayGraph() {
setTimeout(() => {
let smiles_list = ligand.map((x) => x.canonical_smiles);
const network = scaffold_net_chunking_method(smiles_list, 50, rdkit);
const graph = loadGraphFromScaffNet(network, smiles_list);
const graph = loadGraphFromScaffNet(network, smiles_list, rdkit);
setGraph(graph);
setLoaded(true);
}, 80);
Expand Down
226 changes: 92 additions & 134 deletions components/tools/toolComp/ScaffoldNetworkWholeGraph.tsx
Original file line number Diff line number Diff line change
@@ -1,141 +1,99 @@
import "@react-sigma/core/lib/react-sigma.min.css";
import getNodeProgramImage from "sigma/rendering/webgl/programs/node.image";
import {
SigmaContainer,
useRegisterEvents,
useSetSettings,
useSigma,
} from "@react-sigma/core";
import { useEffect, useState } from "react";
import { Attributes } from "graphology-types";
import { circular } from "graphology-layout";
import { subgraph } from "graphology-operators";

const GraphEvents: React.FC = () => {
const registerEvents = useRegisterEvents();
const sigma = useSigma();

const [draggedNode, setDraggedNode] = useState<string | null>(null);
const [hoveredNode, setHoveredNode] = useState<string | null>(null);

const setSettings = useSetSettings();
import React, { useEffect, useRef } from 'react';
import * as d3 from 'd3';

const ScaffoldNetworkWholeGraph = ({ graph, imageSize = 120 }) => {
const svgRef = useRef(null);

useEffect(() => {
setSettings({
nodeReducer: (node, data) => {
const graph = sigma.getGraph();
const newData: Attributes = {
...data,
highlighted: data.highlighted || false,
};

if (hoveredNode) {
if (
node === hoveredNode ||
graph.neighbors(hoveredNode).includes(node)
) {
newData.highlighted = true;
newData.size = 40;
(newData.type = "image"), (newData.image = "/logo.svg");
} else {
newData.color = "#E2E2E2";
newData.highlighted = false;
}
}
return newData;
},
edgeReducer: (edge, data) => {
const graph = sigma.getGraph();
const newData = { ...data, hidden: false };

if (hoveredNode && !graph.extremities(edge).includes(hoveredNode)) {
newData.hidden = true;
}
return newData;
},
});
const width = 928;
const height = 680;


const graph_ex = graph.export();
const { nodes, links } = {
nodes: graph_ex.nodes.map(n => ({ id: n.key, ...n.attributes })),
links: graph_ex.edges
}
const simulation = d3.forceSimulation(nodes)
.force("link", d3.forceLink(links).id(d => d.id))
.force("charge", d3.forceManyBody())
.force("x", d3.forceX())
.force("y", d3.forceY());

const svg = d3.select(svgRef.current)
.attr("width", width)
.attr("height", height)
.attr("viewBox", [-width / 2, -height / 2, width, height])
.attr("style", "max-width: 100%; height: auto;")
.call(d3.zoom().on("zoom", function (event) {
svg.attr("transform", event.transform);
}));

const link = svg.append("g")
.attr("stroke", "#999")
.attr("stroke-opacity", 0.5)
.selectAll("line")
.data(links)
.attr("stroke", d => d.color)
.join("line")
.attr("stroke-width", 0.5)

const node = svg.append("g")
.selectAll("image")
.data(nodes)
.join("image")
.attr("xlink:href", d => d.image) // Function to get image URL
.attr("width", imageSize * 0.10) // Adjust image width
.attr("height", imageSize * 0.10); // Adjust image height

// Register the events
registerEvents({
enterNode: (event) => setHoveredNode(event.node),
leaveNode: () => setHoveredNode(null),
downNode: (e) => {
setDraggedNode(e.node);
sigma.getGraph().setNodeAttribute(e.node, "highlighted", true);
},
mouseup: (e) => {
if (draggedNode) {
setDraggedNode(null);
sigma.getGraph().removeNodeAttribute(draggedNode, "highlighted");
}
},
mousedown: (e) => {
// Disable the autoscale at the first down interaction
if (!sigma.getCustomBBox()) sigma.setCustomBBox(sigma.getBBox());
},
mousemove: (e) => {
if (draggedNode) {
// Get new position of node
const pos = sigma.viewportToGraph(e);
sigma.getGraph().setNodeAttribute(draggedNode, "x", pos.x);
sigma.getGraph().setNodeAttribute(draggedNode, "y", pos.y);

// Prevent sigma to move camera:
e.preventSigmaDefault();
e.original.preventDefault();
e.original.stopPropagation();
}
},
touchup: (e) => {
if (draggedNode) {
setDraggedNode(null);
sigma.getGraph().removeNodeAttribute(draggedNode, "highlighted");
}
},
touchdown: (e) => {
// Disable the autoscale at the first down interaction
if (!sigma.getCustomBBox()) sigma.setCustomBBox(sigma.getBBox());
},
touchmove: (e) => {
if (draggedNode) {
// Get new position of node
// @ts-ignore
const pos = sigma.viewportToGraph(e);
sigma.getGraph().setNodeAttribute(draggedNode, "x", pos.x);
sigma.getGraph().setNodeAttribute(draggedNode, "y", pos.y);

// Prevent sigma to move camera:
// @ts-ignore
e.preventSigmaDefault();
e.original.preventDefault();
e.original.stopPropagation();
}
},
node.append("title")
.text(d => d.smiles);


// Add a drag behavior.
node.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));

// Set the position attributes of links and nodes each time the simulation ticks.
simulation.on("tick", () => {
link
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y);

node
.attr("x", d => d.x)
.attr("y", d => d.y);
});
}, [hoveredNode, setSettings, sigma]);

return null;
// Reheat the simulation when drag starts, and fix the subject position.
function dragstarted(event) {
if (!event.active) simulation.alphaTarget(0.3).restart();
event.subject.fx = event.subject.x;
event.subject.fy = event.subject.y;
}
function dragged(event) {
event.subject.fx = event.x;
event.subject.fy = event.y;
}
function dragended(event) {
if (!event.active) simulation.alphaTarget(0);
event.subject.fx = null;
event.subject.fy = null;
}

// When this cell is re-run, stop the previous simulation. (This doesn’t
// really matter since the target alpha is zero and the simulation will
// stop naturally, but it’s a good practice.)
// // invalidation.then(() => simulation.stop());


}, [graph]);

return <svg ref={svgRef} width="100%" height="80vh" />;
};

export default function ScaffoldNetworkWholeGraph({ graph }) {
const sub = subgraph(graph, function (_, attr) {
return attr.nodeType === "whole";
});
const positions = circular(sub, { scale: 100 });
circular.assign(graph, positions);

return (
<div>
<SigmaContainer
style={{ height: "600px", width: "100%" }}
graph={graph}
settings={{
nodeProgramClasses: { image: getNodeProgramImage() },
defaultNodeType: "image",
}}
>
<GraphEvents />
</SigmaContainer>
</div>
);
}
export default ScaffoldNetworkWholeGraph;
24 changes: 14 additions & 10 deletions components/ui-comps/TabbedComponents.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
// TabWrapper.jsx
import React, { useState, useEffect } from 'react';
import React, { useState } from 'react';
import PropTypes from 'prop-types';

const TabWrapper = ({ children }) => {
const [activeTab, setActiveTab] = useState(1);
const [activeTab, setActiveTab] = useState(0);

const handleTabChange = (index) => {
setActiveTab(index);
Expand All @@ -13,22 +12,28 @@ const TabWrapper = ({ children }) => {
<div className="tab-wrapper">
<div className="tab-header">
{React.Children.map(children, (child, index) =>
React.cloneElement(child as React.ReactElement<any>, {
React.cloneElement(child, {
isActive: index === activeTab,
onTabClick: () => handleTabChange(index),
})
)}
</div>
<div className="tab-content">
{(React.Children.toArray(children)[activeTab] as React.ReactElement<any>).props.children}
{React.Children.map(children, (child, index) =>
index === activeTab ? child.props.children : null
)}
</div>
</div>
);
};

TabWrapper.propTypes = {
children: PropTypes.node.isRequired,
};

export default TabWrapper;

export const Tabs = ({ title, isActive, onTabClick, children }) => {
export const Tabs = ({ title, isActive, onTabClick }) => {
return (
<div className={`tab ${isActive ? 'active' : ''}`} onClick={onTabClick}>
{title}
Expand All @@ -38,7 +43,6 @@ export const Tabs = ({ title, isActive, onTabClick, children }) => {

Tabs.propTypes = {
title: PropTypes.string.isRequired,
isActive: PropTypes.bool,
onTabClick: PropTypes.func,
content: PropTypes.node,
};
isActive: PropTypes.bool.isRequired,
onTabClick: PropTypes.func.isRequired,
};
27 changes: 16 additions & 11 deletions components/utils/loadGraphFromScaffNet.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { MultiDirectedGraph } from "graphology";
import { randomInt } from "mathjs";

function colorOfEdge(edge) {
function colorOfEdge(edge: string) {
if (edge === "Fragment") {
return "#99ccff"; // Muted Blue
} else if (edge === "Generic") {
Expand All @@ -18,21 +17,26 @@ function colorOfEdge(edge) {
export default function loadGraphFromScaffNet(
network: any,
smilesList: string[],
rdkit: any,
svgSize = 120
) {
const graph = new MultiDirectedGraph();
for (let i = 0; i < network.nodes.size(); i++) {
const smiles_string = network.nodes.get(i);
const mol = rdkit.get_mol(smiles_string);
const svg_string = mol.get_svg(svgSize, svgSize);
const blob_link = new Blob([svg_string], { type: "image/svg+xml" });
mol.delete();


try {
graph.addNode(i.toString(), {
x: randomInt(0, 100),
y: randomInt(0, 100),
molCounts: network.molCounts.get(i),
size: network.molCounts.get(i),
smiles: network.nodes.get(i),
nodeType: smilesList.includes(network.nodes.get(i))
? "whole"
: "fragment",
type: "image",
image: "./logo.svg",
// size: 4,
smiles: smiles_string,
nodeType: smilesList.includes(smiles_string) ? "whole" : "fragment",
image: URL.createObjectURL(blob_link),
// color: smilesList.includes(network.nodes.get(i)) ? "grey" : "blue",
});
} catch {
console.error("Error in adding node to graph");
Expand All @@ -54,5 +58,6 @@ export default function loadGraphFromScaffNet(
console.error("Error in adding edge to graph");
}
}

return graph;
}
Loading

0 comments on commit ae2aa23

Please sign in to comment.