diff --git a/README.md b/README.md index 680c192..de6d6b6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # This is git repository for the Cytoscape 3 clustering app: clusterMaker + TODO: 1) Fix BiClusterView 2) Add support for BiClustering routines: @@ -11,6 +12,34 @@ TODO: + + +7 JUNE - 23 AUGUST 2021 Google Summer of Code by Maija Utriainen + +Implementing remote dimensionality reduction techniques using similar approach to last years remote network clusterers (see below). + - Dimensionality reduction techniques added: Isomap, Local Linear Embedding, MDS, Spectral, tSNE, UMAP + - Changes to ClusterJobExecutionService and RemoteServer: abstracting and replacing code to another class + - Subclasses to ClusterJobHandler specific to network clusterers and dimensionality reduction techniques + - The new algorithms registered in CyActivator + +The added and edited code can be found in clusterMaker2/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ + - utils/remoteUtils + - RemoteServer + - ClusterJobExecutionService + - NetworkClusterJobHandler + - DimensionalityReductionJobHandler + - algorithms/dimensionalityRedcution + - isomap + - linearEmbedding + - mds + - spectral + - tSNERemote + - umap + - CyActivator + + + + 1 JUNE - 29 AUGUST 2020 changes done as a Google Summer of Code Project by Maija Utriainen Implementing new algorithms with a new approach that runs the algorithm remotely on a server instead of in clusterMaker/Cytoscape utilizing interfaces in cytoscape.jobs package. diff --git a/api/api/__init__.py b/api/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/api/api/algorithms/algorithms.py b/api/api/algorithms/algorithms.py new file mode 100644 index 0000000..76ebea2 --- /dev/null +++ b/api/api/algorithms/algorithms.py @@ -0,0 +1,56 @@ +from api.jobs import Jobs +from .fastgreedy import FastGreedy +from .infomap import Infomap +from .leiden import Leiden +from .label_propagation import LabelPropagation +from .leading_eigenvector import LeadingEigenvector +from .multilevel import Multilevel +from .dbscan import DBScan +from .umap import UMAP +from .tsne import TSNEREMOTE +from .isomap import IsoMapEmbedding +from .mds import MDS +from .spectral import SpectralEmbedding +#from .lineardisc import LinearDiscriminant +from .lle import LLE +import falcon +import json + +class Algorithms(object): + jobs = None + algorithms = None + + def __init__(self, jobs: Jobs): + self.jobs = jobs + self.algorithms = {} + + # Initialize the list of algorithms + self.algorithms["leiden"] = Leiden(jobs) + self.algorithms["fastgreedy"] = FastGreedy(jobs) + self.algorithms["infomap"] = Infomap(jobs) + self.algorithms["labelpropagation"] = LabelPropagation(jobs) + self.algorithms["leadingeigenvector"] = LeadingEigenvector(jobs) + self.algorithms["multilevel"] = Multilevel(jobs) + self.algorithms["dbscan"] = DBScan(jobs) + + # Dimensionality reduction (manifold) techniques + self.algorithms["umap"] = UMAP(jobs) + self.algorithms["tsneremote"] = TSNEREMOTE(jobs) + self.algorithms["mds"] = MDS(jobs) + self.algorithms["isomap"] = IsoMapEmbedding(jobs) + self.algorithms["spectral"] = SpectralEmbedding(jobs) + self.algorithms["lle"] = LLE(jobs) + # self.algorithms["lineardisc"] = LinearDiscriminant(jobs) + + def on_get(self, req: falcon.Request, resp: falcon.Response): + resp.code = falcon.HTTP_200 + resp.text = '{"algorithms":'+json.dumps(list(self.algorithms.keys()))+'}' + + + def get_algorithms(self) -> list: + return self.algorithms.keys() + + def get_algorithm(self, algorithm:str): + if algorithm in self.algorithms: + return self.algorithms[algorithm] + return None diff --git a/api/api/algorithms/base_algorithm.py b/api/api/algorithms/base_algorithm.py new file mode 100644 index 0000000..82fe131 --- /dev/null +++ b/api/api/algorithms/base_algorithm.py @@ -0,0 +1,108 @@ +""" +Algorithm base class +""" + +from uuid import UUID +import json + +import falcon +import logging + +from multiprocessing import Manager,Process +from datetime import datetime + +from api.jobs import Jobs +import api.utils as utils + +class BaseAlgorithm(): + jobs = None + myjobs = {} + logFile = None + + def __init__(self, jobs: Jobs): + self.jobs = jobs + self.manager = jobs.get_manager() + + def on_post(self, req: falcon.Request, resp: falcon.Response): + """ Get data and arguments """ + result = self.manager.dict() + status = self.manager.dict() + + #self.logFile.write(repr(req)+"\n") + self.log(repr(req)) + + # Get our parameters + args = self.get_args(req) + self.log("Arguments: "+repr(args)) + #self.logFile.write(repr(args)) + + # We need to do the load here because we can't pass a stream to our + # child process + if hasattr(req.get_param('data'),'file'): + # Python requests adds an extra dict + args['json_data'] = json.load(req.get_param('data').file) + else: + # Assume it's just straight text + args['json_data'] = json.loads(req.get_param('data')) + + #f = open("/var/tmp/wsgi.log", "w") + #f.write(str(args['json_data'])) + + uuid = self.jobs.create_job(req.path, self) + proc = Process(target=self.community_detection, args=(args, status, result)) + self.myjobs[uuid] = (proc, status, result) + proc.start() + resp.code = falcon.HTTP_200 + resp.text = json.dumps({'job_id': str(uuid)}) + + def get_status2(self, uid: UUID) -> dict: + if uid in self.myjobs: + (proc, status, result) = self.myjobs[uid] + json_status = {} + for key, value in status.items(): + json_status[key] = value + + return json_status + return None + + def get_status(self, uid: UUID) -> str: + if uid in self.myjobs: + (proc, status, result) = self.myjobs[uid] + #self.logFile.write("Getting status for "+repr(uid)+"\n") + #self.logFile.write("Status = "+repr(status['status'])+"\n") + self.log("Status for "+repr(uid)+" = "+repr(status['status'])) + return status['status'] + return None + + def fetch_results(self, uid: UUID, req: falcon.Request, resp: falcon.Response): + if uid in self.myjobs: + (proc, status, result) = self.myjobs[uid] + # Add our response + resp.text = utils.get_json_result(status, result) + if status['status'] == 'done': + resp.code = falcon.HTTP_200 + elif status['status'] == 'error': + resp.code = falcon.HTTP_500 + self.log("Returning 500. Response = "+repr(resp.text)) + else: + resp.code = falcon.HTTP_400 + resp.text = str({'error':"no such job: "+str(uid)}) + + def terminate(self, uid: UUID, req: falcon.Request, resp: falcon.Response): + # Terminate the running process + if uid in self.myjobs: + (proc, status, result) = self.myjobs[uid] + proc.terminate() + self.jobs.remove_job(uid) + del self.myjobs[uid] + resp.text = "Job: "+str(uid)+" terminated" + resp.code = falcon.HTTP_200 + else: + resp.code = falcon.HTTP_400 + resp.text = str({'error':"no such job: "+str(uid)}) + + def log(self, message: str): + now = datetime.today() + logging.info(str(now)+": "+message) + + diff --git a/api/api/algorithms/dbscan.py b/api/api/algorithms/dbscan.py new file mode 100644 index 0000000..2090f7c --- /dev/null +++ b/api/api/algorithms/dbscan.py @@ -0,0 +1,74 @@ +""" +DBScan cluster algorithm +""" + +import numpy as np +import falcon +import api.utils as utils +from .base_algorithm import BaseAlgorithm +from sklearn.preprocessing import StandardScaler +from sklearn.cluster import DBSCAN +from scipy.sparse import csgraph + +class DBScan(BaseAlgorithm): + + def get_args(self, req: falcon.Request) -> dict: + """ Get the arguments """ + + # Get our parameters + args = {} + args['eps'] = utils.get_param_as_float(req, 'eps', 0.5) + args['min_samples'] = utils.get_param_as_int(req, 'min_samples', 5) + args['metric'] = utils.get_param_as_string(req, 'metric', 'euclidean') + args['algorithm'] = utils.get_param_as_string(req, 'algorithm', 'auto') + args['leaf_size'] = utils.get_param_as_int(req, 'leaf_size', 30) + args['p'] = utils.get_param_as_float(req, 'p', 2) + + return args + + def community_detection(self, args:dict, status:dict, result:dict): + status['status'] = 'running' + + # Get our parameters + eps = args['eps'] + min_samples = args['min_samples'] + metric = args['metric'] + algorithm = args['algorithm'] + leaf_size = args['leaf_size'] + p = args['p'] + data = args['json_data'] + + # We handle two different types of data -- either a square matrix with + # weights in the cells or a matrix with columns representing features + # If the metric is 'precomputed' then we're dealing with a square matrix + # otherwise, we assume we have a standard column matrix + if metric == 'precomputed': + # Get the graph + graph = utils.get_graph(data) + # Get the matrix from the graph + data = graph.get_adjacency_sparse(attribute="weights") + else: + df = utils.get_matrix(data) + columns = df.columns.to_list() + + # We need to drop any NaNs + df = df.dropna() + + data = df[columns[1:]].values # skip over the label and just pull the data + + if (args['scale']): + data = StandardScaler().fit_transform(data) + + + db = DBSCAN(eps=eps, min_samples=min_samples, metric=metric,metric_params=None, + algorithm=algorithm,leaf_size=leaf_size,p=p,n_jobs=None).fit(data) + + core_samples_mask = np.zeros_like(db.labels_, dtype=bool) + core_samples_mask[db.core_sample_indices_] = True + # List of labels -- each element refers to a row + labels = db.labels_ + + result['partitions'] = labels + + status['status'] = 'done' + diff --git a/api/api/algorithms/fastgreedy.py b/api/api/algorithms/fastgreedy.py new file mode 100644 index 0000000..ec7d184 --- /dev/null +++ b/api/api/algorithms/fastgreedy.py @@ -0,0 +1,38 @@ +""" +Fast Greedy cluster algorithm +""" + +import falcon +import logging +import api.utils as utils +from api.jobs import Jobs +from .base_algorithm import BaseAlgorithm + +class FastGreedy(BaseAlgorithm): + + def get_args(self, req: falcon.Request) -> dict: + return {} + + def community_detection(self, args:dict, status:dict, result:dict): + status['status'] = 'running' + + # Get our parameters + data = args['json_data'] + + # Get our data file + graph = utils.get_graph(data) + + try: + # FastGreedy doesn't work for multigraphs + graph = graph.simplify(multiple=True, loops=True, combine_edges=sum); + part = graph.community_fastgreedy(weights="weights") + except Exception as e: + exc = utils.parse_igraph_exception(repr(e)) + status['status'] = 'error' + status['message'] = exc + return + + result['partitions'] = utils.get_vertex_list(graph, part.as_clustering()) + + status['status'] = 'done' + diff --git a/api/api/algorithms/infomap.py b/api/api/algorithms/infomap.py new file mode 100644 index 0000000..95a146a --- /dev/null +++ b/api/api/algorithms/infomap.py @@ -0,0 +1,34 @@ +""" +Infomap cluster algorithm +""" +import falcon +import api.utils as utils +from .base_algorithm import BaseAlgorithm + +class Infomap(BaseAlgorithm): + def get_args(self, req: falcon.Request) -> dict: + args = {} + args['trials'] = utils.get_param_as_int(req, 'trials', 10) + return args + + def community_detection(self, args:dict, status:dict, result:dict): + status['status'] = 'running' + + # Get our parameters + trials = args['trials'] + data = args['json_data'] + + graph = utils.get_graph(data) + + try: + part = graph.community_infomap(edge_weights="weights", trials=trials) + except Exception as e: + exc = utils.parse_igraph_exception(repr(e)) + status['status'] = 'error' + status['message'] = exc + return + + result['partitions'] = utils.get_vertex_list(graph, part) + + status['status'] = 'done' + diff --git a/api/api/algorithms/isomap.py b/api/api/algorithms/isomap.py new file mode 100644 index 0000000..56d961c --- /dev/null +++ b/api/api/algorithms/isomap.py @@ -0,0 +1,77 @@ +""" +IsoMAP dimensionality reduction algorithm +""" + +import falcon +import api.utils as utils +from .base_algorithm import BaseAlgorithm +from sklearn.preprocessing import StandardScaler +from sklearn.manifold import Isomap +import pandas as pd + +class IsoMapEmbedding(BaseAlgorithm): + + def get_args(self, req: falcon.Request) -> dict: + """ Get the arguments """ + + # Get our parameters + args = {} + args['n_neighbors'] = utils.get_param_as_int(req, 'n_neighbors', 5) + args['eigen_solver'] = utils.get_param_as_string(req, 'eigen_solver', 'auto') + args['metric'] = utils.get_param_as_string(req, 'metric', 'minkowski') + args['tol'] = utils.get_param_as_float(req, 'tol', 0) + args['path_method'] = utils.get_param_as_string(req, 'path_method', 'auto') + args['neighbors_algorithm'] = utils.get_param_as_string(req, 'neighbors_algorithm', 'auto') + args['max_iter'] = utils.get_param_as_int(req, 'max_iter', 0) + return args + + # This isn't really a community detection algorithm, but... + def community_detection(self, args:dict, status:dict, result:dict): + status['status'] = 'running' + + # Get our parameters + n_neighbors = args['n_neighbors'] + eigen_solver = args['eigen_solver'] + metric = args['metric'] + tol = args['tol'] + path_method = args['path_method'] + neighbors_algorithm = args['neighbors_algorithm'] + max_iter = args['max_iter'] + data = args['json_data'] + + df = utils.get_matrix(data) + columns = df.columns.to_list() + row_labels = df.index.to_list() + + # We need to drop any NaNs + df = df.dropna() + + data = df[columns[1:]].values # skip over the label and just pull the data + + if max_iter == 0: + max_iter = None + try: + isomap = Isomap(n_neighbors=n_neighbors, n_components=2, eigen_solver=eigen_solver, + tol=tol, path_method=path_method, neighbors_algorithm=neighbors_algorithm, + metric=metric, max_iter=max_iter, n_jobs=10) + embedding = isomap.fit_transform(data) + except Exception as e: + exc = utils.parse_sklearn_exception(repr(e)) + status['status'] = 'error' + status['message'] = exc + return + #print(str(embedding)) + + result_df = pd.DataFrame(embedding, columns=['x','y']) + result_df.insert(0,"Node",row_labels) + #print(str(result_df)) + # result_df[columns[0]] = [str(x) for x in row_labels] + result_list = [] + result_list.append(['Node','x','y']) + for row in result_df.reset_index().values.tolist(): + result_list.append(row[1:]) + + result['embedding'] = result_list + + status['status'] = 'done' + diff --git a/api/api/algorithms/label_propagation.py b/api/api/algorithms/label_propagation.py new file mode 100644 index 0000000..62e5795 --- /dev/null +++ b/api/api/algorithms/label_propagation.py @@ -0,0 +1,33 @@ +""" +Label Propagation cluster algorithm +""" +import falcon +import api.utils as utils +from .base_algorithm import BaseAlgorithm + +class LabelPropagation(BaseAlgorithm): + def get_args(self, req: falcon.Request) -> dict: + """ Get data and arguments """ + return {} + + def community_detection(self, args:dict, status:dict, result:dict): + status['status'] = 'running' + + # Get our parameters + data = args['json_data'] + + # Get our data file + graph = utils.get_graph(data) + + try: + part = graph.community_label_propagation(weights="weights") + except Exception as e: + exc = utils.parse_igraph_exception(repr(e)) + status['status'] = 'error' + status['message'] = exc + return + + result['partitions'] = utils.get_vertex_list(graph, part) + + status['status'] = 'done' + diff --git a/api/api/algorithms/leading_eigenvector.py b/api/api/algorithms/leading_eigenvector.py new file mode 100644 index 0000000..558b8f2 --- /dev/null +++ b/api/api/algorithms/leading_eigenvector.py @@ -0,0 +1,32 @@ +""" +Leading Eigenvector cluster algorithm +""" +import falcon +import api.utils as utils +from .base_algorithm import BaseAlgorithm + +class LeadingEigenvector(BaseAlgorithm): + def get_args(self, req: falcon.Request) -> dict: + return {} + + def community_detection(self, args:dict, status:dict, result:dict): + status['status'] = 'running' + + # Get our parameters + data = args['json_data'] + + # Get our data file + graph = utils.get_graph(data) + + try: + part = graph.community_leading_eigenvector(weights="weights") + except Exception as e: + exc = utils.parse_igraph_exception(repr(e)) + status['status'] = 'error' + status['message'] = exc + return + + result['partitions'] = utils.get_vertex_list(graph, part) + + status['status'] = 'done' + diff --git a/api/api/algorithms/leiden.py b/api/api/algorithms/leiden.py new file mode 100644 index 0000000..ba105c4 --- /dev/null +++ b/api/api/algorithms/leiden.py @@ -0,0 +1,47 @@ +""" +Leiden cluster algorithm +""" + +import falcon +import api.utils as utils +from .base_algorithm import BaseAlgorithm + +class Leiden(BaseAlgorithm): + + def get_args(self, req: falcon.Request) -> dict: + """ Get the arguments """ + + # Get our parameters + args = {} + args['obj_func'] = utils.get_param_as_string(req, 'objective_function', 'modularity') + args['resolution_parameter'] = utils.get_param_as_float(req, 'resolution', 1.0) + args['beta'] = utils.get_param_as_float(req, 'beta', 0.01) + args['iterations'] = utils.get_param_as_int(req, 'iterations', 2) + return args + + def community_detection(self, args:dict, status:dict, result:dict): + status['status'] = 'running' + + # Get our parameters + obj_func = args['obj_func'] + resolution_parameter = args['resolution_parameter'] + beta = args['beta'] + iterations = args['iterations'] + data = args['json_data'] + + graph = utils.get_graph(data) + + try: + part = graph.community_leiden(objective_function=obj_func, weights="weights", + resolution_parameter=resolution_parameter, beta=beta, + n_iterations=iterations) + except Exception as e: + exc = utils.parse_igraph_exception(repr(e)) + status['status'] = 'error' + status['message'] = exc + return + + result['partitions'] = utils.get_vertex_list(graph, part) + + status['status'] = 'done' + diff --git a/api/api/algorithms/lineardisc.py b/api/api/algorithms/lineardisc.py new file mode 100644 index 0000000..4ec7090 --- /dev/null +++ b/api/api/algorithms/lineardisc.py @@ -0,0 +1,67 @@ +""" +MDS (Multidimensional scaling) dimensionality reduction algorithm +""" + +import falcon +import api.utils as utils +from .base_algorithm import BaseAlgorithm +from sklearn.preprocessing import StandardScaler +from sklearn.discriminant_analysis import LocalDiscriminantAnalysis +import pandas as pd + +class LinearDiscriminant(BaseAlgorithm): + + def get_args(self, req: falcon.Request) -> dict: + """ Get the arguments """ + + # Get our parameters + args = {} + args['solver'] = utils.get_param_as_string(req, 'solver', 'svd') + args['shrinkage'] = utils.get_param_as_string(req, 'shrinkage', None) + args['tol'] = utils.get_param_as_float(req, 'tol', 1e-4) + + return args + + # This isn't really a community detection algorithm, but... + def community_detection(self, args:dict, status:dict, result:dict): + status['status'] = 'running' + + # Get our parameters + solver = args['solver'] + shrinkage = args['shrinkage'] + tol = args['tol'] + + data = args['json_data'] + + df = utils.get_matrix(data) + columns = df.columns.to_list() + row_labels = df.index.to_list() + + # We need to drop any NaNs + df = df.dropna() + + data = df[columns[1:]].values # skip over the label and just pull the data + + try: + lda = LinearDiscriminantAnalysis(solver=solver, shrinkage=shrinkage, tol=tol) + embedding = lda.fit_transform(data) + except Exception as e: + exc = utils.parse_sklearn_exception(repr(e)) + status['status'] = 'error' + status['message'] = exc + return + #print(str(embedding)) + + result_df = pd.DataFrame(embedding, columns=['x','y']) + result_df.insert(0,"Node",row_labels) + #print(str(result_df)) + # result_df[columns[0]] = [str(x) for x in row_labels] + result_list = [] + result_list.append(['Node','x','y']) + for row in result_df.reset_index().values.tolist(): + result_list.append(row[1:]) + + result['embedding'] = result_list + + status['status'] = 'done' + diff --git a/api/api/algorithms/lle.py b/api/api/algorithms/lle.py new file mode 100644 index 0000000..f066385 --- /dev/null +++ b/api/api/algorithms/lle.py @@ -0,0 +1,80 @@ +""" +MDS (Multidimensional scaling) dimensionality reduction algorithm +""" + +import falcon +import api.utils as utils +from .base_algorithm import BaseAlgorithm +from sklearn.preprocessing import StandardScaler +from sklearn.manifold import LocallyLinearEmbedding +import pandas as pd + +class LLE(BaseAlgorithm): + + def get_args(self, req: falcon.Request) -> dict: + """ Get the arguments """ + + # Get our parameters + args = {} + args['n_neighbors'] = utils.get_param_as_int(req, 'n_neighbors', 5) + args['reg'] = utils.get_param_as_float(req, 'reg', 1e-3) + args['eigen_solver'] = utils.get_param_as_string(req, 'eigen_solver', 'auto') + args['tol'] = utils.get_param_as_float(req, 'tol', 1e-6) + args['max_iter'] = utils.get_param_as_int(req, 'max_iter', 100) + args['method'] = utils.get_param_as_string(req, 'method', 'standard') + args['hessian_tol'] = utils.get_param_as_float(req, 'hessian_tol', 1e-4) + args['modified_tol'] = utils.get_param_as_float(req, 'modified_tol', 1e-12) + args['neighbors_algorithm'] = utils.get_param_as_string(req, 'neighbors_algorithm', 'auto') + return args + + # This isn't really a community detection algorithm, but... + def community_detection(self, args:dict, status:dict, result:dict): + status['status'] = 'running' + + # Get our parameters + n_neighbors = args['n_neighbors'] + reg = args['reg'] + eigen_solver = args['eigen_solver'] + tol = args['tol'] + max_iter = args['max_iter'] + method = args['method'] + hessian_tol = args['hessian_tol'] + modified_tol = args['modified_tol'] + neighbors_algorithm = args['neighbors_algorithm'] + + data = args['json_data'] + + df = utils.get_matrix(data) + columns = df.columns.to_list() + row_labels = df.index.to_list() + + # We need to drop any NaNs + df = df.dropna() + + data = df[columns[1:]].values # skip over the label and just pull the data + + try: + lle = LocallyLinearEmbedding(n_components=2, n_neighbors=n_neighbors,reg=reg,eigen_solver=eigen_solver, + tol=tol,max_iter=max_iter, method=method, hessian_tol=hessian_tol, + modified_tol=modified_tol, neighbors_algorithm=neighbors_algorithm, n_jobs=10) + embedding = lle.fit_transform(data) + except Exception as e: + exc = utils.parse_sklearn_exception(repr(e)) + status['status'] = 'error' + status['message'] = exc + return + #print(str(embedding)) + + result_df = pd.DataFrame(embedding, columns=['x','y']) + result_df.insert(0,"Node",row_labels) + #print(str(result_df)) + # result_df[columns[0]] = [str(x) for x in row_labels] + result_list = [] + result_list.append(['Node','x','y']) + for row in result_df.reset_index().values.tolist(): + result_list.append(row[1:]) + + result['embedding'] = result_list + + status['status'] = 'done' + diff --git a/api/api/algorithms/mds.py b/api/api/algorithms/mds.py new file mode 100644 index 0000000..1c17f98 --- /dev/null +++ b/api/api/algorithms/mds.py @@ -0,0 +1,70 @@ +""" +MDS (Multidimensional scaling) dimensionality reduction algorithm +""" + +import falcon +import api.utils as utils +from .base_algorithm import BaseAlgorithm +from sklearn.preprocessing import StandardScaler +from sklearn import manifold +import pandas as pd + +class MDS(BaseAlgorithm): + + def get_args(self, req: falcon.Request) -> dict: + """ Get the arguments """ + + # Get our parameters + args = {} + args['metric'] = utils.get_param_as_bool(req, 'metric', True) + args['n_init'] = utils.get_param_as_int(req, 'n_init', 4) + args['max_iter'] = utils.get_param_as_int(req, 'max_iter', 300) + args['eps'] = utils.get_param_as_float(req, 'eps', 1e-3) + args['dissimilarity'] = utils.get_param_as_string(req, 'dissimilarity', 'euclidean') + return args + + # This isn't really a community detection algorithm, but... + def community_detection(self, args:dict, status:dict, result:dict): + status['status'] = 'running' + + # Get our parameters + metric = args['metric'] + n_init = args['n_init'] + max_iter = args['max_iter'] + eps = args['eps'] + dissimilarity = args['dissimilarity'] + data = args['json_data'] + + df = utils.get_matrix(data) + columns = df.columns.to_list() + row_labels = df.index.to_list() + + # We need to drop any NaNs + df = df.dropna() + + data = df[columns[1:]].values # skip over the label and just pull the data + + try: + mds = manifold.MDS(n_components=2, n_init=n_init, eps=eps, dissimilarity=dissimilarity, + metric=metric, max_iter=max_iter, n_jobs=10) + embedding = mds.fit_transform(data) + except Exception as e: + exc = utils.parse_sklearn_exception(repr(e)) + status['status'] = 'error' + status['message'] = exc + return + #print(str(embedding)) + + result_df = pd.DataFrame(embedding, columns=['x','y']) + result_df.insert(0,"Node",row_labels) + #print(str(result_df)) + # result_df[columns[0]] = [str(x) for x in row_labels] + result_list = [] + result_list.append(['Node','x','y']) + for row in result_df.reset_index().values.tolist(): + result_list.append(row[1:]) + + result['embedding'] = result_list + + status['status'] = 'done' + diff --git a/api/api/algorithms/multilevel.py b/api/api/algorithms/multilevel.py new file mode 100644 index 0000000..aebef94 --- /dev/null +++ b/api/api/algorithms/multilevel.py @@ -0,0 +1,32 @@ +""" +Leiden cluster algorithm +""" +import falcon +import api.utils as utils +from .base_algorithm import BaseAlgorithm + +class Multilevel(BaseAlgorithm): + def get_args(self, req: falcon.Request) -> dict: + return {} + + def community_detection(self, args:dict, status:dict, result:dict): + status['status'] = 'running' + + # Get our parameters + data = args['json_data'] + + # Get our data file + graph = utils.get_graph(data) + + try: + part = graph.community_multilevel(weights="weights") + except Exception as e: + exc = utils.parse_igraph_exception(repr(e)) + status['status'] = 'error' + status['message'] = exc + return + + result['partitions'] = utils.get_vertex_list(graph, part) + + status['status'] = 'done' + diff --git a/api/api/algorithms/spectral.py b/api/api/algorithms/spectral.py new file mode 100644 index 0000000..844af11 --- /dev/null +++ b/api/api/algorithms/spectral.py @@ -0,0 +1,68 @@ +""" +MDS (Multidimensional scaling) dimensionality reduction algorithm +""" + +import falcon +import api.utils as utils +from .base_algorithm import BaseAlgorithm +from sklearn.preprocessing import StandardScaler +from sklearn import manifold +import pandas as pd + +class SpectralEmbedding(BaseAlgorithm): + + def get_args(self, req: falcon.Request) -> dict: + """ Get the arguments """ + + # Get our parameters + args = {} + args['affinity'] = utils.get_param_as_string(req, 'affinity', 'nearest_neighbors') + args['gamma'] = utils.get_param_as_float(req, 'gamma', None) + args['eigen_solver'] = utils.get_param_as_string(req, 'eigen_solver', None) + args['n_neighbors'] = utils.get_param_as_int(req, 'n_neighbors', None) + return args + + # This isn't really a community detection algorithm, but... + def community_detection(self, args:dict, status:dict, result:dict): + status['status'] = 'running' + + # Get our parameters + affinity = args['affinity'] + gamma = args['gamma'] + eigen_solver = args['eigen_solver'] + n_neighbors = args['n_neighbors'] + data = args['json_data'] + + df = utils.get_matrix(data) + columns = df.columns.to_list() + row_labels = df.index.to_list() + + # We need to drop any NaNs + df = df.dropna() + + data = df[columns[1:]].values # skip over the label and just pull the data + + try: + spectral = manifold.SpectralEmbedding(n_components=2, affinity=affinity, gamma=gamma, + eigen_solver=eigen_solver,n_neighbors=n_neighbors, n_jobs=10) + embedding = spectral.fit_transform(data) + except Exception as e: + exc = utils.parse_sklearn_exception(repr(e)) + status['status'] = 'error' + status['message'] = exc + return + #print(str(embedding)) + + result_df = pd.DataFrame(embedding, columns=['x','y']) + result_df.insert(0,"Node",row_labels) + #print(str(result_df)) + # result_df[columns[0]] = [str(x) for x in row_labels] + result_list = [] + result_list.append(['Node','x','y']) + for row in result_df.reset_index().values.tolist(): + result_list.append(row[1:]) + + result['embedding'] = result_list + + status['status'] = 'done' + diff --git a/api/api/algorithms/tsne.py b/api/api/algorithms/tsne.py new file mode 100644 index 0000000..73966e7 --- /dev/null +++ b/api/api/algorithms/tsne.py @@ -0,0 +1,73 @@ +""" +tSNE dimensionality reduction algorithm +""" + +import falcon +import api.utils as utils +from .base_algorithm import BaseAlgorithm +from sklearn.manifold import TSNE +import pandas as pd + +class TSNEREMOTE(BaseAlgorithm): + + def get_args(self, req: falcon.Request) -> dict: + """ Get the arguments """ + + # Get our parameters + args = {} + args['perplexity'] = utils.get_param_as_float(req, 'perplexity', 30.0) + args['early_exaggeration'] = utils.get_param_as_float(req, 'early_exaggeration', 12.0) + args['metric'] = utils.get_param_as_string(req, 'metric', 'euclidean') + args['learning_rate'] = utils.get_param_as_float(req, 'learning_rate', 200.0) + args['n_iter'] = utils.get_param_as_int(req, 'n_iter', 1000) + args['init'] = utils.get_param_as_string(req, 'init', 'pca') + return args + + # This isn't really a community detection algorithm, but... + def community_detection(self, args:dict, status:dict, result:dict): + status['status'] = 'running' + + # Get our parameters + perplexity = args['perplexity'] + early_ex = args['early_exaggeration'] + learning_rate = args['learning_rate'] + n_iter = args['n_iter'] + metric = args['metric'] + init = args['init'].lower() # We're picky about the case for this + data = args['json_data'] + + df = utils.get_matrix(data) + columns = df.columns.to_list() + row_labels = df.index.to_list() + + # We need to drop any NaNs + df = df.dropna() + + data = df.values + try: + tsne = TSNE(n_components=2, perplexity=perplexity,early_exaggeration=early_ex, learning_rate=learning_rate, + metric=metric, init=init, n_jobs=10) + embedding = tsne.fit_transform(data) + except Exception as e: + exc = utils.parse_sklearn_exception(repr(e)) + status['status'] = 'error' + status['message'] = exc + return + + # print(str(embedding)) + + result_df = pd.DataFrame(embedding, columns=['x','y']) + result_df.insert(0,"Node",row_labels) + #print(str(result_df)) + # result_df[columns[0]] = [str(x) for x in row_labels] + result_list = [] + result_list.append(['Node','x','y']) + for row in result_df.reset_index().values.tolist(): + result_list.append(row[1:]) + + result['embedding'] = result_list + + # print(str(result_list)) + + status['status'] = 'done' + diff --git a/api/api/algorithms/umap.py b/api/api/algorithms/umap.py new file mode 100644 index 0000000..fde8af1 --- /dev/null +++ b/api/api/algorithms/umap.py @@ -0,0 +1,69 @@ +""" +UMAP dimensionality reduction algorithm +""" + +import falcon +import api.utils as utils +from .base_algorithm import BaseAlgorithm +from sklearn.preprocessing import StandardScaler +import pandas as pd +import umap + +class UMAP(BaseAlgorithm): + + def get_args(self, req: falcon.Request) -> dict: + """ Get the arguments """ + + # Get our parameters + args = {} + args['n_neighbors'] = utils.get_param_as_int(req, 'n_neighbors', 15) + args['min_dist'] = utils.get_param_as_float(req, 'min_dist', 0.1) + args['metric'] = utils.get_param_as_string(req, 'metric', 'euclidean') + args['scale'] = utils.get_param_as_bool(req, 'scale', False) + return args + + # This isn't really a community detection algorithm, but... + def community_detection(self, args:dict, status:dict, result:dict): + status['status'] = 'running' + + # Get our parameters + n_neighbors = args['n_neighbors'] + min_dist = args['min_dist'] + metric = args['metric'] + data = args['json_data'] + + df = utils.get_matrix(data) + columns = df.columns.to_list() + row_labels = df.index.to_list() + + # We need to drop any NaNs + df = df.dropna() + + data = df[columns[1:]].values # skip over the label and just pull the data + + try: + if (args['scale']): + data = StandardScaler().fit_transform(data) + + reducer = umap.UMAP(n_neighbors=n_neighbors,min_dist=min_dist,metric=metric) + embedding = reducer.fit_transform(data) + except Exception as e: + exc = utils.parse_sklearn_exception(repr(e)) + status['status'] = 'error' + status['message'] = exc + return + #print(str(embedding)) + + result_df = pd.DataFrame(embedding, columns=['x','y']) + result_df.insert(0,"Node",row_labels) + #print(str(result_df)) + # result_df[columns[0]] = [str(x) for x in row_labels] + result_list = [] + result_list.append(['Node','x','y']) + for row in result_df.reset_index().values.tolist(): + result_list.append(row[1:]) + + result['embedding'] = result_list + + status['status'] = 'done' + diff --git a/api/api/app.py b/api/api/app.py new file mode 100644 index 0000000..521b84b --- /dev/null +++ b/api/api/app.py @@ -0,0 +1,44 @@ +import falcon +from falcon_multipart.middleware import MultipartMiddleware +from multiprocessing import Manager +from .algorithms.algorithms import Algorithms +from .jobs import Jobs + +import logging +import sys + +manager = None + +def create_app(mgr: Manager): + logging.basicConfig(filename="/var/tmp/webservices_api.log",level=logging.INFO) + logging.info("Service initialized") + print("Service initialized", file=sys.stderr) + + manager = mgr + app = application = falcon.App(middleware=[MultipartMiddleware()]) + + # Provided for backwards compatibility + #scNetViz = ScNetVizHandler(mgr) + #api.add_route('/scnetviz/api/v1/umap', scNetViz) + #api.add_route('/scnetviz/api/v1/tsne', scNetViz) + #api.add_route('/scnetviz/api/v1/drawgraph', scNetViz) + #api.add_route('/scnetviz/api/v1/louvain', scNetViz) + #api.add_route('/scnetviz/api/v1/leiden', scNetViz) + + # New API + jobs = Jobs(mgr) + app.add_route('/status/{job_id}', jobs) + app.add_route('/status2/{job_id}', jobs) + app.add_route('/fetch/{job_id}', jobs) + app.add_route('/terminate/{job_id}', jobs) + algorithms = Algorithms(jobs) + app.add_route('/services', algorithms) + for algorithm in algorithms.get_algorithms(): + app.add_route('/service/'+algorithm, algorithms.get_algorithm(algorithm)) + + # ChimeraX web services + from . import cxservices + cxservices.add_routes(app) + + return application + diff --git a/api/api/job.py b/api/api/job.py new file mode 100644 index 0000000..bbef789 --- /dev/null +++ b/api/api/job.py @@ -0,0 +1,13 @@ +import uuid + +class Job: + job_uuid = None + algorithm = None + + def __init__(self, job_uuid, algorithm): + self.job_uuid = job_uuid + self.algorithm = algorithm + + def check_job(self): + # Check on the process status + pass diff --git a/api/api/jobs.py b/api/api/jobs.py new file mode 100644 index 0000000..fd3a1d4 --- /dev/null +++ b/api/api/jobs.py @@ -0,0 +1,103 @@ +""" The Jobs module """ +from multiprocessing import Manager +import os +import uuid +import logging +import json + +import falcon + +from .service import Service + +class Jobs: + """ A class to keep track of all running jobs """ + manager = None + + def __init__(self, manager:Manager): + self.active_jobs = {} + self.manager = manager + #logging.basicConfig(filename="/tmp/restLogger.log", level=logging.DEBUG) + + def create_job(self, url: str, service: Service) -> uuid.UUID: + """ Create the job record for an Service instance. This is typically + called by the algorithm upon initiation to register it's instance """ + # Create the uuid + job_uuid = uuid.uuid4() + # Add it to the list + self.active_jobs[job_uuid] = service + logging.info('Created job %s for service %s [%d]'%(str(job_uuid), service, os.getpid())) + return job_uuid + + def remove_job(self, job_uuid: uuid.UUID): + """ Remove the job from our list""" + #logging.info('Removing job %s'%str(job_uuid)) + del self.active_jobs[job_uuid] + + def check_job(self, job_uuid: uuid.UUID) -> str: + """ Check the status of a running job """ + if job_uuid in self.active_jobs: + return self.active_jobs[job_uuid].get_status() + return None + + def on_get(self, req: falcon.Request, resp: falcon.Response, job_id: str): + """ Handles GET requests /status and /fetch """ + path = req.path + #print('path: '+path) + #print('job_id: '+job_id) + logging.info('path: %s, job_id: %s [%d], active_jobs=%d'%(path,job_id,os.getpid(),len(self.active_jobs))) + if path.startswith("/status/"): + uid = get_job_id(job_id) + logging.info('path: %s, job_id: %s [%d], active_jobs=%d'%(path,str(uid),os.getpid(),len(self.active_jobs))) + if uid in self.active_jobs: + resp.code = falcon.HTTP_200 + resp.text = str(self.active_jobs[uid].get_status(uid)) + return + add_error(resp, "No such job") + return + + # New version of status that returns a JSON structure rather than a string so we can return + # error messages. We need to do it this way to preserve backwards compatability + if path.startswith("/status2/"): + uid = get_job_id(job_id) + logging.info('path: %s, job_id: %s [%d], active_jobs=%d'%(path,str(uid),os.getpid(),len(self.active_jobs))) + if uid in self.active_jobs: + resp.code = falcon.HTTP_200 + resp.text = json.dumps(self.active_jobs[uid].get_status2(uid)) + logging.info("status2 response: "+resp.text) + return + add_error(resp, "No such job") + return + + if path.startswith("/fetch/"): + uid = get_job_id(job_id) + if uid in self.active_jobs: + self.active_jobs[uid].fetch_results(uid, req, resp) + return + add_error(resp, "No such job") + return + + if path.startswith("/terminate/"): + uid = get_job_id(job_id) + if uid in self.active_jobs: + self.active_jobs[uid].terminate(uid, req, resp) + return + add_error(resp, "No such job") + return + + if path.startswith("/jobs"): + return + + #print('no matching path') + + def get_manager(self): + return self.manager + +def get_job_id(job_id: str) -> uuid.UUID: + """ Get the job id from a URL """ + return uuid.UUID(job_id) + +def add_error(resp: falcon.Response, error: str): + """ Construct an error return """ + #logging.error('error: %s'%(error)) + resp.status = falcon.HTTP_500 + resp.text = '{"error": '+'"'+error+'"}' diff --git a/api/api/service.py b/api/api/service.py new file mode 100644 index 0000000..af002d2 --- /dev/null +++ b/api/api/service.py @@ -0,0 +1,42 @@ +""" +This is the interface for all cluster algorithms +""" + +import abc +import falcon +from uuid import UUID + +class Service(metaclass=abc.ABCMeta): + """ Interface for all algorithms """ + @classmethod + def __subclasshook__(cls, subclass): + return (hasattr(subclass, 'on_get') and + callable(subclass.on_get) and + hasattr(subclass, 'get_status') and + callable(subclass.get_status) and + hasattr(subclass, 'fetch_results') and + callable(subclass.fetch_results) and + hasattr(subclass, 'terminate') and + callable(subclass.terminate) and + NotImplemented) + + @abc.abstractmethod + def on_post(self, req: falcon.Request, resp: falcon.Response): + """ Called from falcon """ + raise NotImplementedError + + @abc.abstractmethod + def get_status(self, uid: UUID) -> str: + """ Called to retrieve the status of the job """ + raise NotImplementedError + + @abc.abstractmethod + def fetch_results(self, uid: UUID, req: falcon.Request, resp: falcon.Response): + """ Called to fetch the results from the job. Note that we are passed the + Falcon Request and Response objects in order to construct the data """ + raise NotImplementedError + + @abc.abstractmethod + def terminate(self, uid: UUID, req: falcon.Request, resp: falcon.Response): + """ Called to kill the running algorithm """ + raise NotImplementedError diff --git a/api/api/utils.py b/api/api/utils.py new file mode 100644 index 0000000..8894b7e --- /dev/null +++ b/api/api/utils.py @@ -0,0 +1,106 @@ +""" +Various utility functions +""" + +import igraph as ig +import numpy as np +import pandas as pd +import json + + +def get_param_as_string(req, param, default): + if req.has_param(param): + return str(req.get_param(param)) + else: + return default + +def get_param_as_float(req, param, default): + if req.has_param(param): + return req.get_param_as_float(param) + else: + return default + +def get_param_as_int(req, param, default): + if req.has_param(param): + return req.get_param_as_int(param) + else: + return default + +def get_param_as_bool(req, param, default): + if req.has_param(param): + return req.get_param_as_bool(param) + +def get_matrix(json_data: str) -> pd.DataFrame: + """ Convert a json string to a pandas dataframe. + JSON format: + { + columns: [label, col1, col2, col3], + data: [[lbl, val1, val2, val3],[lbl, val1, val2, val3],...] + } + """ + + # Get the columns array + columns = json_data['columns'] + rows = json_data['data'] + + # Create the pandas dataframe + df = pd.DataFrame(data=rows, columns=columns) + df.set_index(columns[0], inplace=True) + df = df.astype(float) + return df + +def get_graph(json_data: str) -> ig.Graph: + """ Convert a json string to a dictionary of + vertices and edges """ + vertices = json_data['nodes'] + edges = json_data['edges'] + ncol = [] + for edge in edges: + ncol.append((edge[0],edge[1],float(edge[2]))) + + g = ig.Graph.TupleList(ncol,edge_attrs="weights") + return g + +def get_json_graph(data: str) -> ig.Graph: + """ Convert a json data stream to a dictionary of + vertices and edges """ + json_data = json.load(data) + return get_graph(json_data) + +def get_json_result(status: dict, result: dict) -> str: + json_data = {} + for key, value in status.items(): + json_data[key] = value + for key, value in result.items(): + json_data[key] = value + + return json.dumps(json_data) + +def get_vertex_list(graph: ig.Graph, vertices: list) -> list: + """ Take a list (or list of lists) of vertex indices and return a + list (or list of lists) of vertex names """ + result = [] + for vals in vertices: + if isinstance(vals, list): + result.append(get_vertex_list(graph, vals)) + elif isinstance(vals, str): + result.append(vals) + elif isinstance(vals, int): + result.append(graph.vs[vals]['name']) + return result + +def parse_sklearn_exception(e: str): + """ Parse out the actual error message from the exception and add + that to the status. The general format of the exception is: + ExceptionType: message""" + parts = e.split(":") + return parts[1].strip() + +def parse_igraph_exception(e: str): + """ Parse out the actual error message from the exception and add + that to the status. The general format of the exception is: + ExceptionType(file:line:' message',)""" + + parts = e[0:len(e)-3].split(":") + return parts[2].strip() + diff --git a/api/v1.wsgi b/api/v1.wsgi new file mode 100755 index 0000000..e4925a4 --- /dev/null +++ b/api/v1.wsgi @@ -0,0 +1,11 @@ +import site +import sys +from importlib import import_module +from multiprocessing import Manager + +if __name__ == '__main__' or __name__.startswith('_mod_wsgi'): + site.addsitedir('/usr/local/www/webservices/wsgi-scripts') + print("v1.wsgi initializing", file=sys.stderr) + manager = Manager() + app = import_module("api.app") + application = app.create_app(manager) diff --git a/collinsPlus.cys b/collinsPlus.cys new file mode 100644 index 0000000..7657023 Binary files /dev/null and b/collinsPlus.cys differ diff --git a/pom.xml b/pom.xml index 1ecdb24..902e227 100644 --- a/pom.xml +++ b/pom.xml @@ -5,10 +5,10 @@ UTF-8 clusterMaker2 edu.ucsf.rbvi.clusterMaker2.internal - 3.8.0 + 3.9.0 3.8.0 3.0.0-M3 - 4.1.0 + 4.2.1 6.0.0 UTF-8 @@ -18,7 +18,7 @@ bundle ${bundle.symbolicName} - 1.3.1 + 2.3.4 @@ -108,7 +108,7 @@ org.apache.httpcomponents httpclient - 4.5.9 + 4.5.13 true @@ -147,6 +147,13 @@ ${cytoscape.api.version} provided + + org.cytoscape + util-api + ${cytoscape.api.version} + + true + org.cytoscape swing-application-api @@ -247,7 +254,7 @@ org.ops4j.pax.logging pax-logging-api - 1.5.2 + 1.11.13 provided @@ -271,7 +278,7 @@ org.ojalgo ojalgo - 45.1.0 + 51.3.0 true diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ClusterManagerImpl.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ClusterManagerImpl.java index df5f75f..e0c2ede 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ClusterManagerImpl.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ClusterManagerImpl.java @@ -117,15 +117,18 @@ public void addAlgorithm(ClusterTaskFactory alg) { case NETWORK: networkClusterIndex += 1.0; props.setProperty(MENU_GRAVITY, ""+networkClusterIndex); + props.setProperty(PREFERRED_MENU, "Apps.clusterMaker Cluster Network"); break; case ATTRIBUTE: attributeClusterIndex += 1.0; props.setProperty(MENU_GRAVITY, ""+attributeClusterIndex); + props.setProperty(PREFERRED_MENU, "Apps.clusterMaker Cluster Attributes"); break; case FILTER: filterIndex += 1.0; + props.setProperty(PREFERRED_MENU, "Apps.clusterMaker Filter Clusters"); props.setProperty(MENU_GRAVITY, ""+filterIndex); break; @@ -171,15 +174,17 @@ public void addVisualizer(ClusterVizFactory viz) { // Create our wrapper and register the algorithm Properties props = new Properties(); - props.setProperty(COMMAND, viz.getShortName()); - props.setProperty(COMMAND_DESCRIPTION, viz.getName()); - props.setProperty(COMMAND_NAMESPACE, "clusterviz"); + if (viz.getShortName() != null) { + props.setProperty(COMMAND, viz.getShortName()); + props.setProperty(COMMAND_DESCRIPTION, viz.getName()); + props.setProperty(COMMAND_NAMESPACE, "clusterviz"); + props.setProperty(COMMAND_LONG_DESCRIPTION, viz.getLongDescription()); + props.setProperty(COMMAND_EXAMPLE_JSON, viz.getExampleJSON()); + props.setProperty(COMMAND_SUPPORTS_JSON, viz.getSupportsJSON()); + } props.setProperty(IN_MENU_BAR, "true"); props.setProperty(PREFERRED_MENU, "Apps.clusterMaker Visualizations"); props.setProperty(TITLE, viz.getName()); - props.setProperty(COMMAND_LONG_DESCRIPTION, viz.getLongDescription()); - props.setProperty(COMMAND_EXAMPLE_JSON, viz.getExampleJSON()); - props.setProperty(COMMAND_SUPPORTS_JSON, viz.getSupportsJSON()); vizClusterIndex += 1.0; props.setProperty(MENU_GRAVITY, ""+vizClusterIndex); serviceRegistrar.registerService(viz, TaskFactory.class, props); @@ -219,15 +224,18 @@ public void addRanking(RankFactory rankFactory) { rankMap.put(rankFactory.getName(), rankFactory); Properties props = new Properties(); - props.setProperty(COMMAND, rankFactory.getShortName()); - props.setProperty(COMMAND_NAMESPACE, "clusterrank"); - props.setProperty(COMMAND_DESCRIPTION, rankFactory.getName()); + if (rankFactory.getShortName() != null) { + props.setProperty(COMMAND, rankFactory.getShortName()); + props.setProperty(COMMAND_NAMESPACE, "clusterrank"); + props.setProperty(COMMAND_DESCRIPTION, rankFactory.getName()); + props.setProperty(COMMAND_LONG_DESCRIPTION, rankFactory.getLongDescription()); + props.setProperty(COMMAND_EXAMPLE_JSON, rankFactory.getExampleJSON()); + props.setProperty(COMMAND_SUPPORTS_JSON, rankFactory.getSupportsJSON()); + } + props.setProperty(IN_MENU_BAR, "true"); props.setProperty(PREFERRED_MENU, "Apps.clusterMaker Ranking"); props.setProperty(TITLE, rankFactory.getName()); - props.setProperty(COMMAND_LONG_DESCRIPTION, rankFactory.getLongDescription()); - props.setProperty(COMMAND_EXAMPLE_JSON, rankFactory.getExampleJSON()); - props.setProperty(COMMAND_SUPPORTS_JSON, rankFactory.getSupportsJSON()); rankingIndex += 1.0; props.setProperty(MENU_GRAVITY, ""+rankingIndex); serviceRegistrar.registerService(rankFactory, TaskFactory.class, props); diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/CyActivator.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/CyActivator.java index 2e61886..fc5b9fa 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/CyActivator.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/CyActivator.java @@ -14,11 +14,13 @@ import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.attributeClusterers.kmeans.KMeansTaskFactory; import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.attributeClusterers.kmedoid.KMedoidTaskFactory; import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.attributeClusterers.pam.PAMTaskFactory; + import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.clusterFilters.BestNeighbor.BestNeighborFilterTaskFactory; import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.clusterFilters.CuttingEdge.CuttingEdgeFilterTaskFactory; import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.clusterFilters.Density.DensityFilterTaskFactory; import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.clusterFilters.FilterTaskFactory; import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.clusterFilters.HairCut.HairCutFilterTaskFactory; + import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.AP.APClusterTaskFactory; import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.ConnectedComponents.ConnectedComponentsTaskFactory; import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.FCM.FCMClusterTaskFactory; @@ -35,17 +37,26 @@ import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.SCPS.SCPSClusterTaskFactory; import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.TransClust.TransClustClusterTaskFactory; import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.Leiden.LeidenClusterTaskFactory; -import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.pca.PCAMenuTaskFactory; -import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.pca.PCATaskFactory; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.DBScan.DBScanClusterTaskFactory; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.ranking.NodeRankingTaskFactory; import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.ranking.HITS.HITSTaskFactory; import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.ranking.MAA.MAATaskFactory; import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.ranking.MAM.MAMTaskFactory; import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.ranking.PR.PRTaskFactory; import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.ranking.PRWP.PRWPTaskFactory; -import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.tSNEWrapper.tSNETaskFactory; -import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.pcoa.PCoATaskFactory; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.DimensionalityReductionTaskFactory; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.isomap.IsomapTaskFactory; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.linearEmbedding.LocalLinearEmbeddingTaskFactory; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.mds.MDSTaskFactory; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.pca.PCATaskFactory; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.tSNEWrapper.tSNETaskFactory; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.umap.UMAPTaskFactory; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.pcoa.PCoATaskFactory; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.spectral.SpectralTaskFactory; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.tSNERemote.tSNERemoteTaskFactory; + import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterTaskFactory; import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterVizFactory; import edu.ucsf.rbvi.clusterMaker2.internal.api.RankFactory; @@ -143,10 +154,10 @@ public void start(BundleContext bc) { registerService(bc, new PAMTaskFactory(clusterManager), ClusterTaskFactory.class, new Properties()); // FIXME: FFT is seriously broken! - registerService(bc, new FFTTaskFactory(clusterManager), - ClusterTaskFactory.class, new Properties()); - registerService(bc, new DBSCANTaskFactory(clusterManager), - ClusterTaskFactory.class, new Properties()); + // registerService(bc, new FFTTaskFactory(clusterManager), + // ClusterTaskFactory.class, new Properties()); + // registerService(bc, new DBSCANTaskFactory(clusterManager), + // ClusterTaskFactory.class, new Properties()); /* * Hold off on these until we get improve the performance sufficiently * to allow them to be useful @@ -155,8 +166,8 @@ public void start(BundleContext bc) { // ClusterTaskFactory.class, new Properties()); // registerService(bc, new BiMineTaskFactory(clusterManager), // ClusterTaskFactory.class, new Properties()); - registerService(bc, new ChengChurchTaskFactory(clusterManager), - ClusterTaskFactory.class, new Properties()); + // registerService(bc, new ChengChurchTaskFactory(clusterManager), + // ClusterTaskFactory.class, new Properties()); // Network clusterers registerService(bc, new NetworkClusterTaskFactory(clusterManager), @@ -165,6 +176,8 @@ public void start(BundleContext bc) { ClusterTaskFactory.class, new Properties()); registerService(bc, new AutoSOMETaskFactory(clusterManager, false), ClusterTaskFactory.class, new Properties()); + registerService(bc, new FastGreedyTaskFactory(clusterManager, registrar), + ClusterTaskFactory.class, new Properties()); registerService(bc, new FuzzifierTaskFactory(clusterManager), ClusterTaskFactory.class, new Properties()); registerService(bc, new GLayClusterTaskFactory(clusterManager), @@ -173,28 +186,30 @@ public void start(BundleContext bc) { ClusterTaskFactory.class, new Properties()); registerService(bc, new FCMClusterTaskFactory(clusterManager), ClusterTaskFactory.class, new Properties()); + registerService(bc, new InfomapTaskFactory(clusterManager, registrar), + ClusterTaskFactory.class, new Properties()); + registerService(bc, new LeidenClusterTaskFactory(clusterManager, registrar), + ClusterTaskFactory.class, new Properties()); + //registerService(bc, new DBScanClusterTaskFactory(clusterManager, registrar), + // ClusterTaskFactory.class, new Properties()); + registerService(bc, new LabelPropagationTaskFactory(clusterManager, registrar), + ClusterTaskFactory.class, new Properties()); + registerService(bc, new LEVClusterTaskFactory(clusterManager, registrar), + ClusterTaskFactory.class, new Properties()); registerService(bc, new MCLClusterTaskFactory(clusterManager), ClusterTaskFactory.class, new Properties()); registerService(bc, new MCODEClusterTaskFactory(clusterManager), ClusterTaskFactory.class, new Properties()); + registerService(bc, new MultilevelClusterTaskFactory(clusterManager, registrar), + ClusterTaskFactory.class, new Properties()); registerService(bc, new SCPSClusterTaskFactory(clusterManager), ClusterTaskFactory.class, new Properties()); registerService(bc, new TransClustClusterTaskFactory(clusterManager), ClusterTaskFactory.class, new Properties()); - registerService(bc, new LeidenClusterTaskFactory(clusterManager, registrar), - ClusterTaskFactory.class, new Properties()); - registerService(bc, new InfomapTaskFactory(clusterManager, registrar), - ClusterTaskFactory.class, new Properties()); - registerService(bc, new FastGreedyTaskFactory(clusterManager, registrar), - ClusterTaskFactory.class, new Properties()); - registerService(bc, new LabelPropagationTaskFactory(clusterManager, registrar), - ClusterTaskFactory.class, new Properties()); - registerService(bc, new LEVClusterTaskFactory(clusterManager, registrar), - ClusterTaskFactory.class, new Properties()); - registerService(bc, new MultilevelClusterTaskFactory(clusterManager, registrar), - ClusterTaskFactory.class, new Properties()); // Cluster ranking + registerService(bc, new NodeRankingTaskFactory(clusterManager), + RankFactory.class, new Properties()); registerService(bc, new MAATaskFactory(clusterManager), RankFactory.class, new Properties()); registerService(bc, new MAMTaskFactory(clusterManager), RankFactory.class, new Properties()); registerService(bc, new PRWPTaskFactory(clusterManager), RankFactory.class, new Properties()); @@ -216,6 +231,8 @@ public void start(BundleContext bc) { // registerService(bc, new UITaskFactory(clusterManager), ClusterTaskFactory.class, new Properties()); // Visualizations + registerService(bc, new VizFactory(clusterManager), ClusterVizFactory.class, + new Properties()); registerService(bc, new NewNetworkViewFactory(clusterManager, false), ClusterVizFactory.class, new Properties()); registerService(bc, new NewNetworkViewFactory(clusterManager, true), ClusterVizFactory.class, @@ -224,8 +241,8 @@ public void start(BundleContext bc) { new Properties()); registerService(bc, new KnnViewTaskFactory(clusterManager), ClusterVizFactory.class, new Properties()); - registerService(bc, new BiclusterViewTaskFactory(clusterManager), ClusterVizFactory.class, - new Properties()); + // registerService(bc, new BiclusterViewTaskFactory(clusterManager), ClusterVizFactory.class, + // new Properties()); registerService(bc, new TreeViewTaskFactory(clusterManager), ClusterVizFactory.class, new Properties()); registerService(bc, new CreateResultsPanelTaskFactory(clusterManager,true), @@ -238,10 +255,18 @@ public void start(BundleContext bc) { ClusterVizFactory.class, new Properties()); // Dimensionality Reduction - // registerService(bc, new PCAMenuTaskFactory(clusterManager), ClusterTaskFactory.class, new Properties()); + registerService(bc, new DimensionalityReductionTaskFactory(clusterManager), + ClusterTaskFactory.class, new Properties()); + registerService(bc, new IsomapTaskFactory(clusterManager, registrar), ClusterTaskFactory.class, new Properties()); + registerService(bc, new LocalLinearEmbeddingTaskFactory(clusterManager, registrar), + ClusterTaskFactory.class, new Properties()); + registerService(bc, new MDSTaskFactory(clusterManager, registrar), ClusterTaskFactory.class, new Properties()); registerService(bc, new PCATaskFactory(clusterManager), ClusterTaskFactory.class, new Properties()); registerService(bc, new PCoATaskFactory(clusterManager), ClusterTaskFactory.class, new Properties()); + registerService(bc, new SpectralTaskFactory(clusterManager, registrar), ClusterTaskFactory.class, new Properties()); registerService(bc, new tSNETaskFactory(clusterManager), ClusterTaskFactory.class, new Properties()); + registerService(bc, new tSNERemoteTaskFactory(clusterManager, registrar), ClusterTaskFactory.class, new Properties()); + registerService(bc, new UMAPTaskFactory(clusterManager, registrar), ClusterTaskFactory.class, new Properties()); { // Link Network Selections diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/FuzzyNodeCluster.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/FuzzyNodeCluster.java index 2105cdf..2e7a4e3 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/FuzzyNodeCluster.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/FuzzyNodeCluster.java @@ -21,22 +21,23 @@ */ public class FuzzyNodeCluster extends NodeCluster { + static int clusterCount = 0; + static int maxClusterNumber = 0; private HashMap membershipMap = null; - public static int fuzzyClusterCount = 0; public FuzzyNodeCluster() { super(); - fuzzyClusterCount++; - clusterNumber = fuzzyClusterCount; - + clusterCount++; + maxClusterNumber = Math.max(clusterNumber, maxClusterNumber); } public FuzzyNodeCluster(Collection collection, HashMap clusterMemberships) { super(collection); + clusterCount++; + maxClusterNumber = Math.max(clusterNumber, maxClusterNumber); + membershipMap = new HashMap(); - fuzzyClusterCount++; - clusterNumber = fuzzyClusterCount; for(CyNode element : clusterMemberships.keySet()){ @@ -47,15 +48,29 @@ public FuzzyNodeCluster(Collection collection, HashMap c } - public boolean add(ListnodeList, int index, double membershipValue) { + public static void reset() { + clusterCount = 0; + maxClusterNumber = 0; + } + public boolean add(ListnodeList, int index, double membershipValue) { boolean retval = add(nodeList.get(index)); if(retval){ membershipMap.put(nodeList.get(index), membershipValue); } return retval; } - public static void init() { fuzzyClusterCount = 0; hasScore = false; } + + public boolean add(CyNode node, double membershipValue) { + if (membershipMap == null) + membershipMap = new HashMap(); + + boolean retval = add(node); + if (retval) + membershipMap.put(node, membershipValue); + return retval; + } + public Double getMembership(CyNode node){ return membershipMap.get(node); diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/NodeCluster.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/NodeCluster.java index 4557845..c328037 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/NodeCluster.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/NodeCluster.java @@ -23,6 +23,7 @@ public class NodeCluster extends ArrayList { private int rank = -1; private double rankScore = 0; static int clusterCount = 0; + static int maxClusterNumber = 0; static boolean hasScore = false; protected double score = 0.0; private HashMap nodeScores; @@ -34,19 +35,35 @@ public NodeCluster() { super(); clusterCount++; clusterNumber = clusterCount; + maxClusterNumber = Math.max(clusterNumber, maxClusterNumber); + // System.out.println("maxClusterNumber = "+maxClusterNumber+", clusterNumber = "+clusterNumber); } public NodeCluster(int clusterNumber, Collection collection) { super(collection); this.clusterNumber = clusterNumber; + maxClusterNumber = Math.max(clusterNumber, maxClusterNumber); + // System.out.println("maxClusterNumber = "+maxClusterNumber+", clusterNumber = "+clusterNumber); } public NodeCluster(Collection collection) { super(collection); clusterCount++; clusterNumber = clusterCount; + maxClusterNumber = Math.max(clusterNumber, maxClusterNumber); + // System.out.println("maxClusterNumber = "+maxClusterNumber+", clusterNumber = "+clusterNumber); } + public static void reset() { + hasScore = false; + clusterCount = 0; + maxClusterNumber = 0; + } + + public static int getMaxClusterNumber() { + return maxClusterNumber; + } + public boolean add(CyNode node) { return super.add(node); } diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/attributeClusterers/AttributeList.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/attributeClusterers/AttributeList.java index 2ffe788..68d1482 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/attributeClusterers/AttributeList.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/attributeClusterers/AttributeList.java @@ -63,7 +63,7 @@ public class AttributeList { @Tunable(description="Edges are assymetric (not common)", longDescription="Under rare circumstances, the edges might be assymetric (i.e. directed)", exampleStringValue="false", - groups="Array sources", gravity=52) + groups="Array sources", dependsOn="edgeAttributeList!=", gravity=52) public boolean assymetric = false; public AttributeList(CyNetwork network) { diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/attributeClusterers/autosome/AutoSOMECluster.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/attributeClusterers/autosome/AutoSOMECluster.java index a0116fa..1dc2495 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/attributeClusterers/autosome/AutoSOMECluster.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/attributeClusterers/autosome/AutoSOMECluster.java @@ -210,7 +210,7 @@ public void run(TaskMonitor monitor) { // updateParams(network, params); if (context.showViz) { - if (heatmap) + if (context.dataVisualization.getSelectedValue().equals("Heatmap")) insertTasksAfterCurrentTask(new KnnView(clusterManager)); else insertTasksAfterCurrentTask(new NewNetworkView(network, clusterManager, true, false, !context.selectedOnly)); diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/attributeClusterers/autosome/RunAutoSOME.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/attributeClusterers/autosome/RunAutoSOME.java index 52d55ce..6ed9508 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/attributeClusterers/autosome/RunAutoSOME.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/attributeClusterers/autosome/RunAutoSOME.java @@ -51,10 +51,12 @@ import org.cytoscape.model.CyEdge; import org.cytoscape.model.CyNetwork; +import org.cytoscape.model.CyNetworkManager; import org.cytoscape.model.CyNode; import org.cytoscape.model.CyTable; import org.cytoscape.model.CyTableUtil; import org.cytoscape.model.subnetwork.CyRootNetwork; +import org.cytoscape.model.subnetwork.CySubNetwork; import org.cytoscape.work.TaskMonitor; import cern.colt.matrix.tdouble.DoubleMatrix2D; @@ -93,6 +95,7 @@ public class RunAutoSOME { private TaskMonitor monitor; private ClusterManager clusterManager; private CyNetwork network; + private CyNetwork subNetwork; public RunAutoSOME(ClusterManager clusterManager, List dataAttributes, CyNetwork network, Settings settings, TaskMonitor monitor) @@ -297,6 +300,10 @@ private Map getNodeClusters(clusterRun cr, Map getNodeClustersFCN(clusterRun cr, CyMatrix matrix, Settings s){ attrList = new ArrayList(); attrOrderList = new ArrayList(); @@ -308,6 +315,17 @@ private Map getNodeClustersFCN(clusterRun cr, CyMatrix int currClust=-1; NodeCluster nc = new NodeCluster(); + // Create the new network + CyRootNetwork rootNetwork = ((CySubNetwork)network).getRootNetwork(); + subNetwork = rootNetwork.addSubNetwork(); + String name = ModelUtils.getName(network, network); + subNetwork.getRow(subNetwork).set(CyNetwork.NAME, name+" - AutoSOME_FCN"); + // Register the network + CyNetworkManager netManager = clusterManager.getService(CyNetworkManager.class); + netManager.addNetwork(subNetwork); + + + Map storeOrigNodes = new HashMap(); for(int i = 0; i < nodes.size(); i++){ CyNode cn = (CyNode) nodes.get(i); @@ -333,32 +351,43 @@ private Map getNodeClustersFCN(clusterRun cr, CyMatrix String temp = fcn[0]; - //System.out.println(temp); + // System.out.println(temp); String[] tokens = temp.split("_"); StringBuilder sb = new StringBuilder(); for(int j = 0; j < tokens.length-1; j++) sb.append(tokens[j]+"_"); temp = sb.substring(0,sb.length()-1); - CyNode cn = network.addNode(); - network.getRow(cn).set(CyNetwork.NAME, temp); - network.getRow(cn).set(CyRootNetwork.SHARED_NAME, temp); + CyNode cn = subNetwork.addNode(); + subNetwork.getRow(cn).set(CyNetwork.NAME, temp); + subNetwork.getRow(cn).set(CyRootNetwork.SHARED_NAME, temp); nodeOrderList.add(temp); attrList.add(temp+"\t"+currClust); + + ModelUtils.createAndSet(subNetwork, cn, "_FCN_Cluster", currClust, Integer.class, null); + CyNode orig = (CyNode) storeOrigNodes.get(fcn[2]); + ModelUtils.createAndSet(network, orig, "_FCN_Cluster", currClust, Integer.class, null); + //System.out.println("*\t"+cn.getIdentifier()+"\t"+currClust); if(s.FCNrows){ - CyNode orig = (CyNode) storeOrigNodes.get(fcn[2]); + orig = (CyNode) storeOrigNodes.get(fcn[2]); CyTable nodeAttrs = network.getDefaultNodeTable(); Set atts = CyTableUtil.getColumnNames(nodeAttrs); for (String attribute: atts) { Class type = nodeAttrs.getColumn(attribute).getType(); - Object att = nodeAttrs.getRow(orig).getRaw(attribute); + Class elementType = null; + if (type.equals(List.class)) { + elementType = nodeAttrs.getColumn(attribute).getListElementType(); + } + Object att = nodeAttrs.getRow(orig.getSUID()).getRaw(attribute); if(att==null) continue; - nodeAttrs.getRow(cn).set(attribute, att); + + // Create the row, if necessary + ModelUtils.createAndSet(subNetwork, cn, attribute, att, type, elementType); } } @@ -417,8 +446,9 @@ public List getEdges(int MAXEDGES) { String[] fce = (String[]) allEdges.get(j); CyNode c1 = (CyNode) storeNodes.get(fce[0]); CyNode c2 = (CyNode) storeNodes.get(fce[1]); - CyEdge edge = network.addEdge(c1, c2, false); - network.getDefaultEdgeTable().getRow(edge).set(CyNetwork.NAME,fce[2]); + CyEdge edge = subNetwork.addEdge(c1, c2, false); + subNetwork.getDefaultEdgeTable().getRow(edge.getSUID()).set(CyNetwork.NAME,fce[0]+"-"+fce[1]); + ModelUtils.createAndSet(subNetwork, edge, "Confidence", Double.valueOf(fce[2]), Double.class, null); edges.add(edge); } diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/attributeClusterers/pam/PAMClusterer.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/attributeClusterers/pam/PAMClusterer.java index 98e6c50..2f50feb 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/attributeClusterers/pam/PAMClusterer.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/attributeClusterers/pam/PAMClusterer.java @@ -30,7 +30,7 @@ * */ public class PAMClusterer extends AbstractAttributeClusterer { - public static String SHORTNAME="pam"; + public static String SHORTNAME="PAM Cluster"; public static String NAME="Partition Around Medoids (PAM) cluster"; @Tunable (description="Network to cluster", context="nogui") diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/DimensionalityReductionTaskFactory.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/DimensionalityReductionTaskFactory.java new file mode 100644 index 0000000..f0e6f59 --- /dev/null +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/DimensionalityReductionTaskFactory.java @@ -0,0 +1,63 @@ +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction; + +import java.util.Collections; +import java.util.List; + +//Cytoscape imports +import org.cytoscape.model.CyNetwork; +import org.cytoscape.work.TaskIterator; + +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterTaskFactory; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterTaskFactory.ClusterType; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterViz; + +public class DimensionalityReductionTaskFactory implements ClusterTaskFactory { + ClusterManager clusterManager; + + public DimensionalityReductionTaskFactory(ClusterManager clusterManager) { + this.clusterManager = clusterManager; + } + + public String getShortName() {return null; } + public String getName() {return "--- Dimensionality Reduction Algorithms ---";} + + public ClusterViz getVisualizer() { + // return new NewNetworkView(true); + return null; + } + + public boolean isReady() { + return false; + } + + public boolean isAvailable(CyNetwork network) { + return false; + } + + public List getTypeList() { + return Collections.singletonList(ClusterType.DIMRED); + } + + public TaskIterator createTaskIterator() { + // Not sure why we need to do this, but it looks like + // the tunable stuff "remembers" objects that it's already + // processed this tunable. So, we use a copy constructor + return null; + } + + @Override + public String getSupportsJSON() { return "false"; } + + @Override + public String getLongDescription() { return ""; } + + @Override + public String getExampleJSON() { return ""; } + +} + + + + + diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/isomap/Isomap.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/isomap/Isomap.java new file mode 100644 index 0000000..4a4c237 --- /dev/null +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/isomap/Isomap.java @@ -0,0 +1,154 @@ +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.isomap; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.cytoscape.application.CyApplicationManager; +import org.cytoscape.jobs.CyJobData; +import org.cytoscape.jobs.CyJobDataService; +import org.cytoscape.jobs.CyJobExecutionService; +import org.cytoscape.jobs.CyJobManager; +import org.cytoscape.jobs.CyJobStatus; +import org.cytoscape.jobs.SUIDUtil; +import org.cytoscape.jobs.CyJobStatus.Status; +import org.cytoscape.model.CyColumn; +import org.cytoscape.model.CyNetwork; +import org.cytoscape.model.CyNode; +import org.cytoscape.model.CyRow; +import org.cytoscape.model.CyTable; +import org.cytoscape.service.util.CyServiceRegistrar; +import org.cytoscape.work.ContainsTunables; +import org.cytoscape.work.ProvidesTitle; +import org.cytoscape.work.TaskMonitor; +import org.json.simple.JSONArray; + +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.AbstractNetworkClusterer; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; +import edu.ucsf.rbvi.clusterMaker2.internal.ui.NewNetworkView; +import edu.ucsf.rbvi.clusterMaker2.internal.ui.ScatterPlotDialog; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJob; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJobHandler; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.DimensionalityReductionJobHandler; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.RemoteServer; + +public class Isomap extends AbstractNetworkClusterer { + public static String NAME = "Isomap (remote)"; + public static String SHORTNAME = "isomap"; + final CyServiceRegistrar registrar; + public final static String GROUP_ATTRIBUTE = "__IsomapGroups.SUID"; + + @ContainsTunables + public IsomapContext context = null; + + public Isomap(IsomapContext context, ClusterManager manager, CyServiceRegistrar registrar) { + super(manager); + this.context = context; + if (network == null) + network = clusterManager.getNetwork(); + context.setNetwork(network); + this.registrar = registrar; + } + + @Override + public String getShortName() {return SHORTNAME;} + + @Override + @ProvidesTitle + public String getName() {return NAME;} + + @Override + public void run(TaskMonitor taskMonitor) throws Exception { + monitor = taskMonitor; + // Get the execution service + CyJobExecutionService executionService = + registrar.getService(CyJobExecutionService.class, "(title=ClusterJobExecutor)"); + CyApplicationManager appManager = registrar.getService(CyApplicationManager.class); + CyNetwork currentNetwork = appManager.getCurrentNetwork(); //gets the network presented in Cytoscape + + HashMap configuration = new HashMap<>(); + if (context.isSynchronous == true) { + configuration.put("waitTime", -1); + } else { + configuration.put("waitTime", 20); + } + + configuration.put("n_neighbors", context.n_neighbors); + configuration.put("eigen_solver", context.eigen_solver.getSelectedValue()); + configuration.put("metric", context.metric.getSelectedValue()); + configuration.put("tol", context.tol); + configuration.put("path_method", context.path_method.getSelectedValue()); + configuration.put("neighbors_algorithm", context.neighbors_algorithm.getSelectedValue()); + configuration.put("max_iter", context.max_iter); + + + clusterAttributeName = "__isomap"; + List attributes = context.getnodeAttributeList().getSelectedValues(); // rather than get single select attribute, make it multiple select + + CyTable nodeTable = currentNetwork.getDefaultNodeTable(); + List columns = new ArrayList<>(); + columns.add("name"); + columns.addAll(attributes); + + // creating the data itself, values of the columns chosen for each row (node) + List> data = new ArrayList<>(); + HashMap nodeMap = getNetworkNodes(currentNetwork); + + for (Long nodeSUID : nodeMap.keySet()) { + CyRow row = nodeTable.getRow(nodeSUID); + if (context.selectedOnly && !row.get(CyNetwork.SELECTED, Boolean.class)) + continue; + List rowList = new ArrayList<>(); + for (String columnName : columns) { + CyColumn column = nodeTable.getColumn(columnName); + if (row.get(columnName, column.getType()) != null) { + rowList.add(row.get(columnName, column.getType()).toString()); + } else { + rowList.add("0"); + } + } + data.add(rowList); + } + + String basePath = RemoteServer.getBasePath(); + + // Get our initial job + ClusterJob job = (ClusterJob) executionService.createCyJob(SHORTNAME); //creates a new ClusterJob object + // Get the data service + CyJobDataService dataService = job.getJobDataService(); //gets the dataService of the execution service + // Add our data + CyJobData jobData = dataService.addData(null, "columns", columns); + jobData = dataService.addData(jobData, "data", data); + job.storeClusterData(clusterAttributeName, currentNetwork, clusterManager, createGroups, GROUP_ATTRIBUTE, null, getShortName()); + // Create our handler + DimensionalityReductionJobHandler jobHandler = new DimensionalityReductionJobHandler(job, network, context.showScatterPlot); + job.setJobMonitor(jobHandler); + // Submit the job + CyJobStatus exStatus = executionService.executeJob(job, basePath, configuration, jobData); + + CyJobStatus.Status status = exStatus.getStatus(); + System.out.println("Status: " + status); + + if (status == Status.FINISHED) { + jobHandler.loadData(job, taskMonitor); + } else if (status == Status.RUNNING + || status == Status.SUBMITTED + || status == Status.QUEUED) { + CyJobManager manager = registrar.getService(CyJobManager.class); + manager.addJob(job, jobHandler, 5); //this one shows the load button + + } else if (status == Status.ERROR + || status == Status.UNKNOWN + || status == Status.CANCELED + || status == Status.FAILED + || status == Status.TERMINATED + || status == Status.PURGED) { + monitor.setStatusMessage("Job status: " + status); + } + + // Save our SUIDs in case we get saved and restored + SUIDUtil.saveSUIDs(job, currentNetwork, currentNetwork.getNodeList()); + + } +} diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/isomap/IsomapContext.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/isomap/IsomapContext.java new file mode 100644 index 0000000..e1b62e7 --- /dev/null +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/isomap/IsomapContext.java @@ -0,0 +1,150 @@ +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.isomap; + +import org.cytoscape.model.CyNetwork; +import org.cytoscape.work.ContainsTunables; +import org.cytoscape.work.Tunable; +import org.cytoscape.work.swing.TunableUIHelper; +import org.cytoscape.work.util.BoundedDouble; +import org.cytoscape.work.util.ListMultipleSelection; +import org.cytoscape.work.util.ListSingleSelection; + +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.AdvancedProperties; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.attributeClusterers.AttributeList; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.NetworkVizProperties; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.ModelUtils; + +public class IsomapContext { + + CyNetwork network; + TunableUIHelper helper; + + //Tunables + + public ListMultipleSelection nodeAttributeList = null; + @Tunable(description="Node attributes for cluster", groups="Array sources", + longDescription="Select the node table columns to be used for calculating the dimensionality reduction. "+ + "Note that at least 2 node columns are usually required.", + exampleStringValue="gal1RGexp,gal4RGExp,gal80Rexp", + tooltip = "You must choose at least 2 node columns for dimensionality reduction", gravity=50 ) + public ListMultipleSelection getnodeAttributeList() { + if (network != null && nodeAttributeList == null) + nodeAttributeList = ModelUtils.updateNodeAttributeList(network, nodeAttributeList); + return nodeAttributeList; + } + public void setnodeAttributeList(ListMultipleSelection nal) { } + + @Tunable(description="Only use data from selected nodes", groups="Array sources", + longDescription="Only the data from the array sources of the selected nodes will be used.", + exampleStringValue = "false", + gravity = 1.5) + public boolean selectedOnly = false; + + @Tunable(description = "Number of neighbors", + longDescription = "Number of neighbors to consider for each point.", + exampleStringValue = "5", + tooltip = "Number of neighbors to consider for each point.", + groups = {"Isomap Advanced Settings"}, gravity = 66) + public int n_neighbors = 5; + + @Tunable(description = "Eigen solver", + longDescription = "‘auto’ : Attempt to choose the most efficient solver for the given problem.\r\n" + + "‘arpack’ : Use Arnoldi decomposition to find the eigenvalues and eigenvectors.\r\n" + + "‘dense’ : Use a direct solver (i.e. LAPACK) for the eigenvalue decomposition.", + exampleStringValue = "auto", + tooltip = "'auto': Attempt to choose the most efficient solver for the given problem.
" + + "‘arpack’: Use Arnoldi decomposition to find the eigenvalues and eigenvectors.
" + + "‘dense’: Use a direct solver (i.e. LAPACK) for the eigenvalue decomposition.", + groups = {"Isomap Advanced Settings"}, gravity = 67) + public ListSingleSelection eigen_solver = new ListSingleSelection("auto", "arpack", "dense"); + + @Tunable(description = "Convergence tolerance", + longDescription = "Convergence tolerance passed to arpack or lobpcg. not used if eigen_solver == ‘dense’.", + exampleStringValue = "0.0", + tooltip = "Convergence tolerance passed to arpack or lobpcg. Not used if Eigen solver = ‘dense’.", + groups = {"Isomap Advanced Settings"}, gravity = 68) + public double tol = 0.0; + + @Tunable(description = "Path method", + longDescription = "Method to use in finding shortest path.\r\n" + + "‘auto’ : attempt to choose the best algorithm automatically.\r\n" + + "‘FW’ : Floyd-Warshall algorithm.\r\n" + + "‘D’ : Dijkstra’s algorithm.\r\n", + exampleStringValue = "auto", + tooltip = "Method to use in finding shortest path.
" + + "‘auto’ : attempt to choose the best algorithm automatically.
" + + "‘FW’ : Floyd-Warshall algorithm.
" + + "‘D’ : Dijkstra’s algorithm.", + groups = {"Isomap Advanced Settings"}, gravity = 69) + public ListSingleSelection path_method = new ListSingleSelection("auto", "FW", "D"); + + @Tunable(description = "Neighbors algorithm", + longDescription = "Algorithm to use for nearest neighbors search, passed to neighbors. NearestNeighbors instance.", + exampleStringValue = "auto", + tooltip = "Algorithm to use for nearest neighbors search, passed to neighbors.", + groups = {"Isomap Advanced Settings"}, gravity = 70) + public ListSingleSelection neighbors_algorithm = new ListSingleSelection("auto", "brute", "kd_tree", "ball_tree"); + + @Tunable(description = "Metric", + longDescription = "This controls how distance is computed in the ambient space of the input data. By default UMAP supports a wide variety of metrics.", + exampleStringValue = "euclidean", + tooltip = "This controls how distance is computed in the ambient space of the input data.", + groups = {"Isomap Advanced Settings"}, gravity = 71) + public ListSingleSelection metric = new ListSingleSelection("euclidean", "manhattan", "chebyshev", "minkowski", "canberra", "braycurtis", + "haversine", "mahalanobis", "wminkowski", "seuclidean", "cosine", "correlation", "hamming", "jaccard", "dice", "russellrao", "kulsinski", "rogerstanimoto", + "sokalmichener", "sokalsneath", "yule"); + + @Tunable(description = "Maximum iterations", + longDescription = "Maximum number of iterations for the arpack solver. not used if eigen_solver == ‘dense’.", + exampleStringValue = "None", + tooltip = "Maximum number of iterations for the arpack solver. Not used if Eigen solver = ‘dense’.", + groups = {"Isomap Advanced Settings"}, gravity = 72) + public int max_iter; + + @Tunable(description = "Show scatter plot with results", + longDescription = "If this is set to ```true```, show the scatterplot after the calculation is complete", + exampleStringValue = "true", + tooltip = "If this is checked, show the scatterplot after the calculation is complete", + groups = {"Isomap Advanced Settings"}, gravity = 73) + public boolean showScatterPlot = true; + + @Tunable(description = "Synchronous", + longDescription = "If ```false``` the algorithm will run in the background after specified wait time", + exampleStringValue = "true", + tooltip = "If ```false``` the algorithm will run in the background after specified wait time", + groups = {"Isomap Advanced Settings"}, gravity = 74) + public boolean isSynchronous = true; + + public IsomapContext() { + } + + public IsomapContext(IsomapContext origin) { + + nodeAttributeList = origin.nodeAttributeList; + n_neighbors = origin.n_neighbors; + eigen_solver = origin.eigen_solver; + tol = origin.tol; + path_method = origin.path_method; + neighbors_algorithm = origin.neighbors_algorithm; + max_iter = origin.max_iter; + showScatterPlot = origin.showScatterPlot; + isSynchronous = origin.isSynchronous; + metric = origin.metric; + } + + public void setNetwork(CyNetwork network) { + if (this.network != null && this.network.equals(network)) + return; + + this.network = network; + this.nodeAttributeList = null; + } + + public CyNetwork getNetwork() { return network; } + + + public void setUIHelper(TunableUIHelper helper) { + this.helper = helper; + + } + +} diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/isomap/IsomapTaskFactory.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/isomap/IsomapTaskFactory.java new file mode 100644 index 0000000..e1678ad --- /dev/null +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/isomap/IsomapTaskFactory.java @@ -0,0 +1,46 @@ +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.isomap; + +import java.util.Collections; +import java.util.List; + +import org.cytoscape.service.util.CyServiceRegistrar; +import org.cytoscape.work.TaskIterator; + +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.AbstractClusterTaskFactory; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterViz; + +public class IsomapTaskFactory extends AbstractClusterTaskFactory { + IsomapContext context = null; + final CyServiceRegistrar registrar; + + public IsomapTaskFactory(ClusterManager clusterManager, CyServiceRegistrar registrar) { + super(clusterManager); + context = new IsomapContext(); + this.registrar = registrar; + } + + public String getName() {return Isomap.NAME;} + + public String getShortName() {return Isomap.SHORTNAME;} + + @Override + public String getLongDescription() { + return "One of the earliest approaches to manifold learning is the Isomap algorithm, short for Isometric Mapping. Isomap can be viewed as an extension of Multi-dimensional Scaling (MDS) or Kernel PCA. Isomap seeks a lower-dimensional embedding which maintains geodesic distances between all points."; + } + + @Override + public ClusterViz getVisualizer() { + return null; + } + + @Override + public List getTypeList() { + return Collections.singletonList(ClusterType.DIMRED); + } + + @Override + public TaskIterator createTaskIterator() { + return new TaskIterator(new Isomap(context, clusterManager, registrar)); + } +} diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/linearEmbedding/LocalLinearEmbedding.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/linearEmbedding/LocalLinearEmbedding.java new file mode 100644 index 0000000..9e6f1d5 --- /dev/null +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/linearEmbedding/LocalLinearEmbedding.java @@ -0,0 +1,156 @@ +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.linearEmbedding; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.cytoscape.application.CyApplicationManager; +import org.cytoscape.jobs.CyJobData; +import org.cytoscape.jobs.CyJobDataService; +import org.cytoscape.jobs.CyJobExecutionService; +import org.cytoscape.jobs.CyJobManager; +import org.cytoscape.jobs.CyJobStatus; +import org.cytoscape.jobs.SUIDUtil; +import org.cytoscape.jobs.CyJobStatus.Status; +import org.cytoscape.model.CyColumn; +import org.cytoscape.model.CyNetwork; +import org.cytoscape.model.CyNode; +import org.cytoscape.model.CyRow; +import org.cytoscape.model.CyTable; +import org.cytoscape.service.util.CyServiceRegistrar; +import org.cytoscape.work.ContainsTunables; +import org.cytoscape.work.ProvidesTitle; +import org.cytoscape.work.TaskMonitor; +import org.json.simple.JSONArray; + +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.AbstractNetworkClusterer; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; +import edu.ucsf.rbvi.clusterMaker2.internal.ui.NewNetworkView; +import edu.ucsf.rbvi.clusterMaker2.internal.ui.ScatterPlotDialog; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJob; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJobHandler; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.DimensionalityReductionJobHandler; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.RemoteServer; + +public class LocalLinearEmbedding extends AbstractNetworkClusterer { + public static String NAME = "Local Linear Embedding (remote)"; + public static String SHORTNAME = "lle"; + final CyServiceRegistrar registrar; + public final static String GROUP_ATTRIBUTE = "__LocalLinearEmbeddingGroups.SUID"; + + @ContainsTunables + public LocalLinearEmbeddingContext context = null; + + public LocalLinearEmbedding(LocalLinearEmbeddingContext context, ClusterManager manager, CyServiceRegistrar registrar) { + super(manager); + this.context = context; + if (network == null) + network = clusterManager.getNetwork(); + context.setNetwork(network); + this.registrar = registrar; + } + + @Override + public String getShortName() {return SHORTNAME;} + + @Override + @ProvidesTitle + public String getName() {return NAME;} + + @Override + public void run(TaskMonitor taskMonitor) throws Exception { + monitor = taskMonitor; + // Get the execution service + CyJobExecutionService executionService = + registrar.getService(CyJobExecutionService.class, "(title=ClusterJobExecutor)"); + CyApplicationManager appManager = registrar.getService(CyApplicationManager.class); + CyNetwork currentNetwork = appManager.getCurrentNetwork(); //gets the network presented in Cytoscape + + HashMap configuration = new HashMap<>(); + if (context.isSynchronous == true) { + configuration.put("waitTime", -1); + } else { + configuration.put("waitTime", 20); + } + + configuration.put("n_neighbors",context.n_neighbors); + configuration.put("reg",context.reg); + configuration.put("eigen_solver",context.eigen_solver.getSelectedValue()); + configuration.put("tol",context.tol); + configuration.put("max_iter",context.max_iter); + configuration.put("method",context.method.getSelectedValue()); + configuration.put("hessian_tol",context.hessian_tol); + configuration.put("modified_tol",context.modified_tol); + configuration.put("neighbors_algorithm",context.neighbors_algorithm.getSelectedValue()); + + + clusterAttributeName = "__localLinearEmbedding"; + List attributes = context.getnodeAttributeList().getSelectedValues(); // rather than get single select attribute, make it multiple select + + CyTable nodeTable = currentNetwork.getDefaultNodeTable(); + List columns = new ArrayList<>(); + columns.add("name"); + columns.addAll(attributes); + + // creating the data itself, values of the columns chosen for each row (node) + List> data = new ArrayList<>(); + HashMap nodeMap = getNetworkNodes(currentNetwork); + + for (Long nodeSUID : nodeMap.keySet()) { + CyRow row = nodeTable.getRow(nodeSUID); + if (context.selectedOnly && !row.get(CyNetwork.SELECTED, Boolean.class)) + continue; + List rowList = new ArrayList<>(); + for (String columnName : columns) { + CyColumn column = nodeTable.getColumn(columnName); + if (row.get(columnName, column.getType()) != null) { + rowList.add(row.get(columnName, column.getType()).toString()); + } else { + rowList.add("0"); + } + } + data.add(rowList); + } + + String basePath = RemoteServer.getBasePath(); + + // Get our initial job + ClusterJob job = (ClusterJob) executionService.createCyJob(SHORTNAME); //creates a new ClusterJob object + // Get the data service + CyJobDataService dataService = job.getJobDataService(); //gets the dataService of the execution service + // Add our data + CyJobData jobData = dataService.addData(null, "columns", columns); + jobData = dataService.addData(jobData, "data", data); + job.storeClusterData(clusterAttributeName, currentNetwork, clusterManager, createGroups, GROUP_ATTRIBUTE, null, getShortName()); + // Create our handler + DimensionalityReductionJobHandler jobHandler = new DimensionalityReductionJobHandler(job, network, context.showScatterPlot); + job.setJobMonitor(jobHandler); + // Submit the job + CyJobStatus exStatus = executionService.executeJob(job, basePath, configuration, jobData); + + CyJobStatus.Status status = exStatus.getStatus(); + System.out.println("Status: " + status); + + if (status == Status.FINISHED) { + jobHandler.loadData(job, taskMonitor); + } else if (status == Status.RUNNING + || status == Status.SUBMITTED + || status == Status.QUEUED) { + CyJobManager manager = registrar.getService(CyJobManager.class); + manager.addJob(job, jobHandler, 5); //this one shows the load button + + } else if (status == Status.ERROR + || status == Status.UNKNOWN + || status == Status.CANCELED + || status == Status.FAILED + || status == Status.TERMINATED + || status == Status.PURGED) { + monitor.setStatusMessage("Job status: " + status); + } + + // Save our SUIDs in case we get saved and restored + SUIDUtil.saveSUIDs(job, currentNetwork, currentNetwork.getNodeList()); + + } +} diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/linearEmbedding/LocalLinearEmbeddingContext.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/linearEmbedding/LocalLinearEmbeddingContext.java new file mode 100644 index 0000000..a0165dc --- /dev/null +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/linearEmbedding/LocalLinearEmbeddingContext.java @@ -0,0 +1,170 @@ +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.linearEmbedding; + +import org.cytoscape.model.CyNetwork; +import org.cytoscape.work.ContainsTunables; +import org.cytoscape.work.Tunable; +import org.cytoscape.work.swing.TunableUIHelper; +import org.cytoscape.work.util.BoundedDouble; +import org.cytoscape.work.util.ListMultipleSelection; +import org.cytoscape.work.util.ListSingleSelection; + +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.AdvancedProperties; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.attributeClusterers.AttributeList; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.NetworkVizProperties; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.ModelUtils; + +public class LocalLinearEmbeddingContext { + + CyNetwork network; + TunableUIHelper helper; + + //Tunables + + public ListMultipleSelection nodeAttributeList = null; + @Tunable(description="Node attributes for dimensionality reduction", groups="Array sources", + longDescription="Select the node table columns to be used for calculating the dimensionality reduction. "+ + "Note that at least 2 node columns are usually required.", + exampleStringValue="gal1RGexp,gal4RGExp,gal80Rexp", + tooltip = "You must choose at least 2 node columns for dimensionality reduction.", gravity = 65 ) + public ListMultipleSelection getnodeAttributeList() { + if (network != null && nodeAttributeList == null) + nodeAttributeList = ModelUtils.updateNodeAttributeList(network, nodeAttributeList); + return nodeAttributeList; + } + public void setnodeAttributeList(ListMultipleSelection nal) { } + + @Tunable(description="Only use data from selected nodes", groups="Array sources", + longDescription="Only the data from the array sources of the selected nodes will be used.", + exampleStringValue = "false", + gravity = 1.5) + public boolean selectedOnly = false; + + @Tunable(description = "Number of neighbors", + longDescription = "Number of neighbors to consider for each point.", + exampleStringValue = "5", + tooltip = "Number of neighbors to consider for each point.", + groups = {"Local Linear Embedding Advanced Settings"}, gravity = 66) + public int n_neighbors = 5; + + @Tunable(description = "Regularization constant", + longDescription = "Regularization constant, multiplies the trace of the local covariance matrix of the distances.", + exampleStringValue = "1 * 10^(-3)", + tooltip = "Regularization constant, multiplies the trace of the local covariance matrix of the distances.", + groups = {"Local Linear Embedding Advanced Settings"}, gravity = 67) + public double reg = 1 * 10^(-3); + + @Tunable(description = "Eigen solver", + longDescription = "'auto' : algorithm will attempt to choose the best method for input data\r\n" + + "'arpack': use arnoldi iteration in shift-invert mode.\r\n" + + "For this method, M may be a dense matrix, sparse matrix, or general linear operator. " + + "Warning: ARPACK can be unstable for some problems. It is best to try several random seeds in order to check results.\r\n" + + "'dense': use standard dense matrix operations for the eigenvalue\r\n" + + "decomposition. For this method, M must be an array or matrix type. This method should be avoided fo", + exampleStringValue = "auto", + tooltip = "'auto': algorithm will attempt to choose the best method for input data
" + + "'arpack': use arnoldi iteration in shift-invert mode. For this method, M may be a dense matrix, sparse matrix, or general linear operator.
" + + "Warning: ARPACK can be unstable for some problems. It is best to try several random seeds in order to check results.
" + + "'dense': use standard dense matrix operations for the eigenvalue decomposition. For this method, M must be an array or matrix type.", + groups = {"Local Linear Embedding Advanced Settings"}, gravity = 68) + public ListSingleSelection eigen_solver = new ListSingleSelection("auto", "arpack", "dense"); + + @Tunable(description = "Tolerance", + longDescription = "Tolerance for ‘arpack’ method. Not used if eigen_solver==’dense’.", + exampleStringValue = "1 * 10^(-6)", + tooltip = "Tolerance for ‘arpack’ method. Not used if Eigen solver = ’dense’.", + groups = {"Local Linear Embedding Advanced Settings"}, gravity = 69) + public double tol = 1 * 10^(-6); + + @Tunable(description = "Maximum iterations", + longDescription = "Maximum number of iterations for the arpack solver. Not used if eigen_solver==’dense’.", + exampleStringValue = "100", + tooltip = "Maximum number of iterations for the arpack solver. Not used if Eigen solver = ’dense’.", + groups = {"Local Linear Embedding Advanced Settings"}, gravity = 70) + public int max_iter = 100; + + @Tunable(description = "Method", + longDescription = "standard: use the standard locally linear embedding algorithm.\r\n" + + "hessian: use the Hessian eigenmap method. This method requires n_neighbors > n_components * (1 + (n_components + 1) / 2.\r\n" + + "modified: use the modified locally linear embedding algorithm.\r\n" + + "ltsa: use local tangent space alignment algorithm. ", + exampleStringValue = "standard", + tooltip = "'standard': use the standard locally linear embedding algorithm.
" + + "'hessian': use the Hessian eigenmap method. This method requires n_neighbors > n_components * (1 + (n_components + 1) / 2.
" + + "'modified': use the modified locally linear embedding algorithm.
" + + "'ltsa': use local tangent space alignment algorithm.", + groups = {"Local Linear Embedding Advanced Settings"}, gravity = 71) + public ListSingleSelection method = new ListSingleSelection("standard", "hessian", "modified", "ltsa"); + + @Tunable(description = "Hessian tolerance", + longDescription = "Tolerance for Hessian eigenmapping method. Only used if Method == 'hessian'", + exampleStringValue = "1 * 10^(-4)", + tooltip = "Tolerance for Hessian eigenmapping method. Only used if Method = 'hessian'.", + groups = {"Local Linear Embedding Advanced Settings"}, gravity = 72) + public double hessian_tol = 1 * 10^(-4); + + @Tunable(description = "Modified tolerance", + longDescription = "Tolerance for modified LLE method. Only used if Method == 'modified'", + exampleStringValue = "1 * 10^(-12)", + tooltip = "Tolerance for modified LLE method. Only used if Method = 'modified'.", + groups = {"Local Linear Embedding Advanced Settings"}, gravity = 73) + public double modified_tol = 1 * 10^(-12); + + @Tunable(description = "Neighbors algorithm", + longDescription = "Algorithm to use for nearest neighbors search, passed to neighbors. NearestNeighbors instance.", + exampleStringValue = "auto", + tooltip = "Algorithm to use for nearest neighbors search, passed to neighbors.", + groups = {"Local Linear Embedding Advanced Settings"}, gravity = 74) + public ListSingleSelection neighbors_algorithm = new ListSingleSelection("auto", "brute", "kd_tree", "ball_tree"); + + @Tunable(description = "Show scatter plot with results", + longDescription = "If this is set to ```true```, show the scatterplot after the calculation is complete.", + exampleStringValue = "true", + tooltip = "If this is checked, show the scatterplot after the calculation is complete.", + groups = {"Local Linear Embedding Advanced Settings"}, gravity = 75) + public boolean showScatterPlot = true; + + @Tunable(description = "Synchronous", + longDescription = "If ```false``` the algorithm will run in the background after specified wait time", + exampleStringValue = "true", + tooltip = "If ```false``` the algorithm will run in the background after specified wait time", + groups = {"Local Linear Embedding Advanced Settings"}, gravity = 76) + public boolean isSynchronous = false; + + + public LocalLinearEmbeddingContext() { + + } + + public LocalLinearEmbeddingContext(LocalLinearEmbeddingContext origin) { + + nodeAttributeList = origin.nodeAttributeList; + n_neighbors = origin.n_neighbors; + reg = origin.reg; + tol = origin.tol; + eigen_solver = origin.eigen_solver; + max_iter = origin.max_iter; + method = origin.method; + hessian_tol = origin.hessian_tol; + modified_tol = origin.modified_tol; + neighbors_algorithm = origin.neighbors_algorithm; + isSynchronous = origin.isSynchronous; + + } + + public void setNetwork(CyNetwork network) { + if (this.network != null && this.network.equals(network)) + return; + + this.network = network; + this.nodeAttributeList = null; + } + + public CyNetwork getNetwork() { return network; } + + + public void setUIHelper(TunableUIHelper helper) { + this.helper = helper; + + } + +} diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/linearEmbedding/LocalLinearEmbeddingTaskFactory.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/linearEmbedding/LocalLinearEmbeddingTaskFactory.java new file mode 100644 index 0000000..4f4f73b --- /dev/null +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/linearEmbedding/LocalLinearEmbeddingTaskFactory.java @@ -0,0 +1,47 @@ +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.linearEmbedding; + +import java.util.Collections; +import java.util.List; + +import org.cytoscape.service.util.CyServiceRegistrar; +import org.cytoscape.work.TaskIterator; + +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.AbstractClusterTaskFactory; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterTaskFactory; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterViz; + +public class LocalLinearEmbeddingTaskFactory extends AbstractClusterTaskFactory { + LocalLinearEmbeddingContext context = null; + final CyServiceRegistrar registrar; + + public LocalLinearEmbeddingTaskFactory(ClusterManager clusterManager, CyServiceRegistrar registrar) { + super(clusterManager); + context = new LocalLinearEmbeddingContext(); + this.registrar = registrar; + } + + public String getName() {return LocalLinearEmbedding.NAME;} + + public String getShortName() {return LocalLinearEmbedding.SHORTNAME;} + + @Override + public String getLongDescription() { + return "Locally linear embedding (LLE) seeks a lower-dimensional projection of the data which preserves distances within local neighborhoods. It can be thought of as a series of local Principal Component Analyses which are globally compared to find the best non-linear embedding."; + } + + @Override + public ClusterViz getVisualizer() { + return null; + } + + @Override + public List getTypeList() { + return Collections.singletonList(ClusterTaskFactory.ClusterType.DIMRED); + } + + @Override + public TaskIterator createTaskIterator() { + return new TaskIterator(new LocalLinearEmbedding(context, clusterManager, registrar)); + } +} diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/mds/MDS.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/mds/MDS.java new file mode 100644 index 0000000..50b28b6 --- /dev/null +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/mds/MDS.java @@ -0,0 +1,151 @@ +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.mds; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.cytoscape.application.CyApplicationManager; +import org.cytoscape.jobs.CyJobData; +import org.cytoscape.jobs.CyJobDataService; +import org.cytoscape.jobs.CyJobExecutionService; +import org.cytoscape.jobs.CyJobManager; +import org.cytoscape.jobs.CyJobStatus; +import org.cytoscape.jobs.SUIDUtil; +import org.cytoscape.jobs.CyJobStatus.Status; +import org.cytoscape.model.CyColumn; +import org.cytoscape.model.CyNetwork; +import org.cytoscape.model.CyNode; +import org.cytoscape.model.CyRow; +import org.cytoscape.model.CyTable; +import org.cytoscape.service.util.CyServiceRegistrar; +import org.cytoscape.work.ContainsTunables; +import org.cytoscape.work.ProvidesTitle; +import org.cytoscape.work.TaskMonitor; +import org.json.simple.JSONArray; + +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.AbstractNetworkClusterer; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; +import edu.ucsf.rbvi.clusterMaker2.internal.ui.NewNetworkView; +import edu.ucsf.rbvi.clusterMaker2.internal.ui.ScatterPlotDialog; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJob; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJobHandler; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.DimensionalityReductionJobHandler; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.RemoteServer; + +public class MDS extends AbstractNetworkClusterer { + public static String NAME = "MDS (remote)"; + public static String SHORTNAME = "mds"; + final CyServiceRegistrar registrar; + public final static String GROUP_ATTRIBUTE = "__MDSGroups.SUID"; + + @ContainsTunables + public MDSContext context = null; + + public MDS(MDSContext context, ClusterManager manager, CyServiceRegistrar registrar) { + super(manager); + this.context = context; + if (network == null) + network = clusterManager.getNetwork(); + context.setNetwork(network); + this.registrar = registrar; + } + + @Override + public String getShortName() {return SHORTNAME;} + + @Override + @ProvidesTitle + public String getName() {return NAME;} + + @Override + public void run(TaskMonitor taskMonitor) throws Exception { + monitor = taskMonitor; + // Get the execution service + CyJobExecutionService executionService = + registrar.getService(CyJobExecutionService.class, "(title=ClusterJobExecutor)"); + CyApplicationManager appManager = registrar.getService(CyApplicationManager.class); + CyNetwork currentNetwork = appManager.getCurrentNetwork(); //gets the network presented in Cytoscape + // + HashMap configuration = new HashMap<>(); + if (context.isSynchronous == true) { + configuration.put("waitTime", -1); + } else { + configuration.put("waitTime", 20); + } + + configuration.put("metric",context.metric); + configuration.put("n_init",context.n_init); + configuration.put("max_iter",context.max_iter); + configuration.put("eps",context.eps); + configuration.put("dissimilarity",context.dissimilarity.getSelectedValue()); + + + clusterAttributeName = "__mds"; + List attributes = context.getnodeAttributeList().getSelectedValues(); // rather than get single select attribute, make it multiple select + + CyTable nodeTable = currentNetwork.getDefaultNodeTable(); + List columns = new ArrayList<>(); + columns.add("name"); + columns.addAll(attributes); + + // creating the data itself, values of the columns chosen for each row (node) + List> data = new ArrayList<>(); + HashMap nodeMap = getNetworkNodes(currentNetwork); + + for (Long nodeSUID : nodeMap.keySet()) { + CyRow row = nodeTable.getRow(nodeSUID); + if (context.selectedOnly && !row.get(CyNetwork.SELECTED, Boolean.class)) + continue; + List rowList = new ArrayList<>(); + for (String columnName : columns) { + CyColumn column = nodeTable.getColumn(columnName); + if (row.get(columnName, column.getType()) != null) { + rowList.add(row.get(columnName, column.getType()).toString()); + } else { + rowList.add("0"); + } + } + data.add(rowList); + } + + String basePath = RemoteServer.getBasePath(); + + // Get our initial job + ClusterJob job = (ClusterJob) executionService.createCyJob(SHORTNAME); //creates a new ClusterJob object + // Get the data service + CyJobDataService dataService = job.getJobDataService(); //gets the dataService of the execution service + // Add our data + CyJobData jobData = dataService.addData(null, "columns", columns); + jobData = dataService.addData(jobData, "data", data); + job.storeClusterData(clusterAttributeName, currentNetwork, clusterManager, createGroups, GROUP_ATTRIBUTE, null, getShortName()); + // Create our handler + DimensionalityReductionJobHandler jobHandler = new DimensionalityReductionJobHandler(job, network, context.showScatterPlot); + job.setJobMonitor(jobHandler); + // Submit the job + CyJobStatus exStatus = executionService.executeJob(job, basePath, null, jobData); + + CyJobStatus.Status status = exStatus.getStatus(); + System.out.println("Status: " + status); + if (status == Status.FINISHED) { + jobHandler.loadData(job, taskMonitor); + } else if (status == Status.RUNNING + || status == Status.SUBMITTED + || status == Status.QUEUED) { + CyJobManager manager = registrar.getService(CyJobManager.class); + manager.addJob(job, jobHandler, 5); //this one shows the load button + + } else if (status == Status.ERROR + || status == Status.UNKNOWN + || status == Status.CANCELED + || status == Status.FAILED + || status == Status.TERMINATED + || status == Status.PURGED) { + monitor.setStatusMessage("Job status: " + status); + } + + // Save our SUIDs in case we get saved and restored + SUIDUtil.saveSUIDs(job, currentNetwork, currentNetwork.getNodeList()); + + } +} diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/mds/MDSContext.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/mds/MDSContext.java new file mode 100644 index 0000000..87f29ff --- /dev/null +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/mds/MDSContext.java @@ -0,0 +1,129 @@ +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.mds; + +import org.cytoscape.model.CyNetwork; +import org.cytoscape.work.ContainsTunables; +import org.cytoscape.work.Tunable; +import org.cytoscape.work.swing.TunableUIHelper; +import org.cytoscape.work.util.BoundedDouble; +import org.cytoscape.work.util.ListMultipleSelection; +import org.cytoscape.work.util.ListSingleSelection; + +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.AdvancedProperties; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.attributeClusterers.AttributeList; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.NetworkVizProperties; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.ModelUtils; + +public class MDSContext { + + CyNetwork network; + TunableUIHelper helper; + + //Tunables + + public ListMultipleSelection nodeAttributeList = null; + @Tunable(description="Node attributes for dimensionality reduction", groups = "Array sources", + longDescription="Select the node table columns to be used for calculating dimensionality reduction. "+ + "Note that at least 2 node columns are usually required.", + exampleStringValue="gal1RGexp,gal4RGExp,gal80Rexp", + tooltip = "You must choose at least 2 node columns for dimensionality reduction.", gravity = 68 ) + public ListMultipleSelection getnodeAttributeList() { + if (network != null && nodeAttributeList == null) + nodeAttributeList = ModelUtils.updateNodeAttributeList(network, nodeAttributeList); + return nodeAttributeList; + } + public void setnodeAttributeList(ListMultipleSelection nal) { } + + @Tunable(description="Only use data from selected nodes", groups="Array sources", + longDescription="Only the data from the array sources of the selected nodes will be used.", + exampleStringValue = "false", + gravity = 1.5) + public boolean selectedOnly = false; + + @Tunable(description = "Metric", + longDescription = "If True, perform metric MDS; otherwise, perform nonmetric MDS.", + exampleStringValue = "True", + tooltip = "If checked, perform metric MDS; otherwise, perform nonmetric MDS.", + groups = {"MDS Advanced Settings"}, gravity = 69) + public boolean metric = true; + + @Tunable(description = "Number of initializations", + longDescription = "Number of times the SMACOF algorithm will be run with different initializations. " + + "The final results will be the best output of the runs, determined by the run with the smallest final stress.", + exampleStringValue = "4", + tooltip = "Number of times the SMACOF algorithm will be run with different initializations.
" + + "The final results will be the best output of the runs, determined by the run with the smallest final stress.", + groups = {"MDS Advanced Settings"}, gravity = 70) + public int n_init = 4; + + @Tunable(description = "Maximum iterations", + longDescription = "Maximum number of iterations of the SMACOF algorithm for a single run.", + exampleStringValue = "300", + tooltip = "Maximum number of iterations of the SMACOF algorithm for a single run.", + groups = {"MDS Advanced Settings"}, gravity = 71) + public int max_iter = 300; + + @Tunable(description = "Eps", + longDescription = "Relative tolerance with respect to stress at which to declare convergence.", + exampleStringValue = "1 * 10^(-3)", + tooltip = "Relative tolerance with respect to stress at which to declare convergence.", + groups = {"MDS Advanced Settings"}, gravity = 72) + public double eps = 1 * 10^(-3); + + @Tunable(description = "Dissimilarity", + longDescription = "Dissimilarity measure to use:\r\n" + + "‘euclidean’: Pairwise Euclidean distances between points in the dataset.\r\n" + + "‘precomputed’: Pre-computed dissimilarities are passed directly to fit and fit_transform.", + exampleStringValue = "euclidean", + tooltip = "Dissimilarity measure to use:
" + + "‘euclidean’: Pairwise Euclidean distances between points in the dataset.
" + + "‘precomputed’: Pre-computed dissimilarities are passed directly to fit and fit_transform.", + groups = {"MDS Advanced Settings"}, gravity = 73) + public ListSingleSelection dissimilarity = new ListSingleSelection("euclidean", "precomputed"); + + @Tunable(description = "Show scatter plot with results", + longDescription = "If this is set to ```true```, show the scatterplot after the calculation is complete.", + exampleStringValue = "true", + tooltip = "If this is checked, show the scatterplot after the calculation is complete.", + groups = {"MDS Advanced Settings"}, gravity = 74) + public boolean showScatterPlot = true; + + @Tunable(description = "Synchronous", + longDescription = "If ```false``` the algorithm will run in the background after specified wait time", + exampleStringValue = "true", + tooltip = "If ```false``` the algorithm will run in the background after specified wait time", + groups = {"MDS Advanced Settings"}, gravity = 75) + public boolean isSynchronous = false; + + public MDSContext() { + + } + + + public MDSContext(MDSContext origin) { + + nodeAttributeList = origin.nodeAttributeList; + metric = origin.metric; + n_init = origin.n_init; + max_iter = origin.max_iter; + eps = origin.eps; + dissimilarity = origin.dissimilarity; + + } + + public void setNetwork(CyNetwork network) { + if (this.network != null && this.network.equals(network)) + return; + + this.network = network; + this.nodeAttributeList = null; + } + + public CyNetwork getNetwork() { return network; } + + + public void setUIHelper(TunableUIHelper helper) { + this.helper = helper; + + } + +} diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/mds/MDSTaskFactory.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/mds/MDSTaskFactory.java new file mode 100644 index 0000000..c8560ff --- /dev/null +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/mds/MDSTaskFactory.java @@ -0,0 +1,53 @@ +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.mds; + +import java.util.Collections; +import java.util.List; + +import org.cytoscape.service.util.CyServiceRegistrar; +import org.cytoscape.work.TaskIterator; + +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.AbstractClusterTaskFactory; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterTaskFactory; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterViz; + +public class MDSTaskFactory extends AbstractClusterTaskFactory { + MDSContext context = null; + final CyServiceRegistrar registrar; + + public MDSTaskFactory(ClusterManager clusterManager, CyServiceRegistrar registrar) { + super(clusterManager); + context = new MDSContext(); + this.registrar = registrar; + } + + public String getName() {return MDS.NAME;} + + public String getShortName() {return MDS.SHORTNAME;} + + @Override + public String getLongDescription() { + return "Multidimensional scaling (MDS) seeks a low-dimensional representation of the data in "+ + "which the distances respect well the distances in the original high-dimensional space."+ + "

"+ + "In general, MDS is a technique used for analyzing similarity or dissimilarity data. "+ + "It attempts to model similarity or dissimilarity data as distances in a geometric spaces. "+ + "The data can be ratings of similarity between objects, interaction frequencies of "+ + "molecules, or trade indices between countries."; + } + + @Override + public ClusterViz getVisualizer() { + return null; + } + + @Override + public List getTypeList() { + return Collections.singletonList(ClusterTaskFactory.ClusterType.DIMRED); + } + + @Override + public TaskIterator createTaskIterator() { + return new TaskIterator(new MDS(context, clusterManager, registrar)); + } +} diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pca/PCA.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pca/PCA.java similarity index 97% rename from src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pca/PCA.java rename to src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pca/PCA.java index 38ae3b9..8eacb60 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pca/PCA.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pca/PCA.java @@ -3,7 +3,7 @@ * To change this template file, choose Tools | Templates * and open the template in the editor. */ -package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.pca; +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.pca; import java.util.Arrays; import java.util.List; diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pca/PCAContext.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pca/PCAContext.java similarity index 98% rename from src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pca/PCAContext.java rename to src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pca/PCAContext.java index 2d9d7ae..22131f2 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pca/PCAContext.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pca/PCAContext.java @@ -3,7 +3,7 @@ * To change this template file, choose Tools | Templates * and open the template in the editor. */ -package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.pca; +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.pca; import edu.ucsf.rbvi.clusterMaker2.internal.api.DistanceMetric; import edu.ucsf.rbvi.clusterMaker2.internal.utils.ModelUtils; diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pca/PCATaskFactory.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pca/PCATaskFactory.java similarity index 96% rename from src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pca/PCATaskFactory.java rename to src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pca/PCATaskFactory.java index aca53e8..997c9a0 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pca/PCATaskFactory.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pca/PCATaskFactory.java @@ -3,7 +3,7 @@ * To change this template file, choose Tools | Templates * and open the template in the editor. */ -package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.pca; +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.pca; import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.AbstractClusterTaskFactory; import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.AP.APCluster; diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pca/ResultPanelPCA.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pca/ResultPanelPCA.java similarity index 98% rename from src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pca/ResultPanelPCA.java rename to src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pca/ResultPanelPCA.java index eb7bc35..7009288 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pca/ResultPanelPCA.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pca/ResultPanelPCA.java @@ -3,7 +3,7 @@ * To change this template file, choose Tools | Templates * and open the template in the editor. */ -package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.pca; +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.pca; import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.NodeCluster; import edu.ucsf.rbvi.clusterMaker2.internal.ui.ResultsPanel; diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pca/RunPCA.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pca/RunPCA.java similarity index 98% rename from src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pca/RunPCA.java rename to src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pca/RunPCA.java index cfa2027..9029c1a 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pca/RunPCA.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pca/RunPCA.java @@ -3,7 +3,7 @@ * To change this template file, choose Tools | Templates * and open the template in the editor. */ -package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.pca; +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.pca; import java.util.Arrays; import java.util.Properties; diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pcoa/CalculationMatrix.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pcoa/CalculationMatrix.java similarity index 97% rename from src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pcoa/CalculationMatrix.java rename to src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pcoa/CalculationMatrix.java index f5a1350..90d1319 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pcoa/CalculationMatrix.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pcoa/CalculationMatrix.java @@ -1,4 +1,4 @@ -package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.pcoa; +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.pcoa; import java.util.Arrays; import java.text.DecimalFormat; diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pcoa/GowersMatrix.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pcoa/GowersMatrix.java similarity index 97% rename from src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pcoa/GowersMatrix.java rename to src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pcoa/GowersMatrix.java index b779d96..fe12d56 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pcoa/GowersMatrix.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pcoa/GowersMatrix.java @@ -1,4 +1,4 @@ -package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.pcoa; +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.pcoa; import java.util.Arrays; import java.util.stream.IntStream; diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pcoa/PCoA.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pcoa/PCoA.java similarity index 97% rename from src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pcoa/PCoA.java rename to src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pcoa/PCoA.java index f13e5c7..5d90152 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pcoa/PCoA.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pcoa/PCoA.java @@ -1,4 +1,4 @@ -package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.pcoa; +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.pcoa; import java.awt.geom.Point2D; import java.util.ArrayList; diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pcoa/PCoAContext.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pcoa/PCoAContext.java similarity index 97% rename from src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pcoa/PCoAContext.java rename to src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pcoa/PCoAContext.java index 46e9b81..a0ad5ad 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pcoa/PCoAContext.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pcoa/PCoAContext.java @@ -1,4 +1,4 @@ -package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.pcoa; +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.pcoa; import java.util.List; diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pcoa/PCoATaskFactory.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pcoa/PCoATaskFactory.java similarity index 87% rename from src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pcoa/PCoATaskFactory.java rename to src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pcoa/PCoATaskFactory.java index d9b92bd..83aaf6a 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pcoa/PCoATaskFactory.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pcoa/PCoATaskFactory.java @@ -1,4 +1,4 @@ -package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.pcoa; +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.pcoa; import java.util.Collections; import java.util.List; @@ -6,8 +6,8 @@ import org.cytoscape.work.TaskIterator; import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.AbstractClusterTaskFactory; -import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.pca.PCA; -import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.pca.PCAContext; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.pca.PCA; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.pca.PCAContext; import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterTaskFactory; import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterViz; diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pcoa/RunPCoA.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pcoa/RunPCoA.java similarity index 98% rename from src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pcoa/RunPCoA.java rename to src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pcoa/RunPCoA.java index cb7e46f..21fbdd8 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pcoa/RunPCoA.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/pcoa/RunPCoA.java @@ -1,4 +1,4 @@ -package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.pcoa; +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.pcoa; import java.util.Arrays; import java.util.List; diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/spectral/Spectral.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/spectral/Spectral.java new file mode 100644 index 0000000..1992354 --- /dev/null +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/spectral/Spectral.java @@ -0,0 +1,145 @@ +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.spectral; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.cytoscape.application.CyApplicationManager; +import org.cytoscape.jobs.CyJobData; +import org.cytoscape.jobs.CyJobDataService; +import org.cytoscape.jobs.CyJobExecutionService; +import org.cytoscape.jobs.CyJobManager; +import org.cytoscape.jobs.CyJobStatus; +import org.cytoscape.jobs.SUIDUtil; +import org.cytoscape.jobs.CyJobStatus.Status; +import org.cytoscape.model.CyColumn; +import org.cytoscape.model.CyNetwork; +import org.cytoscape.model.CyNode; +import org.cytoscape.model.CyRow; +import org.cytoscape.model.CyTable; +import org.cytoscape.service.util.CyServiceRegistrar; +import org.cytoscape.work.ContainsTunables; +import org.cytoscape.work.ProvidesTitle; +import org.cytoscape.work.TaskMonitor; +import org.json.simple.JSONArray; + +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.AbstractNetworkClusterer; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; +import edu.ucsf.rbvi.clusterMaker2.internal.ui.NewNetworkView; +import edu.ucsf.rbvi.clusterMaker2.internal.ui.ScatterPlotDialog; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJob; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJobHandler; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.DimensionalityReductionJobHandler; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.RemoteServer; + +public class Spectral extends AbstractNetworkClusterer { + public static String NAME = "Spectral (remote)"; + public static String SHORTNAME = "spectral"; + final CyServiceRegistrar registrar; + public final static String GROUP_ATTRIBUTE = "__SpectralGroups.SUID"; + + @ContainsTunables + public SpectralContext context = null; + + public Spectral(SpectralContext context, ClusterManager manager, CyServiceRegistrar registrar) { + super(manager); + this.context = context; + if (network == null) + network = clusterManager.getNetwork(); + context.setNetwork(network); + this.registrar = registrar; + } + + @Override + public String getShortName() {return SHORTNAME;} + + @Override + @ProvidesTitle + public String getName() {return NAME;} + + @Override + public void run(TaskMonitor taskMonitor) throws Exception { + monitor = taskMonitor; + // Get the execution service + CyJobExecutionService executionService = + registrar.getService(CyJobExecutionService.class, "(title=ClusterJobExecutor)"); + CyApplicationManager appManager = registrar.getService(CyApplicationManager.class); + CyNetwork currentNetwork = appManager.getCurrentNetwork(); //gets the network presented in Cytoscape + + HashMap configuration = new HashMap<>(); + if (context.isSynchronous == true) { + configuration.put("waitTime", -1); + } else { + configuration.put("waitTime", 20); + } + + clusterAttributeName = "__spectral"; + List attributes = context.getnodeAttributeList().getSelectedValues(); // rather than get single select attribute, make it multiple select + + CyTable nodeTable = currentNetwork.getDefaultNodeTable(); + List columns = new ArrayList<>(); + columns.add("name"); + columns.addAll(attributes); + + // creating the data itself, values of the columns chosen for each row (node) + List> data = new ArrayList<>(); + HashMap nodeMap = getNetworkNodes(currentNetwork); + + for (Long nodeSUID : nodeMap.keySet()) { + CyRow row = nodeTable.getRow(nodeSUID); + if (context.selectedOnly && !row.get(CyNetwork.SELECTED, Boolean.class)) + continue; + List rowList = new ArrayList<>(); + for (String columnName : columns) { + CyColumn column = nodeTable.getColumn(columnName); + if (row.get(columnName, column.getType()) != null) { + rowList.add(row.get(columnName, column.getType()).toString()); + } else { + rowList.add("0"); + } + } + data.add(rowList); + } + + String basePath = RemoteServer.getBasePath(); + + // Get our initial job + ClusterJob job = (ClusterJob) executionService.createCyJob(SHORTNAME); //creates a new ClusterJob object + // Get the data service + CyJobDataService dataService = job.getJobDataService(); //gets the dataService of the execution service + // Add our data + CyJobData jobData = dataService.addData(null, "columns", columns); + jobData = dataService.addData(jobData, "data", data); + job.storeClusterData(clusterAttributeName, currentNetwork, clusterManager, createGroups, GROUP_ATTRIBUTE, null, getShortName()); + // Create our handler + DimensionalityReductionJobHandler jobHandler = new DimensionalityReductionJobHandler(job, network, context.showScatterPlot); + job.setJobMonitor(jobHandler); + // Submit the job + CyJobStatus exStatus = executionService.executeJob(job, basePath, configuration, jobData); + + CyJobStatus.Status status = exStatus.getStatus(); + System.out.println("Status: " + status); + + if (status == Status.FINISHED) { + jobHandler.loadData(job, taskMonitor); + } else if (status == Status.RUNNING + || status == Status.SUBMITTED + || status == Status.QUEUED) { + CyJobManager manager = registrar.getService(CyJobManager.class); + manager.addJob(job, jobHandler, 5); //this one shows the load button + + } else if (status == Status.ERROR + || status == Status.UNKNOWN + || status == Status.CANCELED + || status == Status.FAILED + || status == Status.TERMINATED + || status == Status.PURGED) { + monitor.setStatusMessage("Job status: " + status); + } + + // Save our SUIDs in case we get saved and restored + SUIDUtil.saveSUIDs(job, currentNetwork, currentNetwork.getNodeList()); + + } +} diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/spectral/SpectralContext.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/spectral/SpectralContext.java new file mode 100644 index 0000000..b0598de --- /dev/null +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/spectral/SpectralContext.java @@ -0,0 +1,128 @@ +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.spectral; + +import org.cytoscape.model.CyNetwork; +import org.cytoscape.work.ContainsTunables; +import org.cytoscape.work.Tunable; +import org.cytoscape.work.swing.TunableUIHelper; +import org.cytoscape.work.util.BoundedDouble; +import org.cytoscape.work.util.ListMultipleSelection; +import org.cytoscape.work.util.ListSingleSelection; + +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.AdvancedProperties; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.attributeClusterers.AttributeList; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.NetworkVizProperties; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.ModelUtils; + +public class SpectralContext { + + CyNetwork network; + TunableUIHelper helper; + + //Tunables + + public ListMultipleSelection nodeAttributeList = null; + @Tunable(description="Node attributes for dimensionality reduction", groups="Array sources", + longDescription="Select the node table columns to be used for calculating dimensionality reduction. "+ + "Note that at least 2 node columns are usually required.", + exampleStringValue="gal1RGexp,gal4RGExp,gal80Rexp", + tooltip = "You must choose at least 2 node columns for dimensionality reduction.", gravity=50 ) + public ListMultipleSelection getnodeAttributeList() { + if (network != null && nodeAttributeList == null) + nodeAttributeList = ModelUtils.updateNodeAttributeList(network, nodeAttributeList); + return nodeAttributeList; + } + public void setnodeAttributeList(ListMultipleSelection nal) { } + + @Tunable(description="Only use data from selected nodes", groups="Array sources", + longDescription="Only the data from the array sources of the selected nodes will be used.", + exampleStringValue = "false", + gravity = 1.5) + public boolean selectedOnly = false; + + @Tunable(description = "Affinity", + longDescription = "‘nearest_neighbors’ : construct the affinity matrix by computing a graph of nearest neighbors.\r\n" + + "‘rbf’ : construct the affinity matrix by computing a radial basis function (RBF) kernel.\r\n" + + "‘precomputed’ : interpret X as a precomputed affinity matrix.\r\n" + + "‘precomputed_nearest_neighbors’ : interpret X as a sparse graph of precomputed nearest neighbors," + + " and constructs the affinity matrix by selecting the n_neighbors nearest neighbors.\r\n" + + "callable : use passed in function as affinity the function takes in data matrix (n_samples, n_features) " + + "and return affinity matrix (n_samples, n_samples).", + exampleStringValue = "nearest_neighbors", + tooltip = "‘nearest_neighbors’: construct the affinity matrix by computing a graph of nearest neighbors.
" + + "‘rbf’ : construct the affinity matrix by computing a radial basis function (RBF) kernel.
" + + "‘precomputed’: interpret X as a precomputed affinity matrix.
" + + "‘precomputed_nearest_neighbors’: interpret X as a sparse graph of precomputed nearest neighbors,
" + + "and constructs the affinity matrix by selecting the n_neighbors nearest neighbors.
" + + "'callable': use passed in function as affinity the function takes in data matrix (n_samples, n_features)
" + + "and return affinity matrix (n_samples, n_samples).", + groups = {"Spectral Advanced Settings"}, gravity = 67) + public ListSingleSelection affinity = new ListSingleSelection("nearest_neighbors", "rbf", "precomputed", "precomputed_nearest_neighbors", "callable"); + + @Tunable(description = "Gamma", + longDescription = "Kernel coefficient for rbf kernel. If None, gamma will be set to 1/n_features.", + exampleStringValue = "None", + tooltip = "Kernel coefficient for rbf kernel. If None, gamma will be set to 1/n_features.", + groups = {"Spectral Advanced Settings"}, gravity = 68) + public double gamma; + + @Tunable(description = "Eigen solver", + longDescription = "The eigenvalue decomposition strategy to use. AMG requires pyamg to be installed. " + + "It can be faster on very large, sparse problems. If None, then 'arpack' is used.", + exampleStringValue = "None", + tooltip = "The eigenvalue decomposition strategy to use. AMG requires pyamg to be installed.
" + + "It can be faster on very large, sparse problems. If None, then 'arpack' is used.", + groups = {"Spectral Advanced Settings"}, gravity = 69) + public ListSingleSelection eigen_solver = new ListSingleSelection("arpack", "lobpcg", "amg"); + + @Tunable(description = "Number of neighbors", + longDescription = "Number of nearest neighbors for nearest_neighbors graph building. If None, n_neighbors will be set to max(n_samples/10, 1).", + exampleStringValue = "None", + tooltip = "Number of nearest neighbors for nearest_neighbors graph building. If None, Number of neighbors will be set to max(n_samples/10, 1).", + groups = {"Spectral Advanced Settings"}, gravity = 70) + public int n_neighbors; + + @Tunable(description = "Show scatter plot with results", + longDescription = "If this is set to ```true```, show the scatterplot after the calculation is complete", + exampleStringValue = "true", + tooltip = "If this is checked, show the scatterplot after the calculation is complete.", + groups = {"Spectral Advanced Settings"}, gravity = 71) + public boolean showScatterPlot = true; + + @Tunable(description = "Synchronous", + longDescription = "If ```false``` the algorithm will run in the background after specified wait time", + exampleStringValue = "true", + tooltip = "If ```false``` the algorithm will run in the background after specified wait time", + groups = {"Leiden Advanced Settings"}, gravity = 72) + public boolean isSynchronous = false; + + public SpectralContext() { + + } + + public SpectralContext(SpectralContext origin) { + + nodeAttributeList = origin.nodeAttributeList; + affinity = origin.affinity; + gamma = origin.gamma; + eigen_solver = origin.eigen_solver; + n_neighbors = origin.n_neighbors; + isSynchronous = origin.isSynchronous; + } + + public void setNetwork(CyNetwork network) { + if (this.network != null && this.network.equals(network)) + return; + + this.network = network; + this.nodeAttributeList = null; + } + + public CyNetwork getNetwork() { return network; } + + + public void setUIHelper(TunableUIHelper helper) { + this.helper = helper; + + } + +} diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/spectral/SpectralTaskFactory.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/spectral/SpectralTaskFactory.java new file mode 100644 index 0000000..a880254 --- /dev/null +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/spectral/SpectralTaskFactory.java @@ -0,0 +1,47 @@ +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.spectral; + +import java.util.Collections; +import java.util.List; + +import org.cytoscape.service.util.CyServiceRegistrar; +import org.cytoscape.work.TaskIterator; + +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.AbstractClusterTaskFactory; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterTaskFactory; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterViz; + +public class SpectralTaskFactory extends AbstractClusterTaskFactory { + SpectralContext context = null; + final CyServiceRegistrar registrar; + + public SpectralTaskFactory(ClusterManager clusterManager, CyServiceRegistrar registrar) { + super(clusterManager); + context = new SpectralContext(); + this.registrar = registrar; + } + + public String getName() {return Spectral.NAME;} + + public String getShortName() {return Spectral.SHORTNAME;} + + @Override + public String getLongDescription() { + return "Spectral Embedding is an approach to calculating a non-linear embedding. Scikit-learn implements Laplacian Eigenmaps, which finds a low dimensional representation of the data using a spectral decomposition of the graph Laplacian. The graph generated can be considered as a discrete approximation of the low dimensional manifold in the high dimensional space. Minimization of a cost function based on the graph ensures that points close to each other on the manifold are mapped close to each other in the low dimensional space, preserving local distances."; + } + + @Override + public ClusterViz getVisualizer() { + return null; + } + + @Override + public List getTypeList() { + return Collections.singletonList(ClusterTaskFactory.ClusterType.DIMRED); + } + + @Override + public TaskIterator createTaskIterator() { + return new TaskIterator(new Spectral(context, clusterManager, registrar)); + } +} diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/tSNERemote/tSNERemote.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/tSNERemote/tSNERemote.java new file mode 100644 index 0000000..1bf44ff --- /dev/null +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/tSNERemote/tSNERemote.java @@ -0,0 +1,153 @@ +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.tSNERemote; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.cytoscape.application.CyApplicationManager; +import org.cytoscape.jobs.CyJobData; +import org.cytoscape.jobs.CyJobDataService; +import org.cytoscape.jobs.CyJobExecutionService; +import org.cytoscape.jobs.CyJobManager; +import org.cytoscape.jobs.CyJobStatus; +import org.cytoscape.jobs.SUIDUtil; +import org.cytoscape.jobs.CyJobStatus.Status; +import org.cytoscape.model.CyColumn; +import org.cytoscape.model.CyNetwork; +import org.cytoscape.model.CyNode; +import org.cytoscape.model.CyRow; +import org.cytoscape.model.CyTable; +import org.cytoscape.service.util.CyServiceRegistrar; +import org.cytoscape.work.ContainsTunables; +import org.cytoscape.work.ProvidesTitle; +import org.cytoscape.work.TaskMonitor; +import org.json.simple.JSONArray; + +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.AbstractNetworkClusterer; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; +import edu.ucsf.rbvi.clusterMaker2.internal.ui.NewNetworkView; +import edu.ucsf.rbvi.clusterMaker2.internal.ui.ScatterPlotDialog; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJob; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJobHandler; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.DimensionalityReductionJobHandler; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.RemoteServer; + +public class tSNERemote extends AbstractNetworkClusterer { + public static String NAME = "tSNE (remote)"; + public static String SHORTNAME = "tsneremote"; + final CyServiceRegistrar registrar; + public final static String GROUP_ATTRIBUTE = "__tSNERemoteGroups.SUID"; + + @ContainsTunables + public tSNERemoteContext context = null; + + public tSNERemote(tSNERemoteContext context, ClusterManager manager, CyServiceRegistrar registrar) { + super(manager); + this.context = context; + if (network == null) + network = clusterManager.getNetwork(); + context.setNetwork(network); + this.registrar = registrar; + } + + @Override + public String getShortName() {return SHORTNAME;} + + @Override + @ProvidesTitle + public String getName() {return NAME;} + + @Override + public void run(TaskMonitor taskMonitor) throws Exception { + monitor = taskMonitor; + // Get the execution service + CyJobExecutionService executionService = + registrar.getService(CyJobExecutionService.class, "(title=ClusterJobExecutor)"); + CyApplicationManager appManager = registrar.getService(CyApplicationManager.class); + CyNetwork currentNetwork = appManager.getCurrentNetwork(); //gets the network presented in Cytoscape + + HashMap configuration = new HashMap<>(); + if (context.isSynchronous == true) { + configuration.put("waitTime", -1); + } else { + configuration.put("waitTime", 20); + } + + configuration.put("perplexity", context.perplexity); + configuration.put("early_exaggeration", context.early_exaggeration); + configuration.put("metric", context.metric.getSelectedValue()); + configuration.put("learning_rate", context.learning_rate); + configuration.put("n_iter", context.n_iter); + configuration.put("init", context.init.getSelectedValue()); + + + clusterAttributeName = "__tsneremote"; + List attributes = context.getnodeAttributeList().getSelectedValues(); + + // list of column names + CyTable nodeTable = currentNetwork.getDefaultNodeTable(); + List columns = new ArrayList<>(); + columns.add("name"); + columns.addAll(attributes); + + // creating the data itself, values of the columns chosen for each row (node) + List> data = new ArrayList<>(); + HashMap nodeMap = getNetworkNodes(currentNetwork); + + for (Long nodeSUID : nodeMap.keySet()) { + CyRow row = nodeTable.getRow(nodeSUID); + if (context.selectedOnly && !row.get(CyNetwork.SELECTED, Boolean.class)) + continue; + List rowList = new ArrayList<>(); + for (String columnName : columns) { + CyColumn column = nodeTable.getColumn(columnName); + if (row.get(columnName, column.getType()) != null) { + rowList.add(row.get(columnName, column.getType()).toString()); + } else { + rowList.add("0"); + } + } + data.add(rowList); + } + + String basePath = RemoteServer.getBasePath(); + + // Get our initial job + ClusterJob job = (ClusterJob) executionService.createCyJob(SHORTNAME); //creates a new ClusterJob object + // Get the data service + CyJobDataService dataService = job.getJobDataService(); //gets the dataService of the execution service + // Add our data + CyJobData jobData = dataService.addData(null, "columns", columns); + jobData = dataService.addData(jobData, "data", data); + job.storeClusterData(clusterAttributeName, currentNetwork, clusterManager, createGroups, GROUP_ATTRIBUTE, null, getShortName()); + // Create our handler + DimensionalityReductionJobHandler jobHandler = new DimensionalityReductionJobHandler(job, network, context.showScatterPlot); + job.setJobMonitor(jobHandler); + // Submit the job + CyJobStatus exStatus = executionService.executeJob(job, basePath, configuration, jobData); + + CyJobStatus.Status status = exStatus.getStatus(); + System.out.println("Status: " + status); + if (status == Status.FINISHED) { + jobHandler.loadData(job, taskMonitor); + } else if (status == Status.RUNNING + || status == Status.SUBMITTED + || status == Status.QUEUED) { + CyJobManager manager = registrar.getService(CyJobManager.class); + manager.addJob(job, jobHandler, 5); //this one shows the load button + + } else if (status == Status.ERROR + || status == Status.UNKNOWN + || status == Status.CANCELED + || status == Status.FAILED + || status == Status.TERMINATED + || status == Status.PURGED) { + monitor.setStatusMessage("Job status: " + status); + } + + // Save our SUIDs in case we get saved and restored + SUIDUtil.saveSUIDs(job, currentNetwork, currentNetwork.getNodeList()); + + } +} diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/tSNERemote/tSNERemoteContext.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/tSNERemote/tSNERemoteContext.java new file mode 100644 index 0000000..2c8c1dd --- /dev/null +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/tSNERemote/tSNERemoteContext.java @@ -0,0 +1,172 @@ +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.tSNERemote; + +import org.cytoscape.model.CyNetwork; +import org.cytoscape.work.ContainsTunables; +import org.cytoscape.work.Tunable; +import org.cytoscape.work.swing.TunableUIHelper; +import org.cytoscape.work.util.BoundedDouble; +import org.cytoscape.work.util.ListMultipleSelection; +import org.cytoscape.work.util.ListSingleSelection; + +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.AdvancedProperties; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.attributeClusterers.AttributeList; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.NetworkVizProperties; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.ModelUtils; + +public class tSNERemoteContext { + + CyNetwork network; + TunableUIHelper helper; + + //Tunables + + public ListMultipleSelection nodeAttributeList = null; + @Tunable(description="Node attributes for dimensionality reduction", groups = "Array sources", + longDescription="Select the node table columns to be used for calculating the cluster. "+ + "Note that at least 2 node columns are usually required.", + exampleStringValue="gal1RGexp,gal4RGExp,gal80Rexp", + tooltip = "You must choose at least 2 node columns for dimensionality reduction.", gravity = 66 ) + public ListMultipleSelection getnodeAttributeList() { + if (network != null && nodeAttributeList == null) + nodeAttributeList = ModelUtils.updateNodeAttributeList(network, nodeAttributeList); + return nodeAttributeList; + } + public void setnodeAttributeList(ListMultipleSelection nal) { } + + @Tunable(description="Only use data from selected nodes", groups="Array sources", + longDescription="Only the data from the array sources of the selected nodes will be used.", + exampleStringValue = "false", + gravity = 1.5) + public boolean selectedOnly = false; + + @Tunable(description = "Perplexity", + longDescription = "The perplexity is related to the number of nearest neighbors " + + "that is used in other manifold learning algorithms. Larger datasets usually " + + "require a larger perplexity. Consider selecting a value between 5 and 50. " + + "Different values can result in significantly different results.", + exampleStringValue="30.0", + tooltip = "The perplexity is related to the number of nearest neighbors that is used in other manifold learning algorithms.
" + + "Larger datasets usually require a larger perplexity. Consider selecting a value between 5 and 50.
" + + "Different values can result in significantly different results.", + groups = {"t-SNE Advanced Settings"}, gravity = 67) + public double perplexity = 30.0; + + @Tunable(description = "Number of Iterations", + longDescription = "The number of iterations of the algorithm to perform", + exampleStringValue = "1000", + tooltip = "The number of iterations of the algorithm to perform.", + groups = {"t-SNE Advanced Settings"}, gravity = 68) + public int n_iter = 1000; + + @Tunable(description ="Early Exaggeration", + longDescription = "Controls how tight natural clusters in the original space are in the embedded " + + "space and how much space will be between them. For larger values, the space between natural clusters " + + "will be larger in the embedded space. Again, the choice of this parameter is not very critical. " + + "If the cost function increases during initial optimization, the early exaggeration factor or the learning rate " + + "might be too high.", + exampleStringValue = "12.0", + tooltip = "Controls how tight natural clusters in the original space are in the embedded
" + + "space and how much space will be between them. For larger values, the space between natural clusters
" + + "will be larger in the embedded space. Again, the choice of this parameter is not very critical.
" + + "If the cost function increases during initial optimization, the early exaggeration factor or the learning rate might be too high.", + groups = {"t-SNE Advanced Settings"}, gravity = 69) + public double early_exaggeration = 12.0; + + @Tunable(description = "Metric", + longDescription = "The metric to use when calculating distance between instances in a feature array. " + + "If metric is a string, it must be one of the options allowed by scipy.spatial.distance.pdist " + + "for its metric parameter, or a metric listed in pairwise.PAIRWISE_DISTANCE_FUNCTIONS. If metric is " + + "“precomputed”, X is assumed to be a distance matrix. Alternatively, if metric is a callable function, " + + "it is called on each pair of instances (rows) and the resulting value recorded. The callable should take " + + "two arrays from X as input and return a value indicating the distance between them. The default is " + + "“euclidean” which is interpreted as squared euclidean distance.", + exampleStringValue = "euclidean", + tooltip = "The metric to use when calculating distance between instances in a feature array.
" + + "The default is 'euclidean' which is interpreted as squared euclidean distance.", + groups = {"t-SNE Advanced Settings"}, gravity = 70) + public ListSingleSelection metric = new ListSingleSelection("euclidean", "manhattan", "chebyshev", "minkowski", "canberra", "braycurtis", + "haversine", "mahalanobis", "wminkowski", "seuclidean", "cosine", "correlation", "hamming", "jaccard", "dice", "russellrao", "kulsinski", "rogerstanimoto", + "sokalmichener", "sokalsneath", "yule"); + + @Tunable(description = "Learning Rate", + longDescription = "The learning rate for t-SNE is usually in the range [10.0, 1000.0]. " + + "If the learning rate is too high, the data may look like a ‘ball’ with any point approximately equidistant " + + "from its nearest neighbours. If the learning rate is too low, most points may look compressed in a dense cloud " + + "with few outliers. If the cost function gets stuck in a bad local minimum increasing the learning rate may help.", + exampleStringValue = "200.0", + tooltip = "The learning rate for t-SNE is usually in the range [10.0, 1000.0]." + + "If the learning rate is too high, the data may look like a ‘ball’ with any point approximately equidistant
" + + "from its nearest neighbours. If the learning rate is too low, most points may look compressed in a dense cloud
" + + "with few outliers. If the cost function gets stuck in a bad local minimum increasing the learning rate may help.", + groups = {"t-SNE Advanced Settings"}, gravity = 71) + public double learning_rate = 200.0; + + @Tunable(description = "Init", + longDescription = "Initialization of embedding. Possible options are ‘random’, " + + "‘pca’, and a numpy array of shape (n_samples, n_components). PCA initialization cannot be " + + "used with precomputed distances and is usually more globally stable than random initialization.", + exampleStringValue = "pca", + tooltip = "Initialization of embedding. Possible options are ‘random’,
" + + "‘pca’, and a numpy array of shape (n_samples, n_components). PCA initialization cannot be
" + + "used with precomputed distances and is usually more globally stable than random initialization.", + groups = {"t-SNE Advanced Settings"}, gravity = 72) + public ListSingleSelection init = new ListSingleSelection("pca", "random"); + + @Tunable(description = "Show scatter plot with results", + longDescription = "If this is set to ```true```, show the scatterplot after the calculation is complete", + exampleStringValue = "true", + tooltip = "If this is checked, show the scatterplot after the calculation is complete.", + groups = {"t-SNE Advanced Settings"}, gravity = 73) + public boolean showScatterPlot = true; + + @Tunable(description = "Synchronous", + longDescription = "If ```false``` the algorithm will run in the background after specified wait time", + exampleStringValue = "true", + tooltip = "If ```false``` the algorithm will run in the background after specified wait time", + groups = {"Leiden Advanced Settings"}, gravity = 74) + public boolean isSynchronous = false; + + //@ContainsTunables + //public AdvancedProperties advancedAttributes; + + //@ContainsTunables + //public NetworkVizProperties vizProperties = new NetworkVizProperties(); + + public tSNERemoteContext() { + //advancedAttributes = new AdvancedProperties("__tsneremote", false); //this is the name of the column Integer that is created when click LOAD + } + + public tSNERemoteContext(tSNERemoteContext origin) { + /*if (origin.advancedAttributes != null) + advancedAttributes = new AdvancedProperties(origin.advancedAttributes); + else + advancedAttributes = new AdvancedProperties("__tsneremote", false); */ + + nodeAttributeList = origin.nodeAttributeList; + perplexity = origin.perplexity; + n_iter = origin.n_iter; + early_exaggeration = origin.early_exaggeration; + metric = origin.metric; + learning_rate = origin.learning_rate; + init = origin.init; + isSynchronous = origin.isSynchronous; + } + + public void setNetwork(CyNetwork network) { + if (this.network != null && this.network.equals(network)) + return; + + this.network = network; + this.nodeAttributeList = null; + } + + public CyNetwork getNetwork() { return network; } + + //public String getClusterAttribute() { return advancedAttributes.clusterAttribute;} + + public void setUIHelper(TunableUIHelper helper) { + this.helper = helper; + + } + +} diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/tSNERemote/tSNERemoteTaskFactory.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/tSNERemote/tSNERemoteTaskFactory.java new file mode 100644 index 0000000..cc54d78 --- /dev/null +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/tSNERemote/tSNERemoteTaskFactory.java @@ -0,0 +1,47 @@ +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.tSNERemote; + +import java.util.Collections; +import java.util.List; + +import org.cytoscape.service.util.CyServiceRegistrar; +import org.cytoscape.work.TaskIterator; + +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.AbstractClusterTaskFactory; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterTaskFactory; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterViz; + +public class tSNERemoteTaskFactory extends AbstractClusterTaskFactory { + tSNERemoteContext context = null; + final CyServiceRegistrar registrar; + + public tSNERemoteTaskFactory(ClusterManager clusterManager, CyServiceRegistrar registrar) { + super(clusterManager); + context = new tSNERemoteContext(); + this.registrar = registrar; + } + + public String getName() {return tSNERemote.NAME;} + + public String getShortName() {return tSNERemote.SHORTNAME;} + + @Override + public String getLongDescription() { + return ""; + } + + @Override + public ClusterViz getVisualizer() { + return null; + } + + @Override + public List getTypeList() { + return Collections.singletonList(ClusterTaskFactory.ClusterType.DIMRED); + } + + @Override + public TaskIterator createTaskIterator() { + return new TaskIterator(new tSNERemote(context, clusterManager, registrar)); + } +} \ No newline at end of file diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/tSNEWrapper/RuntSNE.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/tSNEWrapper/RuntSNE.java similarity index 95% rename from src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/tSNEWrapper/RuntSNE.java rename to src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/tSNEWrapper/RuntSNE.java index c29bca6..02f91e8 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/tSNEWrapper/RuntSNE.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/tSNEWrapper/RuntSNE.java @@ -1,4 +1,4 @@ -package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.tSNEWrapper; +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.tSNEWrapper; import java.awt.Color; diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/tSNEWrapper/TSneInterface.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/tSNEWrapper/TSneInterface.java similarity index 82% rename from src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/tSNEWrapper/TSneInterface.java rename to src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/tSNEWrapper/TSneInterface.java index 7e43306..a785858 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/tSNEWrapper/TSneInterface.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/tSNEWrapper/TSneInterface.java @@ -1,4 +1,4 @@ -package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.tSNEWrapper; +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.tSNEWrapper; import org.cytoscape.model.CyNetwork; diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/tSNEWrapper/tSNE.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/tSNEWrapper/tSNE.java similarity index 97% rename from src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/tSNEWrapper/tSNE.java rename to src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/tSNEWrapper/tSNE.java index ea14f35..1bc6b70 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/tSNEWrapper/tSNE.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/tSNEWrapper/tSNE.java @@ -1,4 +1,4 @@ -package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.tSNEWrapper; +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.tSNEWrapper; import java.awt.geom.Point2D; import java.util.HashMap; diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/tSNEWrapper/tSNEContext.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/tSNEWrapper/tSNEContext.java similarity index 96% rename from src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/tSNEWrapper/tSNEContext.java rename to src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/tSNEWrapper/tSNEContext.java index 33a3be2..4c7b14b 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/tSNEWrapper/tSNEContext.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/tSNEWrapper/tSNEContext.java @@ -1,4 +1,4 @@ -package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.tSNEWrapper; +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.tSNEWrapper; import org.cytoscape.model.CyNetwork; import org.cytoscape.work.ContainsTunables; @@ -15,6 +15,9 @@ public class tSNEContext implements TSneConfiguration { CyNetwork network; public boolean cancelled = false; + // use this website for the algorithms + // https://scikit-learn.org/stable/modules/generated/sklearn.manifold.TSNE.html + //Tunables @ContainsTunables public AttributeList attributeList = null; diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/tSNEWrapper/tSNETaskFactory.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/tSNEWrapper/tSNETaskFactory.java similarity index 95% rename from src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/tSNEWrapper/tSNETaskFactory.java rename to src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/tSNEWrapper/tSNETaskFactory.java index 50c2d01..68e38a2 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/tSNEWrapper/tSNETaskFactory.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/tSNEWrapper/tSNETaskFactory.java @@ -1,4 +1,4 @@ -package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.tSNEWrapper; +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.tSNEWrapper; import java.util.Collections; import java.util.List; diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/umap/UMAP.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/umap/UMAP.java new file mode 100644 index 0000000..d04a20a --- /dev/null +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/umap/UMAP.java @@ -0,0 +1,155 @@ +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.umap; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.swing.SwingUtilities; + +import org.cytoscape.application.CyApplicationManager; +import org.cytoscape.jobs.CyJob; +import org.cytoscape.jobs.CyJobData; +import org.cytoscape.jobs.CyJobDataService; +import org.cytoscape.jobs.CyJobExecutionService; +import org.cytoscape.jobs.CyJobManager; +import org.cytoscape.jobs.CyJobStatus; +import org.cytoscape.jobs.SUIDUtil; +import org.cytoscape.jobs.CyJobStatus.Status; +import org.cytoscape.model.CyColumn; +import org.cytoscape.model.CyNetwork; +import org.cytoscape.model.CyNode; +import org.cytoscape.model.CyRow; +import org.cytoscape.model.CyTable; +import org.cytoscape.service.util.CyServiceRegistrar; +import org.cytoscape.work.ContainsTunables; +import org.cytoscape.work.ProvidesTitle; +import org.cytoscape.work.TaskMonitor; +import org.json.simple.JSONArray; + +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.AbstractNetworkClusterer; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; +import edu.ucsf.rbvi.clusterMaker2.internal.ui.NewNetworkView; +import edu.ucsf.rbvi.clusterMaker2.internal.ui.ScatterPlotDialog; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJob; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJobHandler; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.DimensionalityReductionJobHandler; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.RemoteServer; + +public class UMAP extends AbstractNetworkClusterer { + public static String NAME = "UMAP (remote)"; + public static String SHORTNAME = "umap"; + final CyServiceRegistrar registrar; + public final static String GROUP_ATTRIBUTE = "__UMAPGroups.SUID"; + + @ContainsTunables + public UMAPContext context = null; + + public UMAP(UMAPContext context, ClusterManager manager, CyServiceRegistrar registrar) { + super(manager); + this.context = context; + if (network == null) + network = clusterManager.getNetwork(); + context.setNetwork(network); + this.registrar = registrar; + } + + @Override + public String getShortName() {return SHORTNAME;} + + @Override + @ProvidesTitle + public String getName() {return NAME;} + + @Override + public void run(TaskMonitor taskMonitor) throws Exception { + monitor = taskMonitor; + // Get the execution service + CyJobExecutionService executionService = registrar.getService(CyJobExecutionService.class, "(title=ClusterJobExecutor)"); + CyApplicationManager appManager = registrar.getService(CyApplicationManager.class); + CyNetwork currentNetwork = appManager.getCurrentNetwork(); //gets the network presented in Cytoscape + + HashMap configuration = new HashMap<>(); + if (context.isSynchronous == true) { + configuration.put("waitTime", -1); + } else { + configuration.put("waitTime", 20); + } + + configuration.put("n_neighbors",context.n_neighbors); + configuration.put("min_dist",context.min_dist); + configuration.put("metric",context.metric.getSelectedValue()); + configuration.put("scale",context.scale); + + + clusterAttributeName = "__umap"; + + List attributes = context.getnodeAttributeList().getSelectedValues(); // rather than get single select attribute, make it multiple select + + CyTable nodeTable = currentNetwork.getDefaultNodeTable(); + List columns = new ArrayList<>(); + columns.add("name"); + columns.addAll(attributes); + + // creating the data itself, values of the columns chosen for each row (node) + List> data = new ArrayList<>(); + HashMap nodeMap = getNetworkNodes(currentNetwork); + + for (Long nodeSUID : nodeMap.keySet()) { + CyRow row = nodeTable.getRow(nodeSUID); + if (context.selectedOnly && !row.get(CyNetwork.SELECTED, Boolean.class)) + continue; + List rowList = new ArrayList<>(); + for (String columnName : columns) { + CyColumn column = nodeTable.getColumn(columnName); + if (row.get(columnName, column.getType()) != null) { + rowList.add(row.get(columnName, column.getType()).toString()); + } else { + rowList.add("0"); + } + } + data.add(rowList); + } + + String basePath = RemoteServer.getBasePath(); + + // Get our initial job + ClusterJob job = (ClusterJob) executionService.createCyJob(SHORTNAME); //creates a new ClusterJob object + // Get the data service + CyJobDataService dataService = job.getJobDataService(); //gets the dataService of the execution service + // Add our data + CyJobData jobData = dataService.addData(null, "columns", columns); + jobData = dataService.addData(jobData, "data", data); + job.storeClusterData(clusterAttributeName, currentNetwork, clusterManager, createGroups, GROUP_ATTRIBUTE, null, getShortName()); + // Create our handler + DimensionalityReductionJobHandler jobHandler = new DimensionalityReductionJobHandler(job, network, context.showScatterPlot); + job.setJobMonitor(jobHandler); + // Submit the job + CyJobStatus exStatus = executionService.executeJob(job, basePath, configuration, jobData); + + CyJobStatus.Status status = exStatus.getStatus(); + System.out.println("Status: " + status); + + if (status == Status.FINISHED) { + jobHandler.loadData(job, taskMonitor); + } else if (status == Status.RUNNING + || status == Status.SUBMITTED + || status == Status.QUEUED) { + CyJobManager manager = registrar.getService(CyJobManager.class); + manager.addJob(job, jobHandler, 5); //this one shows the load button + + } else if (status == Status.ERROR + || status == Status.UNKNOWN + || status == Status.CANCELED + || status == Status.FAILED + || status == Status.TERMINATED + || status == Status.PURGED) { + monitor.setStatusMessage("Job status: " + status); + } + + // Save our SUIDs in case we get saved and restored + SUIDUtil.saveSUIDs(job, currentNetwork, currentNetwork.getNodeList()); + + } + +} diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/umap/UMAPContext.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/umap/UMAPContext.java new file mode 100644 index 0000000..f799334 --- /dev/null +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/umap/UMAPContext.java @@ -0,0 +1,153 @@ +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.umap; + +import org.cytoscape.model.CyNetwork; +import org.cytoscape.work.ContainsTunables; +import org.cytoscape.work.Tunable; +import org.cytoscape.work.swing.TunableUIHelper; +import org.cytoscape.work.util.ListMultipleSelection; +import org.cytoscape.work.util.ListSingleSelection; + +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.AdvancedProperties; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.NetworkVizProperties; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.ModelUtils; + +public class UMAPContext { + + CyNetwork network; + TunableUIHelper helper; + + //Tunables + + + public ListMultipleSelection nodeAttributeList = null; + @Tunable(description="Node attributes for dimensionality reduction", groups="Array sources", + longDescription="Select the node table columns to be used for calculating the cluster. "+ + "Note that at least 2 node columns are usually required.", + exampleStringValue="gal1RGexp,gal4RGExp,gal80Rexp", + tooltip = "You must choose at least 2 node columns for dimensionality reduction.", gravity = 1.0 ) + public ListMultipleSelection getnodeAttributeList() { + if (network != null && nodeAttributeList == null) + nodeAttributeList = ModelUtils.updateNodeAttributeList(network, nodeAttributeList); + return nodeAttributeList; + } + public void setnodeAttributeList(ListMultipleSelection nal) { } + + @Tunable(description="Only use data from selected nodes", groups="Array sources", + longDescription="Only the data from the array sources of the selected nodes will be used.", + exampleStringValue = "false", + gravity = 1.5) + public boolean selectedOnly = false; + + @Tunable(description = "Number of neighbors", + + longDescription = "This parameter controls how UMAP balances local versus " + + "global structure in the data. It does this by constraining the size of " + + "the local neighborhood UMAP will look at when attempting to learn the manifold " + + "structure of the data. This means that low values of n_neighbors will force UMAP " + + "to concentrate on very local structure (potentially to the detriment of the big picture), " + + "while large values will push UMAP to look at larger neighborhoods of each point when " + + "estimating the manifold structure of the data, losing fine detail structure for the sake " + + "of getting the broader of the data.", + exampleStringValue = "2", + tooltip = "This parameter controls how UMAP balances local versus global structure
" + + "in the data. It does this by constraining the size of the local neighborhood
" + + "UMAP will look at when attempting to learn the manifold structure of the data.
" + + "This means that low value of Number of neighbors will force UMAP to concentrate on very
" + + "local structure (potentially to the detriment of the big picture), while large
" + + "values will push UMAP to look at larger neighborhoods of each point when estimating
" + + "the manifold structure of the data, losing fine detail structure for the sake
" + + "of getting the broader of the data.", + groups = {"UMAP Advanced Settings"}, gravity = 2.0) + public int n_neighbors = 15; + + @Tunable(description = "Minumum distance", + longDescription = "The min_dist parameter controls how tightly UMAP is allowed to pack points together. " + + "It, quite literally, provides the minimum distance apart that points are allowed to be in the low " + + "dimensional representation. This means that low values of min_dist will result in clumpier embeddings. " + + "This can be useful if you are interested in clustering, or in finer topological structure. " + + "Larger values of min_dist will prevent UMAP from packing points together and will focus on the preservation " + + "of the broad topological structure instead.", + exampleStringValue = "1.0", + tooltip = "The Minimum distance parameter controls how tightly UMAP is allowed to pack points together.
" + + "It, quite literally, provides the minimum distance apart that points are allowed to be in the low
" + + "dimensional representation. This means that low values of Minimum distance will result in clumpier embeddings.
" + + "This can be useful if you are interested in clustering, or in finer topological structure.
" + + "Larger values of Minimum distance will prevent UMAP from packing points together and will focus on the preservation
" + + "of the broad topological structure instead.", + groups = {"UMAP Advanced Settings"}, gravity = 3.0) + public double min_dist = 0.1; + + @Tunable(description = "Metric", + longDescription = "This controls how distance is computed in the ambient space of the input data. By default UMAP supports a wide variety of metrics.", + exampleStringValue = "euclidean", + tooltip = "This controls how distance is computed in the ambient space of the input data.", + groups = {"UMAP Advanced Settings"}, gravity = 4.0) + public ListSingleSelection metric = new ListSingleSelection("euclidean", "manhattan", "chebyshev", "minkowski", "canberra", "braycurtis", + "haversine", "mahalanobis", "wminkowski", "seuclidean", "cosine", "correlation", "hamming", "jaccard", "dice", "russellrao", "kulsinski", "rogerstanimoto", + "sokalmichener", "sokalsneath", "yule"); + + @Tunable(description = "Scale", + longDescription = "true/false. If true, preprocess the data to scale the matrix", + exampleStringValue = "True", + tooltip = "If checked, preprocess the data to scale the matrix", + groups = {"UMAP Advanced Settings"}, gravity = 5.0) + public Boolean scale = true; + + @Tunable(description = "Show scatter plot with results", + longDescription = "If this is set to ```true```, show the scatterplot after the calculation is complete", + exampleStringValue = "true", + tooltip = "If this is checked, show the scatterplot after the calculation is complete", + groups = {"UMAP Advanced Settings"}, gravity = 6.0) + public boolean showScatterPlot = true; + + @Tunable(description = "Synchronous", + longDescription = "If ```false``` the algorithm will run in the background after specified wait time", + exampleStringValue = "true", + tooltip = "If ```false``` the algorithm will run in the background after specified wait time", + groups = {"UMAP Advanced Settings"}, gravity = 7.0) + public boolean isSynchronous = false; + + //@ContainsTunables + //public AdvancedProperties advancedAttributes; + + //@ContainsTunables + //public NetworkVizProperties vizProperties = new NetworkVizProperties(); + + + public UMAPContext() { + // advancedAttributes = new AdvancedProperties("__umap", false); //this is the name of the column Integer that is created when click LOAD + } + + public UMAPContext(UMAPContext origin) { + // if (origin.advancedAttributes != null) + // advancedAttributes = new AdvancedProperties(origin.advancedAttributes); + // else + // advancedAttributes = new AdvancedProperties("__umap", false); + + nodeAttributeList = origin.nodeAttributeList; + n_neighbors = origin.n_neighbors; + min_dist = origin.min_dist; + metric = origin.metric; + scale = origin.scale; + showScatterPlot = origin.showScatterPlot; + isSynchronous = origin.isSynchronous; + } + + public void setNetwork(CyNetwork network) { + if (this.network != null && this.network.equals(network)) + return; + + this.network = network; + this.nodeAttributeList = null; + } + + public CyNetwork getNetwork() { return network; } + + // public String getClusterAttribute() { return advancedAttributes.clusterAttribute;} + + public void setUIHelper(TunableUIHelper helper) { + this.helper = helper; + + } + +} diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/umap/UMAPTaskFactory.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/umap/UMAPTaskFactory.java new file mode 100644 index 0000000..84416e0 --- /dev/null +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/dimensionalityReduction/umap/UMAPTaskFactory.java @@ -0,0 +1,58 @@ +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.dimensionalityReduction.umap; + +import java.util.Collections; +import java.util.List; + +import org.cytoscape.service.util.CyServiceRegistrar; +import org.cytoscape.work.TaskIterator; + +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.AbstractClusterTaskFactory; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterTaskFactory; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterViz; + +public class UMAPTaskFactory extends AbstractClusterTaskFactory { + UMAPContext context = null; + final CyServiceRegistrar registrar; + + public UMAPTaskFactory(ClusterManager clusterManager, CyServiceRegistrar registrar) { + super(clusterManager); + context = new UMAPContext(); + this.registrar = registrar; + } + + public String getName() {return UMAP.NAME;} + + public String getShortName() {return UMAP.SHORTNAME;} + + @Override + public String getLongDescription() { + return "Uniform Manifold Approximation and Projection (UMAP) is a dimension reduction "+ + "technique that can be used for visualisation similarly to t-SNE, but also for "+ + "general non-linear dimension reduction. The algorithm is founded on three assumptions "+ + "about the data:"+ + "
  1. The data is uniformly distributed on a Riemannian manifold;
  2. "+ + "
  3. The Riemannian metric is locally constant (or can be approximated as such);
  4. "+ + "
  5. The manifold is locally connected.
"+ + "From these assumptions it is possible to model the manifold with a fuzzy topological structure. The embedding is found by searching for a low dimensional projection of the data that has the closest possible equivalent fuzzy topological structure."+ + "

"+ + "The details for the underlying mathematics can be found in the paper on ArXiv:"+ + "

"+ + "McInnes, L, Healy, J, UMAP: Uniform Manifold Approximation and Projection for Dimension Reduction, ArXiv e-prints 1802.03426, 2018"; + } + + @Override + public ClusterViz getVisualizer() { + return null; + } + + @Override + public List getTypeList() { + return Collections.singletonList(ClusterTaskFactory.ClusterType.DIMRED); + } + + @Override + public TaskIterator createTaskIterator() { + return new TaskIterator(new UMAP(context, clusterManager, registrar)); + } +} diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/matrix/CyOjAlgoMatrix.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/matrix/CyOjAlgoMatrix.java index 2c0107c..0220ddf 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/matrix/CyOjAlgoMatrix.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/matrix/CyOjAlgoMatrix.java @@ -7,6 +7,7 @@ import org.cytoscape.model.CyNode; import org.ojalgo.matrix.store.PhysicalStore; +import org.ojalgo.matrix.store.Primitive64Store; import edu.ucsf.rbvi.clusterMaker2.internal.api.CyMatrix; import edu.ucsf.rbvi.clusterMaker2.internal.api.DistanceMetric; @@ -251,7 +252,7 @@ public void sortByRowLabels(boolean isNumeric) { String[] newRowLabels = new String[nRows]; CyNode[] newRowNodes = new CyNode[nRows]; - PhysicalStore newData = storeFactory.makeZero(nRows, nColumns); + Primitive64Store newData = (Primitive64Store)storeFactory.makeZero(nRows, nColumns); for (int row = 0; row < nRows; row++) { newRowLabels[row] = rowLabels[index[row]]; newRowNodes[row] = rowNodes[index[row]]; diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/matrix/OjAlgoMatrix.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/matrix/OjAlgoMatrix.java index db4376a..ccbde0a 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/matrix/OjAlgoMatrix.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/matrix/OjAlgoMatrix.java @@ -19,7 +19,6 @@ import cern.colt.matrix.tdouble.DoubleMatrix2D; import org.ojalgo.OjAlgoUtils; -import org.ojalgo.access.Access1D; import org.ojalgo.function.NullaryFunction; import org.ojalgo.function.PrimitiveFunction; import org.ojalgo.function.aggregator.Aggregator; @@ -27,14 +26,13 @@ import org.ojalgo.machine.Hardware; import org.ojalgo.machine.VirtualMachine; import org.ojalgo.matrix.BasicMatrix; -import org.ojalgo.matrix.BasicMatrix.Builder; -import org.ojalgo.matrix.PrimitiveMatrix; +import org.ojalgo.matrix.Primitive64Matrix; import org.ojalgo.matrix.decomposition.DecompositionStore; import org.ojalgo.matrix.decomposition.Eigenvalue; import org.ojalgo.matrix.store.ElementsSupplier; import org.ojalgo.matrix.store.MatrixStore; import org.ojalgo.matrix.store.PhysicalStore; -import org.ojalgo.matrix.store.PrimitiveDenseStore; +import org.ojalgo.matrix.store.Primitive64Store; import org.ojalgo.matrix.task.InverterTask; import org.ojalgo.matrix.task.SolverTask; import org.ojalgo.random.Binomial; @@ -46,7 +44,7 @@ import edu.ucsf.rbvi.clusterMaker2.internal.api.MatrixOps; public class OjAlgoMatrix implements Matrix { - protected PhysicalStore data; + protected Primitive64Store data; protected int[] index = null; protected int nRows; protected int nColumns; @@ -63,7 +61,8 @@ public class OjAlgoMatrix implements Matrix { public final OjAlgoOps ops; static VirtualMachine env = null; - protected final PhysicalStore.Factory storeFactory; + protected final Primitive64Matrix.Factory matrixFactory; + protected final PhysicalStore.Factory storeFactory; // For debugging messages private static DecimalFormat scFormat = new DecimalFormat("0.###E0"); @@ -71,7 +70,8 @@ public class OjAlgoMatrix implements Matrix { public OjAlgoMatrix() { nThreads = Runtime.getRuntime().availableProcessors()-1; - storeFactory = PrimitiveDenseStore.FACTORY; + matrixFactory = Primitive64Matrix.FACTORY; + storeFactory = Primitive64Store.FACTORY; if (env == null) { env = org.ojalgo.OjAlgoUtils.ENVIRONMENT; // System.out.println("Architecture = "+env.getArchitecture()); @@ -101,7 +101,7 @@ public OjAlgoMatrix(OjAlgoMatrix mat) { public OjAlgoMatrix(int rows, int columns) { this(); - data = storeFactory.makeZero(rows, columns); + data = (Primitive64Store)storeFactory.makeFilled(rows, columns, new MatrixFiller(0.0)); nRows = rows; nColumns = columns; rowLabels = new String[rows]; @@ -112,7 +112,7 @@ public OjAlgoMatrix(int rows, int columns) { public OjAlgoMatrix(int rows, int columns, double initialValue) { this(); MatrixFiller f = new MatrixFiller(initialValue); - data = storeFactory.makeFilled(rows, columns, f); + data = (Primitive64Store)storeFactory.makeFilled(rows, columns, f); nRows = rows; nColumns = columns; rowLabels = new String[rows]; @@ -176,7 +176,7 @@ public void initialize(int rows, int columns, double[][] arrayData) { if (arrayData != null) { data = storeFactory.rows(arrayData); } else { - data = storeFactory.makeZero(rows, columns); + data = (Primitive64Store)storeFactory.makeFilled(rows, columns, new MatrixFiller(0.0)); } nRows = (int)data.countRows(); nColumns = (int)data.countColumns(); @@ -188,7 +188,7 @@ public void initialize(int rows, int columns, double[][] arrayData) { } public void initialize(int rows, int columns, Double[][] arrayData) { - data = storeFactory.makeZero(rows, columns); + data = (Primitive64Store)storeFactory.makeFilled(rows, columns, new MatrixFiller(0.0)); nRows = (int)data.countRows(); nColumns = (int)data.countColumns(); if (arrayData != null) { @@ -776,7 +776,7 @@ public DoubleMatrix2D getColtMatrix() { protected Matrix copyDataFromMatrix(PhysicalStore matrix2D) { OjAlgoMatrix mat = new OjAlgoMatrix((int)matrix2D.countRows(), (int)matrix2D.countColumns()); mat.symmetric = true; - mat.data = matrix2D; + mat.data = (Primitive64Store)matrix2D; String[] labels; if (this.transposed) labels = rowLabels; @@ -800,7 +800,7 @@ protected int colStart(int row) { return row; } - class MatrixFiller implements NullaryFunction { + public class MatrixFiller implements NullaryFunction { double value; public MatrixFiller(double value) { this.value = value; diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/matrix/OjAlgoOps.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/matrix/OjAlgoOps.java index 03b1b79..1f3dd14 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/matrix/OjAlgoOps.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/matrix/OjAlgoOps.java @@ -7,20 +7,19 @@ import org.apache.log4j.Logger; import org.ojalgo.OjAlgoUtils; -import org.ojalgo.access.Access1D; import org.ojalgo.function.PrimitiveFunction; import org.ojalgo.function.aggregator.Aggregator; import org.ojalgo.function.aggregator.AggregatorFunction; +import org.ojalgo.function.constant.PrimitiveMath; import org.ojalgo.matrix.BasicMatrix; -import org.ojalgo.matrix.BasicMatrix.Builder; -import org.ojalgo.matrix.PrimitiveMatrix; +import org.ojalgo.matrix.Primitive64Matrix; import org.ojalgo.matrix.decomposition.DecompositionStore; import org.ojalgo.matrix.decomposition.Eigenvalue; import org.ojalgo.matrix.decomposition.SingularValue; import org.ojalgo.matrix.store.ElementsSupplier; import org.ojalgo.matrix.store.MatrixStore; import org.ojalgo.matrix.store.PhysicalStore; -import org.ojalgo.matrix.store.PrimitiveDenseStore; +import org.ojalgo.matrix.store.Primitive64Store; import org.ojalgo.matrix.task.InverterTask; import org.ojalgo.matrix.task.SolverTask; import org.ojalgo.scalar.ComplexNumber; @@ -60,7 +59,7 @@ public void threshold(final double thresh) { * Create a new matrix that is the transpose of this one */ public Matrix transpose() { - PhysicalStore data = matrix.storeFactory.transpose(matrix.data); + Primitive64Store data = matrix.storeFactory.transpose(matrix.data); OjAlgoMatrix result = new OjAlgoMatrix(matrix, data); result.transposed = true; return result; @@ -125,13 +124,13 @@ public void normalizeMatrix() { public double normalizeRow(int row) { double sum = rowSum(row); - matrix.data.modifyRow(row, 0L, PrimitiveFunction.DIVIDE.second(sum)); + matrix.data.modifyRow(row, 0L, PrimitiveMath.DIVIDE.second(sum)); return sum; } public double normalizeColumn(int column) { double sum = columnSum(column); - matrix.data.modifyColumn(0L, column, PrimitiveFunction.DIVIDE.second(sum)); + matrix.data.modifyColumn(0L, column, PrimitiveMath.DIVIDE.second(sum)); return sum; } @@ -266,7 +265,8 @@ public double rowVariance(int row, double mean) { } public Matrix covariance() { - PhysicalStore cov = matrix.storeFactory.makeZero(matrix.nColumns(), matrix.nColumns()); + OjAlgoMatrix mat = new OjAlgoMatrix(matrix.nColumns(), matrix.nColumns()); + Primitive64Store cov = mat.data; double[] columnMeans = new double[matrix.nColumns()]; for (int i=0; i < matrix.nColumns(); i++) { columnMeans[i] = columnMean(i); @@ -293,7 +293,7 @@ public Matrix covariance() { * Add a value to all cells in the matrix */ public void addScalar(double value) { - matrix.data.modifyAll(PrimitiveFunction.ADD.second(value)); + matrix.data.modifyAll(PrimitiveMath.ADD.second(value)); } /** @@ -302,14 +302,14 @@ public void addScalar(double value) { public void addElement(Matrix addend) { OjAlgoMatrix ojAddend = (OjAlgoMatrix)addend; MatrixStore d = matrix.data.add(ojAddend.data); - matrix.data = (PhysicalStore)d; + matrix.data = (Primitive64Store)d; } /** * Subtract a value to all cells in the matrix */ public void subtractScalar(double value) { - matrix.data.modifyAll(PrimitiveFunction.SUBTRACT.second(value)); + matrix.data.modifyAll(PrimitiveMath.SUBTRACT.second(value)); } /** @@ -318,14 +318,14 @@ public void subtractScalar(double value) { public void subtractElement(Matrix subend) { OjAlgoMatrix ojSubend = (OjAlgoMatrix)subend; MatrixStore d = matrix.data.subtract(ojSubend.data); - matrix.data = (PhysicalStore)d; + matrix.data = (Primitive64Store)d; } /** * Multiply a value to all cells in the matrix */ public void multiplyScalar(double value) { - matrix.data.modifyAll(PrimitiveFunction.MULTIPLY.second(value)); + matrix.data.modifyAll(PrimitiveMath.MULTIPLY.second(value)); } public Matrix multiplyMatrix(Matrix m2) { @@ -341,7 +341,7 @@ public Matrix multiplyMatrix(Matrix m2) { * Divide a value to all cells in the matrix */ public void divideScalar(double value) { - matrix.data.modifyAll(PrimitiveFunction.DIVIDE.second(value)); + matrix.data.modifyAll(PrimitiveMath.DIVIDE.second(value)); } /** @@ -354,7 +354,7 @@ public void divideScalar(double value) { * @param value to divide each cell in the column by */ public void divideScalarColumn(int column, double value) { - matrix.data.modifyColumn(0L, column, PrimitiveFunction.DIVIDE.second(value)); + matrix.data.modifyColumn(0L, column, PrimitiveMath.DIVIDE.second(value)); } /** @@ -379,7 +379,9 @@ private double covariance(int i, int j, double iMean, double jMean) { * Calculates the Pearson correlation matrix */ public Matrix correlation() { - PhysicalStore corr = matrix.storeFactory.makeZero(matrix.nColumns(), matrix.nColumns()); + OjAlgoMatrix ojCorr = new OjAlgoMatrix(matrix.nColumns(), matrix.nColumns()); + Primitive64Store corr = ojCorr.data; + // Primitive64Store corr = (Primitive64Store)matrix.storeFactory.makeFilled(matrix.nColumns(), matrix.nColumns()); double[] columnMeans = new double[matrix.nColumns()]; double[] columnStdDev = new double[matrix.nColumns()]; for (int i=0; i < matrix.nColumns(); i++) { @@ -415,7 +417,7 @@ public void eigenInit(){ public double[] eigenValues(boolean nonZero){ if (decomp == null) { - decomp = Eigenvalue.make(matrix.data); + decomp = Eigenvalue.PRIMITIVE.make(matrix.data); decomp.decompose(matrix.data); } @@ -439,7 +441,7 @@ public double[] eigenValues(boolean nonZero){ public double[][] eigenVectors(){ if (decomp == null) { - decomp = Eigenvalue.make(matrix.data); + decomp = Eigenvalue.PRIMITIVE.make(matrix.data); decomp.decompose(matrix.data); } @@ -454,15 +456,15 @@ public void svdInit(){ public Matrix svdU() { if (svdDecomp == null) { - svdDecomp = SingularValue.make(matrix.data); + svdDecomp = SingularValue.PRIMITIVE.make(matrix.data); svdDecomp.decompose(matrix.data); } - return wrap(svdDecomp.getQ1()); + return wrap(svdDecomp.getU()); } public Matrix svdS() { if (svdDecomp == null) { - svdDecomp = SingularValue.make(matrix.data); + svdDecomp = SingularValue.PRIMITIVE.make(matrix.data); svdDecomp.decompose(matrix.data); } return wrap(svdDecomp.getD()); @@ -470,10 +472,10 @@ public Matrix svdS() { public Matrix svdV() { if (svdDecomp == null) { - svdDecomp = SingularValue.make(matrix.data); + svdDecomp = SingularValue.PRIMITIVE.make(matrix.data); svdDecomp.decompose(matrix.data); } - return wrap(svdDecomp.getQ2()); + return wrap(svdDecomp.getV()); } public int cardinality() { return matrix.data.aggregateAll(Aggregator.CARDINALITY).intValue(); } diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/AbstractFuzzyNetworkClusterer.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/AbstractFuzzyNetworkClusterer.java index 3427dad..8beaa76 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/AbstractFuzzyNetworkClusterer.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/AbstractFuzzyNetworkClusterer.java @@ -36,34 +36,37 @@ public AbstractFuzzyNetworkClusterer(ClusterManager clusterManager) { protected void createFuzzyTable(List clusters){ + String tableSuidColumn = clusterAttributeName + "_Table.SUID"; + String fuzzyClusterTableName = clusterAttributeName + "_Table"; CyTable networkTable = network.getTable(CyNetwork.class, CyNetwork.LOCAL_ATTRS); - CyTable FuzzyClusterTable = null; - if(!CyTableUtil.getColumnNames(networkTable).contains(clusterAttributeName + "_Table.SUID")){ - - network.getDefaultNetworkTable().createColumn(clusterAttributeName + "_Table.SUID", Long.class, false); - FuzzyClusterTable = tableFactory.createTable(clusterAttributeName + "_Table", "Fuzzy_Node.SUID", Long.class, true, true); - - } - else{ - long FuzzyClusterTableSUID = network.getRow(network).get(clusterAttributeName + "_Table.SUID", Long.class); - FuzzyClusterTable = tableManager.getTable(FuzzyClusterTableSUID); + CyTable fuzzyClusterTable = null; + if(!CyTableUtil.getColumnNames(networkTable).contains(tableSuidColumn)) { + networkTable.createColumn(tableSuidColumn, Long.class, false); + fuzzyClusterTable = tableFactory.createTable(fuzzyClusterTableName, "Fuzzy_Node.SUID", Long.class, true, true); + } else { + Long fuzzyClusterTableSUID = networkTable.getRow(network.getSUID()).get(tableSuidColumn, Long.class); + if (fuzzyClusterTableSUID == null) { + fuzzyClusterTable = tableFactory.createTable(fuzzyClusterTableName, "Fuzzy_Node.SUID", Long.class, true, true); + } else { + fuzzyClusterTable = tableManager.getTable(fuzzyClusterTableSUID); + } } for(FuzzyNodeCluster cluster : clusters){ - if(FuzzyClusterTable.getColumn("Cluster_"+cluster.getClusterNumber()) == null){ - FuzzyClusterTable.createColumn("Cluster_"+cluster.getClusterNumber(), Double.class, false); + if(fuzzyClusterTable.getColumn("Cluster_"+cluster.getClusterNumber()) == null){ + fuzzyClusterTable.createColumn("Cluster_"+cluster.getClusterNumber(), Double.class, false); } } - CyRow TableRow; + CyRow tableRow; for(CyNode node: network.getNodeList()){ - TableRow = FuzzyClusterTable.getRow(node.getSUID()); + tableRow = fuzzyClusterTable.getRow(node.getSUID()); for(FuzzyNodeCluster cluster : clusters){ - TableRow.set("Cluster_"+cluster.getClusterNumber(), cluster.getMembership(node)); + tableRow.set("Cluster_"+cluster.getClusterNumber(), cluster.getMembership(node)); } } - network.getRow(network).set(clusterAttributeName + "_Table.SUID", FuzzyClusterTable.getSUID()); - tableManager.addTable(FuzzyClusterTable); + networkTable.getRow(network.getSUID()).set(tableSuidColumn, fuzzyClusterTable.getSUID()); + tableManager.addTable(fuzzyClusterTable); } } diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/AbstractNetworkClusterer.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/AbstractNetworkClusterer.java index 3dad875..f8de49f 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/AbstractNetworkClusterer.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/AbstractNetworkClusterer.java @@ -245,9 +245,17 @@ protected List getNetworkEdges(CyNetwork currentNetwork, Map then 1.0 - - if (attribute == "None") weight = null; + // Check to see if the weight is Integer, Long, or Double (we don't handle Lists at this point) + Double weight = null; + if (attribute != "None") { + Object oWeight = currentNetwork.getRow(edge).getRaw(attribute); + if (oWeight instanceof Double) + weight = (Double)oWeight; + else if (oWeight instanceof Integer) + weight = ((Integer)oWeight).doubleValue(); + else if (oWeight instanceof Long) + weight = ((Long)oWeight).doubleValue(); + } if (weight == null) { sourceTargetWeight[2] = "1.0"; diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/DBScan/DBScanCluster.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/DBScan/DBScanCluster.java new file mode 100644 index 0000000..e91b9df --- /dev/null +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/DBScan/DBScanCluster.java @@ -0,0 +1,148 @@ +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.DBScan; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.cytoscape.application.CyApplicationManager; +import org.cytoscape.group.CyGroup; +import org.cytoscape.model.CyEdge; +import org.cytoscape.model.CyNetwork; +import org.cytoscape.model.CyNode; +import org.cytoscape.model.CyRow; +import org.cytoscape.model.CyTable; +import org.cytoscape.service.util.CyServiceRegistrar; +import org.cytoscape.work.ContainsTunables; +import org.cytoscape.work.ProvidesTitle; +import org.cytoscape.work.TaskMonitor; +import org.json.simple.JSONArray; +import org.cytoscape.jobs.CyJob; +import org.cytoscape.jobs.CyJobData; +import org.cytoscape.jobs.CyJobDataService; +import org.cytoscape.jobs.CyJobExecutionService; +import org.cytoscape.jobs.CyJobManager; +import org.cytoscape.jobs.CyJobStatus; +import org.cytoscape.jobs.SUIDUtil; +import org.cytoscape.jobs.CyJobStatus.Status; + +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.AbstractClusterResults; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.NodeCluster; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.AbstractNetworkClusterer; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterResults; +import edu.ucsf.rbvi.clusterMaker2.internal.ui.NewNetworkView; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.ModelUtils; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJob; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJobData; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJobDataService; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJobExecutionService; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.RemoteServer; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJobHandler; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.NetworkClusterJobHandler; + +public class DBScanCluster extends AbstractNetworkClusterer { + public static String NAME = "DBScan Clusterer (remote)"; + public static String SHORTNAME = "dbscan"; + final CyServiceRegistrar registrar; + public final static String GROUP_ATTRIBUTE = "__DBScanGroups.SUID"; + + + @ContainsTunables + public DBScanContext context = null; + + public DBScanCluster(DBScanContext context, ClusterManager manager, CyServiceRegistrar registrar) { + super(manager); + this.context = context; + if (network == null) + network = clusterManager.getNetwork(); + context.setNetwork(network); + this.registrar = registrar; + } + + @Override + public String getShortName() {return SHORTNAME;} + + @Override + @ProvidesTitle + public String getName() {return NAME;} + + @Override + public void run(TaskMonitor taskMonitor) throws Exception { + monitor = taskMonitor; + // Get the execution service + CyJobExecutionService executionService = + registrar.getService(CyJobExecutionService.class, "(title=ClusterJobExecutor)"); + CyApplicationManager appManager = registrar.getService(CyApplicationManager.class); + CyNetwork currentNetwork = appManager.getCurrentNetwork(); //gets the network presented in Cytoscape + + clusterAttributeName = context.getClusterAttribute(); + createGroups = context.advancedAttributes.createGroups; + String attribute = context.getattribute().getSelectedValue(); + + HashMap configuration = new HashMap<>(); + if (context.isSynchronous == true) { + configuration.put("waitTime", -1); + } else { + configuration.put("waitTime", 20); + } + + // Get the arguments from our context + configuration.put("eps", context.eps); + configuration.put("min_samples", context.min_samples); + configuration.put("metric", context.metric.getSelectedValue()); + configuration.put("algorithm", context.algorithm.getSelectedValue()); + configuration.put("leaf_size", context.leaf_size); + configuration.put("p", context.p); + + HashMap nodeMap = getNetworkNodes(currentNetwork); + List nodeArray = new ArrayList<>(); + for (Long nodeSUID : nodeMap.keySet()) { + nodeArray.add(nodeMap.get(nodeSUID)); + } + + List edgeArray = getNetworkEdges(currentNetwork, nodeMap, attribute); + + String basePath = RemoteServer.getBasePath(); + + // Get our initial job + ClusterJob job = (ClusterJob) executionService.createCyJob("ClusterJob"); //creates a new ClusterJob object + // Get the data service + CyJobDataService dataService = job.getJobDataService(); //gets the dataService of the execution service + // Add our data + CyJobData jobData = dataService.addData(null, "nodes", nodeArray); + jobData = dataService.addData(jobData, "edges", edgeArray); + job.storeClusterData(clusterAttributeName, currentNetwork, clusterManager, createGroups, GROUP_ATTRIBUTE, null, getShortName()); + // Create our handler + NetworkClusterJobHandler jobHandler = new NetworkClusterJobHandler(job, network, context.vizProperties.showUI, context.vizProperties.restoreEdges); + job.setJobMonitor(jobHandler); + // Submit the job + CyJobStatus exStatus = executionService.executeJob(job, basePath, configuration, jobData); + + CyJobStatus.Status status = exStatus.getStatus(); + System.out.println("Status: " + status); + + if (status == Status.FINISHED) { + jobHandler.loadData(job, taskMonitor); + } else if (status == Status.RUNNING + || status == Status.SUBMITTED + || status == Status.QUEUED) { + CyJobManager manager = registrar.getService(CyJobManager.class); + manager.addJob(job, jobHandler, 5); //this one shows the load button + + } else if (status == Status.ERROR) { + monitor.showMessage(TaskMonitor.Level.ERROR, "Job error: " + exStatus.getMessage()); + } else if (status == Status.UNKNOWN + || status == Status.CANCELED + || status == Status.FAILED + || status == Status.TERMINATED + || status == Status.PURGED) { + monitor.setStatusMessage("Job status: " + status); + } + + // Save our SUIDs in case we get saved and restored + SUIDUtil.saveSUIDs(job, currentNetwork, currentNetwork.getNodeList()); + + } + +} diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/DBScan/DBScanClusterTaskFactory.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/DBScan/DBScanClusterTaskFactory.java new file mode 100644 index 0000000..14d5784 --- /dev/null +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/DBScan/DBScanClusterTaskFactory.java @@ -0,0 +1,101 @@ +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.DBScan; + +import java.util.Collections; +import java.util.List; + +import org.cytoscape.model.CyNetwork; +import org.cytoscape.service.util.CyServiceRegistrar; +import org.cytoscape.task.NetworkTaskFactory; +import org.cytoscape.work.TaskIterator; + +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.AbstractClusterTaskFactory; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.MCL.MCLCluster; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterViz; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterTaskFactory.ClusterType; + +public class DBScanClusterTaskFactory extends AbstractClusterTaskFactory{ + DBScanContext context = null; + final CyServiceRegistrar registrar; + + public DBScanClusterTaskFactory(ClusterManager clusterManager, CyServiceRegistrar registrar) { + super(clusterManager); + context = new DBScanContext(); + this.registrar = registrar; + } + + public String getName() {return DBScanCluster.NAME;} + + public String getShortName() {return DBScanCluster.SHORTNAME;} + + @Override + public String getLongDescription() { + return "This function implements the DBScan (Density-Based Spaciel Clustering of Applications with Noise)"+ + "algorithm for finding community structure. It finds core samples of high density and expands clusters"+ + "from them. Good for data which contains clusters of similar density."+ + "see Ester, M., H.P.Kriegel, J. Sander, and X. Xu, "+ + "A Density-Based Algorithm for Discovering Clusters in Large Spatial Databases with Noise."+ + "In Proceedings of the 2nd International Conference on Knowledge Discovery and Data Mining, "+ + "Portland, OR, AAAI Press, pp. 226–231. 1996"+ + "

"+ + "

The DBSCAN algorithm views clusters as areas of high"+ + "density separated by areas of low density. Due to this rather"+ + "generic view, clusters found by DBSCAN can be any shape,"+ + "as opposed to k-means which assumes that clusters are convex"+ + "shaped. The central component to the DBSCAN is the concept"+ + "of core samples, which are samples that are in areas of"+ + "high density. A cluster is therefore a set of core samples,"+ + "each close to each other (measured by some distance measure)"+ + "and a set of non-core samples that are close to a core sample"+ + "(but are not themselves core samples). There are two parameters"+ + "to the algorithm, min_samples and eps, which define formally"+ + "what we mean when we say dense. Higher min_samples or lower"+ + "eps indicate higher density necessary to form a cluster."+ + "

More formally, we define a core sample as being a sample"+ + "in the dataset such that there exist min_samples other samples"+ + "within a distance of eps, which are defined as neighbors"+ + "of the core sample. This tells us that the core sample is"+ + "in a dense area of the vector space. A cluster is a set of"+ + "core samples that can be built by recursively taking a core"+ + "sample, finding all of its neighbors that are core samples,"+ + "finding all of their neighbors that are core samples,"+ + "and so on. A cluster also has a set of non-core samples,"+ + "which are samples that are neighbors of a core sample in the"+ + "cluster but are not themselves core samples. Intuitively,"+ + "these samples are on the fringes of a cluster."+ + "

Any core sample is part of a cluster, by definition. Any"+ + "sample that is not a core sample, and is at least eps in"+ + "distance from any core sample, is considered an outlier by"+ + "the algorithm."+ + "While the parameter min_samples primarily controls how"+ + "tolerant the algorithm is towards noise (on noisy and large"+ + "data sets it may be desirable to increase this parameter),"+ + "the parameter eps is crucial to choose appropriately for the"+ + "data set and distance function and usually cannot be left"+ + "at the default value. It controls the local neighborhood"+ + "of the points. When chosen too small, most data will not be"+ + "clustered at all (and labeled as -1 for “noise”). When"+ + "chosen too large, it causes close clusters to be merged"+ + "into one cluster, and eventually the entire data set to be"+ + "returned as a single cluster. Some heuristics for choosing"+ + "this parameter have been discussed in the literature, for"+ + "example based on a knee in the nearest neighbor distances plot."; + } + + @Override + public ClusterViz getVisualizer() { + return null; + } + + @Override + public List getTypeList() { + return Collections.singletonList(ClusterType.NETWORK); + } + + @Override + public TaskIterator createTaskIterator() { + return new TaskIterator(new DBScanCluster(context, clusterManager, registrar)); + } + + +} diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/DBScan/DBScanContext.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/DBScan/DBScanContext.java new file mode 100644 index 0000000..4c15f0f --- /dev/null +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/DBScan/DBScanContext.java @@ -0,0 +1,142 @@ +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.DBScan; + + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; + +import org.cytoscape.model.CyColumn; +import org.cytoscape.model.CyNetwork; +import org.cytoscape.model.CyTable; +import org.cytoscape.work.ContainsTunables; +import org.cytoscape.work.Tunable; +import org.cytoscape.work.swing.TunableUIHelper; +import org.cytoscape.work.util.ListSingleSelection; + +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterAlgorithmContext; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.ModelUtils; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.AdvancedProperties; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.edgeConverters.EdgeAttributeHandler; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.NetworkVizProperties; + +public class DBScanContext implements ClusterAlgorithmContext { + CyNetwork network; + TunableUIHelper helper; + + //Tunables + // + private ListSingleSelection attribute ; + @Tunable(description = "Attribute", groups={"Source for array data"}, params="displayState=uncollapsed", + longDescription = "The column containing the data to be used for the clustering. "+ + "If no weight column is used, select ```--NONE---```", + exampleStringValue = "weight", + gravity=1.0) + public ListSingleSelection getattribute(){ + attribute = ModelUtils.updateEdgeAttributeList(network, attribute); + return attribute; + } + public void setattribute(ListSingleSelection attr) { } + + @Tunable(description = "EPS", + longDescription = "The maximum distance between two samples for one to be considered as in the neighborhood of the other.", + exampleStringValue = "0.5", + tooltip = "The maximum distance between two samples for one to be considered as in the neighborhood of the other. This is not a maximum bound on the distances of points within a cluster. This is the most important DBSCAN parameter to choose appropriately for your data set and distance function.", + groups = {"DBScan Advanced Settings"}, gravity = 1.0) + public double eps = 0.5; + + @Tunable(description = "Minimum Samples", + longDescription = "The number of samples (or total weight) in a neighborhood for a point to be considered as a core point. This includes the point itself.", + exampleStringValue = "5", + tooltip = "The number of samples (or total weight) in a neighborhood for a point to be considered as a core point. This includes the point itself.", + groups = {"DBScan Advanced Settings"}, gravity = 2.0) + public int min_samples = 5; + + @Tunable(description = "Metric", + longDescription = "The metric to use when calculating distance between instances in a feature array.", + exampleStringValue = "euclidean", + tooltip = "The metric to use when calculating distance between instances in a feature array. It must be one of:

  • cityblock
  • cosine
  • euclidean
  • l1
  • l2
  • manhattan
  • braycurtis
  • canberra
  • chebyshev
  • correlation
  • dice
  • hamming
  • jaccard
  • kulsinski
  • mahalanobis
  • minkowski
  • rogerstanimoto
  • russellrao
  • seuclidean
  • sokalmicherner
  • sokalsneath
  • sqeuclidean
  • yule
", + groups = {"DBScan Advanced Settings"}, gravity = 3.0) + public ListSingleSelection metric = new ListSingleSelection("cityblock", "cosine", "euclidean", "l1", "l2", "manhattan", + "braycurtis", "canberra", "chebyshev", "correlation", "dice", + "hamming", "jaccard", "kulsinski", "mahalanobis", "minkowski", + "rogerstanimoto", "russellrao", "seuclidean", "sokalmichener", + "sokalsneath", "sqeuclidean", "yule"); + + @Tunable(description = "Algorithm", + longDescription = "The algorithm to be used by the NearestNeighbors module to compute pointwise distances and find nearest neighbors.", + tooltip = "The algorithm to be used by the NearestNeighbors module to compute pointwise distances and find nearest neighbors.", + exampleStringValue = "auto", + groups = {"DBScan Advanced Settings"}, gravity = 4.0) + public ListSingleSelection algorithm = new ListSingleSelection("auto", "ball_tree", "kd_tree", "brute"); + + + @Tunable(description = "Leaf size", + longDescription = "Leaf size passed to BallTree or cKDTree. This can affect the speed of the construction and query, as well as the memory required to store the tree. The optimal value depends on the nature of the problem.", + tooltip = "Leaf size passed to BallTree or cKDTree. This can affect the speed of the construction and query, as well as the memory required to store the tree. The optimal value depends on the nature of the problem.", + exampleStringValue = "30", + groups = {"DBScan Advanced Settings"}, gravity = 5.0) + public int leaf_size = 30; + + + @Tunable(description = "P", + longDescription = "The power of the Minkowski metric to be used to calculate distance between points.", + + tooltip = "The power of the Minkowski metric to be used to calculate distance between points.", + exampleStringValue = "2.0", + groups = {"DBScan Advanced Settings"}, gravity = 5.0) + public double p = 2; + + // We let the back-end handle this + // public int n_jobs; + + @Tunable(description = "Synchronous", + longDescription = "If ```false``` the algorithm will run in the background after specified wait time", + exampleStringValue = "true", + tooltip = "If ```false``` the algorithm will run in the background after specified wait time", + groups = {"DBScan Advanced Settings"}, gravity = 6.0) + public boolean isSynchronous = false; + + @ContainsTunables + public AdvancedProperties advancedAttributes; + + @ContainsTunables + public NetworkVizProperties vizProperties = new NetworkVizProperties(); + + public DBScanContext() { + advancedAttributes = new AdvancedProperties("__dbscanCluster", false); //this is the name of the column Integer that is created when click LOAD + metric.setSelectedValue("euclidean"); + } + + public DBScanContext(DBScanContext origin) { + if (origin.advancedAttributes != null) + advancedAttributes = new AdvancedProperties(origin.advancedAttributes); + else + advancedAttributes = new AdvancedProperties("__dbscanCluster", false); + + eps = origin.eps; + min_samples = origin.min_samples; + metric = origin.metric; + algorithm = origin.algorithm; + leaf_size = origin.leaf_size; + p = origin.p; + + } + + public void setNetwork(CyNetwork network) { + if (this.network != null && this.network.equals(network)) + return; + + this.network = network; + } + + public CyNetwork getNetwork() { return network; } + + public String getClusterAttribute() { return advancedAttributes.clusterAttribute;} + + public void setUIHelper(TunableUIHelper helper) { + this.helper = helper; + + } + +} diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/FCM/FCMCluster.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/FCM/FCMCluster.java index 37c1a24..b9ca596 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/FCM/FCMCluster.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/FCM/FCMCluster.java @@ -124,7 +124,7 @@ public void run( TaskMonitor taskmonitor) { int[] mostRelevantCluster = new int[network.getNodeList().size()]; - FuzzyNodeCluster.fuzzyClusterCount = 0; + FuzzyNodeCluster.reset(); runFCM = new RunFCM(distanceMatrix, context.iterations, context.cNumber, context.fIndex, context.beta, context.membershipThreshold.getValue(), context.maxThreads, this.monitor); diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/FastGreedy/FastGreedy.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/FastGreedy/FastGreedy.java index 851f524..61c0fe8 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/FastGreedy/FastGreedy.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/FastGreedy/FastGreedy.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; import org.cytoscape.application.CyApplicationManager; import org.cytoscape.jobs.CyJobData; @@ -15,20 +16,23 @@ import org.cytoscape.model.CyNetwork; import org.cytoscape.service.util.CyServiceRegistrar; import org.cytoscape.work.ContainsTunables; +import org.cytoscape.work.ProvidesTitle; import org.cytoscape.work.TaskMonitor; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.NodeCluster; import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.AbstractNetworkClusterer; import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.FastGreedy.FastGreedyContext; import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; import edu.ucsf.rbvi.clusterMaker2.internal.ui.NewNetworkView; import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJob; import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJobHandler; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.NetworkClusterJobHandler; import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.RemoteServer; import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJobExecutionService; public class FastGreedy extends AbstractNetworkClusterer { - public static String NAME = "Fast Greedy"; + public static String NAME = "Fast Greedy (remote)"; public static String SHORTNAME = "fastgreedy"; final CyServiceRegistrar registrar; public final static String GROUP_ATTRIBUTE = "__FastGreedyGroups.SUID"; @@ -49,10 +53,12 @@ public FastGreedy(FastGreedyContext context, ClusterManager manager, CyServiceRe public String getShortName() {return SHORTNAME;} @Override + @ProvidesTitle public String getName() {return NAME;} @Override public void run(TaskMonitor taskMonitor) throws Exception { + monitor = taskMonitor; // Get the execution service CyJobExecutionService executionService = registrar.getService(CyJobExecutionService.class, "(title=ClusterJobExecutor)"); @@ -62,6 +68,13 @@ public void run(TaskMonitor taskMonitor) throws Exception { clusterAttributeName = context.getClusterAttribute(); createGroups = context.advancedAttributes.createGroups; String attribute = context.getattribute().getSelectedValue(); + + HashMap configuration = new HashMap<>(); + if (context.isSynchronous == true) { + configuration.put("waitTime", -1); + } else { + configuration.put("waitTime", 20); + } HashMap nodeMap = getNetworkNodes(currentNetwork); List nodeArray = new ArrayList<>(); @@ -82,28 +95,24 @@ public void run(TaskMonitor taskMonitor) throws Exception { jobData = dataService.addData(jobData, "edges", edgeArray); job.storeClusterData(clusterAttributeName, currentNetwork, clusterManager, createGroups, GROUP_ATTRIBUTE, null, getShortName()); // Create our handler - ClusterJobHandler jobHandler = new ClusterJobHandler(job, network); + NetworkClusterJobHandler jobHandler = new NetworkClusterJobHandler(job, network, context.vizProperties.showUI, context.vizProperties.restoreEdges); job.setJobMonitor(jobHandler); // Submit the job - CyJobStatus exStatus = executionService.executeJob(job, basePath, null, jobData); + CyJobStatus exStatus = executionService.executeJob(job, basePath, configuration, jobData); CyJobStatus.Status status = exStatus.getStatus(); - System.out.println("Status: " + status); + if (status == Status.FINISHED) { - executionService.fetchResults(job, dataService.getDataInstance()); - if (context.vizProperties.showUI) { - taskMonitor.showMessage(TaskMonitor.Level.INFO, "Creating network"); - insertTasksAfterCurrentTask(new NewNetworkView(network, clusterManager, true, context.vizProperties.restoreEdges, false)); - } - + jobHandler.loadData(job, taskMonitor); } else if (status == Status.RUNNING || status == Status.SUBMITTED || status == Status.QUEUED) { CyJobManager manager = registrar.getService(CyJobManager.class); manager.addJob(job, jobHandler, 5); //this one shows the load button - } else if (status == Status.ERROR - || status == Status.UNKNOWN + } else if (status == Status.ERROR) { + monitor.showMessage(TaskMonitor.Level.ERROR, "Job error: " + exStatus.getMessage()); + } else if (status == Status.UNKNOWN || status == Status.CANCELED || status == Status.FAILED || status == Status.TERMINATED diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/FastGreedy/FastGreedyContext.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/FastGreedy/FastGreedyContext.java index a1f674e..2ee0a25 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/FastGreedy/FastGreedyContext.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/FastGreedy/FastGreedyContext.java @@ -29,6 +29,13 @@ public ListSingleSelection getattribute(){ } public void setattribute(ListSingleSelection attr) { } + @Tunable(description = "Synchronous", + longDescription = "If ```false``` the algorithm will run in the background after specified wait time", + exampleStringValue = "true", + tooltip = "If ```false``` the algorithm will run in the background after specified wait time", + groups = {"FastGreedy Advanced Settings"}, gravity = 6.0) + public boolean isSynchronous = false; + @ContainsTunables public AdvancedProperties advancedAttributes; @@ -46,6 +53,7 @@ public FastGreedyContext(FastGreedyContext origin) { advancedAttributes = new AdvancedProperties("__fastGreedyCluster", false); attribute = origin.attribute; + isSynchronous = origin.isSynchronous; } public void setNetwork(CyNetwork network) { diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/FastGreedy/FastGreedyTaskFactory.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/FastGreedy/FastGreedyTaskFactory.java index 9c8e052..12c7aea 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/FastGreedy/FastGreedyTaskFactory.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/FastGreedy/FastGreedyTaskFactory.java @@ -26,11 +26,6 @@ public FastGreedyTaskFactory(ClusterManager clusterManager, CyServiceRegistrar r public String getShortName() {return FastGreedy.SHORTNAME;} - @Override - public String getLongDescription() { - return ""; - } - @Override public ClusterViz getVisualizer() { return null; @@ -45,4 +40,11 @@ public List getTypeList() { public TaskIterator createTaskIterator() { return new TaskIterator(new FastGreedy(context, clusterManager, registrar)); } + + @Override + public String getLongDescription() { + return "This function implements the fast greedy modularity optimization algorithm for "+ + "finding community structure, see A Clauset, MEJ Newman, C Moore: Finding community "+ + "structure in very large networks, http://www.arxiv.org/abs/cond-mat/0408187 for the details."; + } } diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Fuzzifier/Fuzzifier.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Fuzzifier/Fuzzifier.java index da844c6..ccd69cf 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Fuzzifier/Fuzzifier.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Fuzzifier/Fuzzifier.java @@ -74,7 +74,7 @@ public class Fuzzifier extends AbstractFuzzyNetworkClusterer{ private boolean ignoreMissing = true; CyTableFactory tableFactory = null; CyTableManager tableManager = null; - private List Clusters = null; + private List clusters = null; private int cNumber; private String[] attributeArray = new String[1]; @@ -95,6 +95,8 @@ public Fuzzifier(FuzzifierContext context, ClusterManager manager) { tableManager = clusterManager.getTableManager(); } context.setNetwork(network); + context.edgeAttributeHandler.adjustLoops = false; + super.network = network; } @@ -117,12 +119,16 @@ public void run( TaskMonitor monitor) { network = clusterManager.getNetwork(); super.network = network; - this.Clusters = getClusters(); - this.cNumber = Clusters.size(); - // Make sure to update the context context.setNetwork(network); + // Update our tunable results + clusterAttributeName = context.getClusterAttribute(); + + NodeCluster.reset(); + this.clusters = getClusters(); + this.cNumber = clusters.size(); + Long networkID = network.getSUID(); CyTable nodeAttributes = network.getDefaultNodeTable(); @@ -133,11 +139,8 @@ public void run( TaskMonitor monitor) { return; } - // Update our tunable results - clusterAttributeName = context.getClusterAttribute(); - - runFuzzifier = new RunFuzzifier(Clusters, distanceMatrix, cNumber, - context.membershipThreshold.getValue(), context.maxThreads, monitor); + runFuzzifier = new RunFuzzifier(clusters, distanceMatrix, cNumber, + context.membershipThreshold.getValue(), 0, monitor); runFuzzifier.setDebug(debug); @@ -145,8 +148,9 @@ public void run( TaskMonitor monitor) { monitor.showMessage(TaskMonitor.Level.INFO,"Clustering..."); - List FuzzyClusters = runFuzzifier.run(network, monitor); - if (FuzzyClusters == null) return; // Canceled? + FuzzyNodeCluster.reset(); + List fuzzyClusters = runFuzzifier.run(network, context, monitor); + if (fuzzyClusters == null) return; // Canceled? monitor.showMessage(TaskMonitor.Level.INFO,"Removing groups"); @@ -158,9 +162,9 @@ public void run( TaskMonitor monitor) { params = new ArrayList(); context.edgeAttributeHandler.setParams(params); - List> nodeClusters = createFuzzyGroups(network, FuzzyClusters, GROUP_ATTRIBUTE); + List> nodeClusters = createFuzzyGroups(network, fuzzyClusters, GROUP_ATTRIBUTE); - results = new AbstractClusterResults(network, FuzzyClusters); + results = new AbstractClusterResults(network, fuzzyClusters); monitor.showMessage(TaskMonitor.Level.INFO, "Done. Fuzzifier results:\n"+results); if (context.vizProperties.showUI) { @@ -170,12 +174,12 @@ public void run( TaskMonitor monitor) { context.vizProperties.restoreEdges, !context.edgeAttributeHandler.selectedOnly)); } else { - monitor.showMessage(TaskMonitor.Level.INFO, "Done. Fizzifier results:\n"+results); + monitor.showMessage(TaskMonitor.Level.INFO, "Done. Fuzzifier results:\n"+results); } - System.out.println("Creating fuzzy table"); - createFuzzyTable(FuzzyClusters); - System.out.println("Done"); + // System.out.println("Creating fuzzy table"); + createFuzzyTable(fuzzyClusters); + // System.out.println("Done"); } @@ -196,17 +200,36 @@ public List getClusters(){ if (network == null) return nodeClusters; List nodeList = network.getNodeList(); - clusterAttributeName = network.getRow(network).get("__clusterAttribute", String.class); - - // Save the seed cluster we used - ModelUtils.createAndSetLocal(network, network, "__fuzzifierSeed", - clusterAttributeName, String.class, null); + String clusterName = network.getRow(network).get("__clusterAttribute", String.class); + + // Special case. If the clusterAttribute is __fuzzifierCluster, then we're repeating this, + // so use the __fuzzifierSeed instead + if (clusterName.equals(clusterAttributeName)) { + // We're reclustering + clusterName = network.getRow(network).get("__fuzzifierSeed", String.class); + + // We need to do some resetting + if (ModelUtils.hasColumn(network, network.getDefaultNetworkTable(), "__fuzzifierCluster_Table.SUID")) { + Long fuzzyTableSUID = network.getRow(network).get("__fuzzifierCluster_Table.SUID", Long.class); + if (fuzzyTableSUID != null) { + tableManager.deleteTable(fuzzyTableSUID); + ModelUtils.deleteColumnLocal(network, CyNetwork.class, "__fuzzifierCluster_Table.SUID"); + } + } + } else { + // Save the seed cluster we used + ModelUtils.createAndSetLocal(network, network, "__fuzzifierSeed", + clusterName, String.class, null); + } + + if (!ModelUtils.hasColumn(network, network.getDefaultNodeTable(), clusterName)) + throw new RuntimeException("Can't find cluster attribute: "+clusterName); for(CyNode node : nodeList){ // System.out.println("Node SUID:"+node.getSUID()); - if (ModelUtils.hasAttribute(network, node, clusterAttributeName)){ + if (ModelUtils.hasAttribute(network, node, clusterName)){ - Integer cluster = network.getRow(node).get(clusterAttributeName, Integer.class); + Integer cluster = network.getRow(node).get(clusterName, Integer.class); // System.out.println("Cluster for "+node.getSUID()+"--"+cluster); if (!clusterMap.containsKey(cluster)) clusterMap.put(cluster, new ArrayList()); @@ -216,7 +239,10 @@ public List getClusters(){ } for (int key : clusterMap.keySet()){ - nodeClusters.add(new NodeCluster(clusterMap.get(key))); + if (clusterMap.get(key).size() > context.minClusterSize) { + NodeCluster nodeCluster = new NodeCluster(key, clusterMap.get(key)); + nodeClusters.add(nodeCluster); + } } // System.out.println("NodeCluster Size : " +nodeClusters.size()); return nodeClusters; diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Fuzzifier/FuzzifierContext.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Fuzzifier/FuzzifierContext.java index ef09024..d9db77d 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Fuzzifier/FuzzifierContext.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Fuzzifier/FuzzifierContext.java @@ -27,10 +27,13 @@ public class FuzzifierContext implements ClusterAlgorithmContext { public EdgeAttributeHandler edgeAttributeHandler; @Tunable(description = "Threshold for Fuzzy Membership in a Cluster", groups={"Fuzzifier Advanced Settings"}, params="displayState=collapsed, slider=true",gravity=20.0) - public BoundedDouble membershipThreshold = new BoundedDouble(0.0, 0.2, 1.0, false, false); + public BoundedDouble membershipThreshold = new BoundedDouble(0.0, 0.4, 1.0, false, false); - @Tunable(description = "Maximum number of threads", groups={"Fuzzifier Advanced Settings"}, gravity=21.0) - public int maxThreads = 0; + // @Tunable(description = "Maximum number of threads", groups={"Fuzzifier Advanced Settings"}, gravity=21.0) + // public int maxThreads = 0; + + @Tunable(description = "Minimum cluster size to consider", groups={"Fuzzifier Advanced Settings"}, gravity=22.0) + public int minClusterSize = 1; /* @Tunable(description = "Distance Metric", groups={"Fuzzifier Advanced Settings"}, gravity=22.0) @@ -60,7 +63,7 @@ public FuzzifierContext(FuzzifierContext origin) { membershipThreshold = origin.membershipThreshold; - maxThreads = origin.maxThreads; + // maxThreads = origin.maxThreads; /* distanceMetric = new ListSingleSelection(DistanceMetric.VALUE_IS_CORRELATION, DistanceMetric.UNCENTERED_CORRELATION, diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Fuzzifier/RunFuzzifier.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Fuzzifier/RunFuzzifier.java index 9a4a104..70fd07b 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Fuzzifier/RunFuzzifier.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Fuzzifier/RunFuzzifier.java @@ -51,7 +51,7 @@ public class RunFuzzifier { HashMap> groupMap = null; - private List Clusters = null; + private List clusters = null; private int number_clusters; private List nodeList= null; private boolean canceled = false; @@ -65,11 +65,12 @@ public class RunFuzzifier { double membershipThreshold = 0; private boolean debug = false; private int nThreads = Runtime.getRuntime().availableProcessors()-1; + private Map baseMembership; - public RunFuzzifier (List Clusters, CyMatrix distanceMatrix, int cClusters, - double membershipThreshold,int maxThreads, TaskMonitor monitor ){ + public RunFuzzifier (List clusters, CyMatrix distanceMatrix, int cClusters, + double membershipThreshold, int maxThreads, TaskMonitor monitor ){ - this.Clusters = Clusters; + this.clusters = clusters; this.distanceMatrix = distanceMatrix; this.number_clusters = cClusters; this.monitor = monitor; @@ -86,6 +87,15 @@ public RunFuzzifier (List Clusters, CyMatrix distanceMatrix, int cC //monitor.showMessage(TaskMonitor.Level.INFO,"Matrix info: = "+distanceMatrix.printMatrixInfo(matrix)); monitor.showMessage(TaskMonitor.Level.INFO,"Number of Clusters = "+number_clusters); + // Create a map to record each node's membership + this.baseMembership = new HashMap<>(); + for (NodeCluster c: clusters) { + Integer clusterNumber = c.getClusterNumber()-1; + for (CyNode n: c) { + baseMembership.put(n, clusterNumber); + } + } + } public void cancel () { canceled = true; } @@ -98,91 +108,234 @@ public RunFuzzifier (List Clusters, CyMatrix distanceMatrix, int cC * @return List of FuzzyNodeCLusters */ - public List run(CyNetwork network, TaskMonitor monitor){ - - Long networkID = network.getSUID(); + public List run(CyNetwork network, FuzzifierContext context, TaskMonitor monitor){ + + /** + * Algorithm (transcoded from the R usedist package's dist_to_centroids method: + * Given the matrix distMatrix: + * d2 = distMatrix ** 2 + * group_items = list of nodes in each cluster + * group_sizes = sizes of each cluster + * group_d2s = list of subportions of the d2 for each cluster + * within_group_sums = list of sums for each group_d2s + * foreach node: + * foreach group: + * idx1 = group_items[group] + * n1 = group_sizes[group] + * sum1 = winthin_group_sums[group] + * sum12 = sum(d2[idx1, node]) # Note: idx1 is a vector + * term1 = sum1 / n1**2 + * term12 = sum12 / n1 + * result_squared = term12 - term1 + * dist[node, group] = sqrt(result_sqared) + */ long startTime = System.currentTimeMillis(); int nelements = distanceMatrix.nRows(); nodeList = distanceMatrix.getRowNodes(); + int max_cluster = Math.max(NodeCluster.getMaxClusterNumber(), number_clusters); //Matrix to store the temporary cluster membership values of elements - double [][] ClusterMemberships = new double[nelements][number_clusters]; + double [][] clusterMemberships = new double[nelements][max_cluster]; - //Initializing all membership values to 0 + //Initializing all membership values to NaN for (int i = 0; i < nelements; i++){ - for (int j = 0; j < number_clusters; j++){ - ClusterMemberships[i][j] = 0; + for (int j = 0; j < max_cluster; j++){ + clusterMemberships[i][j] = Double.NaN; } } - // This matrix will store the centroid data - CyMatrix cData = CyMatrixFactory.makeSmallMatrix(network, number_clusters, nelements); - - getFuzzyCenters(cData); - - for (CyNode node : nodeList){ - int nodeIndex = nodeList.indexOf(node); - double sumDistances = 0; - for (int i = 0 ; i < Clusters.size(); i++){ - sumDistances += cData.doubleValue(i, nodeIndex); - } - for(int i = 0 ; i < Clusters.size(); i++){ - ClusterMemberships[nodeIndex][i] = cData.doubleValue(i, nodeIndex)/sumDistances; - } - } - - HashMap membershipMap = createMembershipMap(ClusterMemberships); + monitor.showMessage(TaskMonitor.Level.INFO, "Calculating distances"); + + CyMatrix d2 = distanceMatrix.copy(); + // System.out.println(d2.printMatrix()); + d2.ops().powScalar(2); + // System.out.println(d2.printMatrix()); + List group_items = clusters; + + int[] group_sizes = get_sizes(group_items); + // for (int i = 0; i < group_sizes.length; i++) { System.out.println("Group "+i+" has "+group_sizes[i]+" elements"); } + double[] within_group_sums = get_sums(d2, group_items); + // for (int i = 0; i < within_group_sums.length; i++) { System.out.println("Group "+i+" has sum "+within_group_sums[i]); } + boolean watch = false; + for (CyNode node: nodeList) { + /* Debugging + if (node.getSUID() == 5705) + watch = true; + else + watch = false; + */ + // This part can be done in parallel + // get_distance(d2, node_index, group, group_size, clusterMemberships) + // clusterMemberships needs to be in a critical section. + for (NodeCluster idx1: group_items) { + int group = idx1.getClusterNumber()-1; + if (watch) System.out.println("group = "+group); + int n1 = group_sizes[group]; + double sum1 = within_group_sums[group]; + if (sum1 == 0.0d) + continue; + double sum12 = get_sum(d2, idx1, node, watch); + if (watch) System.out.println("sum12 = "+sum12); + double term1 = sum1 / Math.pow(n1, 2); + if (watch) System.out.println("term1 = "+term1); + double term12 = sum12 / n1; + if (watch) System.out.println("term12 = "+term12); + double result_squared = term12 - term1; + // clusterMemberships[nodeList.indexOf(node)][group] = result_squared; + if (watch) System.out.println("result_squared = "+result_squared); + if (result_squared > 0.0d) { + clusterMemberships[nodeList.indexOf(node)][group] = Math.sqrt(result_squared); + if (watch) System.out.println("result = "+Math.sqrt(result_squared)); + // System.out.println("Node "+node+" is "+clusterMemberships[nodeList.indexOf(node)][group]+" away from group "+group); + } else { + clusterMemberships[nodeList.indexOf(node)][group] = Double.NaN; + } + } + } + + long algTime = System.currentTimeMillis()-startTime; + System.out.println("Algorithm took "+((double)algTime)/1000.0+" seconds"); + + monitor.showMessage(TaskMonitor.Level.INFO, "Assigning fuzzy membership"); + + // OK, at this point, we have a matrix with the squared distance from each node to the + // the centroid of each cluster. Now, we need to calculate the proportional + // membership, which we do by normalizing all of the distances so that they sum to 1.0 + for (int node_index = 0; node_index< nelements; node_index++) { + double sum = 0.0d; + // System.out.println("Node: "+nodeList.get(node_index)); + for (int cluster_index = 0; cluster_index < max_cluster; cluster_index++) { + double v = clusterMemberships[node_index][cluster_index]; + if (Double.isNaN(v)) + continue; + sum += v; + } + // System.out.println("Node "+nodeList.get(node_index)+" distances sum to "+sum); + for (int cluster_index = 0; cluster_index < max_cluster; cluster_index++) { + double v = clusterMemberships[node_index][cluster_index]; + if (Double.isNaN(v)) + continue; + CyNode node = nodeList.get(node_index); + if (sum == 0.0) + v = 1.0; + else + v = v/sum; + // if (v < 1.0) + // v = 1.0 - v; + clusterMemberships[node_index][cluster_index] = v; + // System.out.println("Node "+nodeList.get(node_index)+" is "+clusterMemberships[node_index][cluster_index]+" away from group "+cluster_index); + } + } + + /* + long assignTime = System.currentTimeMillis()-algTime; + System.out.println("Assigning clusters took "+((double)assignTime)/1000.0+" seconds"); + */ + + monitor.showMessage(TaskMonitor.Level.INFO, "Making cluster map"); + + HashMap membershipMap = createMembershipMap(clusterMemberships); List fuzzyClusters = new ArrayList(); // Adding the nodes which have memberships greater than the threshold to fuzzy node clusters List fuzzyNodeList; - for(int i = 0 ; i < number_clusters; i++){ + for(int i = 0 ; i < max_cluster; i++){ fuzzyNodeList = new ArrayList(); HashMap clusterMembershipMap = new HashMap(); - for( CyNode node: nodeList){ - if (membershipMap.get(node)[i] > membershipThreshold ){ + for( int node_index = 0; node_index < nodeList.size(); node_index++) { + CyNode node = nodeList.get(node_index); + double v = clusterMemberships[node_index][i]; + if (!Double.isNaN(v) && v > membershipThreshold) { + fuzzyNodeList.add(node); + clusterMembershipMap.put(node, v); + } else if (!Double.isNaN(v) && baseMembership.containsKey(node) && baseMembership.get(node) == i) { + // Force this to be part of this fuzzy cluster if it's part of the base cluster fuzzyNodeList.add(node); - clusterMembershipMap.put(node, membershipMap.get(node)[i]); - } + clusterMembershipMap.put(node, v); + } } - fuzzyClusters.add(new FuzzyNodeCluster(fuzzyNodeList,clusterMembershipMap)); - } - - return fuzzyClusters; + if (fuzzyNodeList.size() < context.minClusterSize) + continue; - } - - /** - * The method calculates the centers of fuzzy clusters - * - * @param cData matrix to store the data for cluster centers - */ + FuzzyNodeCluster fCluster = new FuzzyNodeCluster(fuzzyNodeList,clusterMembershipMap); + fCluster.setClusterNumber(i+1); // This will number this the same as the corresponding source cluster + fuzzyClusters.add(fCluster); + } - public void getFuzzyCenters(CyMatrix cData){ + long totalTime = System.currentTimeMillis()-startTime; + System.out.println("Total time: "+((double)totalTime)/1000.0+" seconds"); - // To store the sum of memberships(raised to fuzziness index) corresponding to each cluster - int nelements = distanceMatrix.nRows(); + return fuzzyClusters; - for (NodeCluster cluster : Clusters){ - int c = Clusters.indexOf(cluster); - double numerator = 0; - Double distance = 0.0; - int i = 0; - for (int e = 0; e < nelements; e++) { - numerator = 0; - for(CyNode node : cluster){ - i = nodeList.indexOf(node); - distance = distanceMatrix.doubleValue(i,e); - numerator += distance; - } - cData.setValue(c,e,(numerator/cluster.size())); - } - } } + private double get_sum(CyMatrix d2, NodeCluster cluster, CyNode node, boolean watch) { + int n_index = nodeList.indexOf(node); + if (watch) System.out.println("n_index = "+n_index); + double sum = 0.0d; + for (CyNode c_node: cluster) { + int c_index = nodeList.indexOf(c_node); + if (watch) System.out.println("c_index = "+c_index+" node = "+c_node); + if (watch) System.out.println("distance = "+d2.doubleValue(n_index, c_index)+" weight = "+(1/d2.doubleValue(n_index, c_index))); + sum += d2.doubleValue(n_index,c_index); + } + + return sum; + } + + private int[] get_sizes(List group_items) { + int[] sizes = new int[NodeCluster.getMaxClusterNumber()]; + for (NodeCluster c: group_items) { + sizes[c.getClusterNumber()-1] = c.size(); + } + return sizes; + } + + // For each cluster, sum up the distances + private double[] get_sums(CyMatrix d2, List group_items) { + double[] sums = new double[NodeCluster.getMaxClusterNumber()]; + List nodes = d2.getRowNodes(); + + for (NodeCluster c: group_items) { + int cluster_number = c.getClusterNumber()-1; + sums[cluster_number] = 0.0d; + + int[] node_indices = node_index(c, nodes); + if (node_indices == null) + continue; + + for (int i = 0; i < node_indices.length; i++) { + for (int j = i+1; j < node_indices.length; j++) { + sums[cluster_number] += d2.doubleValue(node_indices[i], node_indices[j]); + } + } + } + return sums; + } + + private int[] node_index(NodeCluster c, Listnodes) { + int[] indices = new int[c.size()]; + int index = 0; + /* + System.out.print("cluster number: "+c.getClusterNumber()+", nodes = ["); + for (CyNode n: nodes) {System.out.print(n+",");} + System.out.println("]"); + */ + for (CyNode n: c) { + int nodeid = nodes.indexOf(n); + if (nodeid >= 0) { + // System.out.println("n = "+n+", index(n) = "+nodes.indexOf(n)); + indices[index++] = nodes.indexOf(n); + } + } + if (index == 0) return null; + + return Arrays.copyOf(indices, index); + } + /** * Creates a Map from nodes to their respective membership arrays * @@ -192,8 +345,7 @@ public void getFuzzyCenters(CyMatrix cData){ public HashMap createMembershipMap(double[][] membershipArray){ HashMap membershipHM = new HashMap(); - List nodeList = distanceMatrix.getRowNodes(); - for ( int i = 0; i configuration = new HashMap<>(); + if (context.isSynchronous == true) { + configuration.put("waitTime", -1); + } else { + configuration.put("waitTime", 20); + } + + configuration.put("trials", context.trials); HashMap nodeMap = getNetworkNodes(currentNetwork); List nodeArray = new ArrayList<>(); @@ -95,29 +109,24 @@ public void run(TaskMonitor taskMonitor) throws Exception { jobData = dataService.addData(jobData, "edges", edgeArray); job.storeClusterData(clusterAttributeName, currentNetwork, clusterManager, createGroups, GROUP_ATTRIBUTE, null, getShortName()); // Create our handler - ClusterJobHandler jobHandler = new ClusterJobHandler(job, network); + NetworkClusterJobHandler jobHandler = new NetworkClusterJobHandler(job, network, context.vizProperties.showUI, context.vizProperties.restoreEdges); job.setJobMonitor(jobHandler); // Submit the job - CyJobStatus exStatus = executionService.executeJob(job, basePath, null, jobData); + CyJobStatus exStatus = executionService.executeJob(job, basePath, configuration, jobData); CyJobStatus.Status status = exStatus.getStatus(); System.out.println("Status: " + status); if (status == Status.FINISHED) { - executionService.fetchResults(job, dataService.getDataInstance()); - - if (context.vizProperties.showUI) { - taskMonitor.showMessage(TaskMonitor.Level.INFO, "Creating network"); - insertTasksAfterCurrentTask(new NewNetworkView(network, clusterManager, true, context.vizProperties.restoreEdges, false)); - } - + jobHandler.loadData(job, taskMonitor); } else if (status == Status.RUNNING || status == Status.SUBMITTED || status == Status.QUEUED) { CyJobManager manager = registrar.getService(CyJobManager.class); manager.addJob(job, jobHandler, 5); //this one shows the load button - } else if (status == Status.ERROR - || status == Status.UNKNOWN + } else if (status == Status.ERROR) { + monitor.showMessage(TaskMonitor.Level.ERROR, "Job error: " + exStatus.getMessage()); + } else if (status == Status.UNKNOWN || status == Status.CANCELED || status == Status.FAILED || status == Status.TERMINATED diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Infomap/InfomapContext.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Infomap/InfomapContext.java index 54625e0..2a987c7 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Infomap/InfomapContext.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Infomap/InfomapContext.java @@ -28,6 +28,7 @@ public class InfomapContext implements ClusterAlgorithmContext { @Tunable(description = "Trials", longDescription = "The number of attempts to partition the network.", exampleStringValue = "10", + tooltip = "The number of attempts to partition the network.", groups = {"Infomap Advanced Settings"}, gravity = 1.0) public int trials = 10; @@ -43,6 +44,13 @@ public ListSingleSelection getattribute(){ } public void setattribute(ListSingleSelection attr) { } + @Tunable(description = "Synchronous", + longDescription = "If ```false``` the algorithm will run in the background after specified wait time", + exampleStringValue = "true", + tooltip = "If ```false``` the algorithm will run in the background after specified wait time", + groups = {"Leiden Advanced Settings"}, gravity = 6.0) + public boolean isSynchronous = false; + @ContainsTunables public AdvancedProperties advancedAttributes; @@ -61,6 +69,7 @@ public InfomapContext(InfomapContext origin) { trials = origin.trials; attribute = origin.attribute; + isSynchronous = origin.isSynchronous; } public void setNetwork(CyNetwork network) { diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Infomap/InfomapTaskFactory.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Infomap/InfomapTaskFactory.java index 58a4cf5..24eac93 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Infomap/InfomapTaskFactory.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Infomap/InfomapTaskFactory.java @@ -30,8 +30,8 @@ public InfomapTaskFactory(ClusterManager clusterManager, CyServiceRegistrar regi @Override public String getLongDescription() { - return ""; - } + return "Find community structure that minimizes the expected description length of a random walker trajectory. Implementation of the InfoMap community detection algorithm.of Martin Rosvall and Carl T. Bergstrom."; + } @Override public ClusterViz getVisualizer() { diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/LabelPropagation/LabelPropagation.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/LabelPropagation/LabelPropagation.java index c027ad7..0874cc8 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/LabelPropagation/LabelPropagation.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/LabelPropagation/LabelPropagation.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; import org.cytoscape.application.CyApplicationManager; import org.cytoscape.jobs.CyJobData; @@ -15,19 +16,23 @@ import org.cytoscape.model.CyNetwork; import org.cytoscape.service.util.CyServiceRegistrar; import org.cytoscape.work.ContainsTunables; +import org.cytoscape.work.ProvidesTitle; import org.cytoscape.work.TaskMonitor; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.NodeCluster; import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.AbstractNetworkClusterer; import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.LabelPropagation.LabelPropagationContext; import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; import edu.ucsf.rbvi.clusterMaker2.internal.ui.NewNetworkView; import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJob; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJobExecutionService; import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJobHandler; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.NetworkClusterJobHandler; import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.RemoteServer; public class LabelPropagation extends AbstractNetworkClusterer { - public static String NAME = "Label Propagation"; + public static String NAME = "Label Propagation (remote)"; public static String SHORTNAME = "labelpropagation"; final CyServiceRegistrar registrar; public final static String GROUP_ATTRIBUTE = "__LabelPropagationGroups.SUID"; @@ -48,10 +53,12 @@ public LabelPropagation(LabelPropagationContext context, ClusterManager manager, public String getShortName() {return SHORTNAME;} @Override + @ProvidesTitle public String getName() {return NAME;} @Override public void run(TaskMonitor taskMonitor) throws Exception { + monitor = taskMonitor; // Get the execution service CyJobExecutionService executionService = registrar.getService(CyJobExecutionService.class, "(title=ClusterJobExecutor)"); @@ -61,6 +68,13 @@ public void run(TaskMonitor taskMonitor) throws Exception { clusterAttributeName = context.getClusterAttribute(); createGroups = context.advancedAttributes.createGroups; String attribute = context.getattribute().getSelectedValue(); + + HashMap configuration = new HashMap<>(); + if (context.isSynchronous == true) { + configuration.put("waitTime", -1); + } else { + configuration.put("waitTime", 20); + } HashMap nodeMap = getNetworkNodes(currentNetwork); List nodeArray = new ArrayList<>(); @@ -81,15 +95,30 @@ public void run(TaskMonitor taskMonitor) throws Exception { jobData = dataService.addData(jobData, "edges", edgeArray); job.storeClusterData(clusterAttributeName, currentNetwork, clusterManager, createGroups, GROUP_ATTRIBUTE, null, getShortName()); // Create our handler - ClusterJobHandler jobHandler = new ClusterJobHandler(job, network); + NetworkClusterJobHandler jobHandler = new NetworkClusterJobHandler(job, network, context.vizProperties.showUI, context.vizProperties.restoreEdges); job.setJobMonitor(jobHandler); // Submit the job - CyJobStatus exStatus = executionService.executeJob(job, basePath, null, jobData); + CyJobStatus exStatus = executionService.executeJob(job, basePath, configuration, jobData); CyJobStatus.Status status = exStatus.getStatus(); System.out.println("Status: " + status); if (status == Status.FINISHED) { - executionService.fetchResults(job, dataService.getDataInstance()); + CyJobData data = dataService.getDataInstance(); + executionService.fetchResults(job, data); + + Map clusterData = job.getClusterData().getAllValues(); + + String clusterAttributeName = (String) clusterData.get("clusterAttributeName"); + Boolean createGroups = (Boolean) clusterData.get("createGroups"); + String group_attr = (String) clusterData.get("group_attr"); + List params = (List) clusterData.get("params"); + + List nodeClusters = ClusterJobExecutionService.createClusters(data, clusterAttributeName, network); //move this to remote utils + System.out.println("NodeClusters: " + nodeClusters); + + AbstractNetworkClusterer.createGroups(network, nodeClusters, group_attr, clusterAttributeName, + clusterManager, createGroups, params, SHORTNAME); + if (context.vizProperties.showUI) { taskMonitor.showMessage(TaskMonitor.Level.INFO, "Creating network"); insertTasksAfterCurrentTask(new NewNetworkView(network, clusterManager, true, context.vizProperties.restoreEdges, false)); @@ -101,8 +130,9 @@ public void run(TaskMonitor taskMonitor) throws Exception { CyJobManager manager = registrar.getService(CyJobManager.class); manager.addJob(job, jobHandler, 5); //this one shows the load button - } else if (status == Status.ERROR - || status == Status.UNKNOWN + } else if (status == Status.ERROR) { + monitor.showMessage(TaskMonitor.Level.ERROR, "Job error: " + exStatus.getMessage()); + } else if (status == Status.UNKNOWN || status == Status.CANCELED || status == Status.FAILED || status == Status.TERMINATED diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/LabelPropagation/LabelPropagationContext.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/LabelPropagation/LabelPropagationContext.java index d17385c..7d58550 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/LabelPropagation/LabelPropagationContext.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/LabelPropagation/LabelPropagationContext.java @@ -29,6 +29,13 @@ public ListSingleSelection getattribute(){ } public void setattribute(ListSingleSelection attr) { } + @Tunable(description = "Synchronous", + longDescription = "If ```false``` the algorithm will run in the background after specified wait time", + exampleStringValue = "true", + tooltip = "If ```false``` the algorithm will run in the background after specified wait time", + groups = {"Label Propagation Advanced Settings"}, gravity = 6.0) + public boolean isSynchronous = false; + @ContainsTunables public AdvancedProperties advancedAttributes; @@ -46,6 +53,7 @@ public LabelPropagationContext(LabelPropagationContext origin) { advancedAttributes = new AdvancedProperties("__labelPropagationCluster", false); attribute = origin.attribute; + isSynchronous = origin.isSynchronous; } public void setNetwork(CyNetwork network) { diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/LabelPropagation/LabelPropagationTaskFactory.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/LabelPropagation/LabelPropagationTaskFactory.java index ab8b29e..8e1e0e8 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/LabelPropagation/LabelPropagationTaskFactory.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/LabelPropagation/LabelPropagationTaskFactory.java @@ -29,7 +29,15 @@ public LabelPropagationTaskFactory(ClusterManager clusterManager, CyServiceRegis @Override public String getLongDescription() { - return ""; + return "This function implements the community detection method described in: Raghavan, U.N. and Albert, R. and "+ + "Kumara, S.: Near linear time algorithm to detect community structures in large-scale networks. Phys Rev E "+ + "76, 036106. (2007). This version extends the original method by the ability to take edge weights into consideration "+ + "and also by allowing some labels to be fixed."+ + "

"+ + "Weights are taken into account as follows: when the new label of node i is determined, the algorithm iterates over "+ + "all edges incident on node i and calculate the total weight of edges leading to other nodes with label 0, 1, 2, ..., "+ + "k - 1 (where k is the number of possible labels). The new label of node i will then be the label whose edges "+ + "(among the ones incident on node i) have the highest total weight"; } @Override diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/LeadingEigenVector/LEVCluster.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/LeadingEigenVector/LEVCluster.java index e6c6035..b51aeb8 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/LeadingEigenVector/LEVCluster.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/LeadingEigenVector/LEVCluster.java @@ -1,119 +1,130 @@ package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.LeadingEigenVector; - import java.util.ArrayList; - import java.util.HashMap; - import java.util.List; - - import org.cytoscape.application.CyApplicationManager; - import org.cytoscape.jobs.CyJobData; - import org.cytoscape.jobs.CyJobDataService; - import org.cytoscape.jobs.CyJobExecutionService; - import org.cytoscape.jobs.CyJobManager; - import org.cytoscape.jobs.CyJobStatus; - import org.cytoscape.jobs.SUIDUtil; - import org.cytoscape.jobs.CyJobStatus.Status; - import org.cytoscape.model.CyNetwork; - import org.cytoscape.service.util.CyServiceRegistrar; - import org.cytoscape.work.ContainsTunables; - import org.cytoscape.work.TaskMonitor; - - import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.AbstractNetworkClusterer; - import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.LeadingEigenVector.LEVContext; - import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; - import edu.ucsf.rbvi.clusterMaker2.internal.ui.NewNetworkView; - import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJob; - import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJobHandler; - import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.RemoteServer; - - public class LEVCluster extends AbstractNetworkClusterer { - - public static String NAME = "Leading Eigenvector"; - public static String SHORTNAME = "leadingeigenvector"; - final CyServiceRegistrar registrar; - public final static String GROUP_ATTRIBUTE = "__LeadingEigenvector.SUID"; - - @ContainsTunables - public LEVContext context = null; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.cytoscape.application.CyApplicationManager; +import org.cytoscape.jobs.CyJobData; +import org.cytoscape.jobs.CyJobDataService; +import org.cytoscape.jobs.CyJobExecutionService; +import org.cytoscape.jobs.CyJobManager; +import org.cytoscape.jobs.CyJobStatus; +import org.cytoscape.jobs.SUIDUtil; +import org.cytoscape.jobs.CyJobStatus.Status; +import org.cytoscape.model.CyNetwork; +import org.cytoscape.service.util.CyServiceRegistrar; +import org.cytoscape.work.ContainsTunables; +import org.cytoscape.work.ProvidesTitle; +import org.cytoscape.work.TaskMonitor; + +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.NodeCluster; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.AbstractNetworkClusterer; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.LeadingEigenVector.LEVContext; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; +import edu.ucsf.rbvi.clusterMaker2.internal.ui.NewNetworkView; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJob; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJobExecutionService; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJobHandler; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.NetworkClusterJobHandler; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.RemoteServer; + +public class LEVCluster extends AbstractNetworkClusterer { + + public static String NAME = "Leading Eigenvector (remote)"; + public static String SHORTNAME = "leadingeigenvector"; + final CyServiceRegistrar registrar; + public final static String GROUP_ATTRIBUTE = "__LeadingEigenvector.SUID"; + + @ContainsTunables + public LEVContext context = null; + + public LEVCluster(LEVContext context, ClusterManager manager, CyServiceRegistrar registrar) { + super(manager); + this.context = context; + if (network == null) + network = clusterManager.getNetwork(); + context.setNetwork(network); + this.registrar = registrar; + } + + @Override + public String getShortName() {return SHORTNAME;} + + @Override + @ProvidesTitle + public String getName() {return NAME;} + + @Override + public void run(TaskMonitor taskMonitor) throws Exception { + monitor = taskMonitor; + // Get the execution service + CyJobExecutionService executionService = + registrar.getService(CyJobExecutionService.class, "(title=ClusterJobExecutor)"); + CyApplicationManager appManager = registrar.getService(CyApplicationManager.class); + CyNetwork currentNetwork = appManager.getCurrentNetwork(); //gets the network presented in Cytoscape + + clusterAttributeName = context.getClusterAttribute(); + createGroups = context.advancedAttributes.createGroups; + String attribute = context.getattribute().getSelectedValue(); - public LEVCluster(LEVContext context, ClusterManager manager, CyServiceRegistrar registrar) { - super(manager); - this.context = context; - if (network == null) - network = clusterManager.getNetwork(); - context.setNetwork(network); - this.registrar = registrar; + HashMap configuration = new HashMap<>(); + if (context.isSynchronous == true) { + configuration.put("waitTime", -1); + } else { + configuration.put("waitTime", 20); + } + + HashMap nodeMap = getNetworkNodes(currentNetwork); + List nodeArray = new ArrayList<>(); + for (Long nodeSUID : nodeMap.keySet()) { + nodeArray.add(nodeMap.get(nodeSUID)); } - @Override - public String getShortName() {return SHORTNAME;} + List edgeArray = getNetworkEdges(currentNetwork, nodeMap, attribute); - @Override - public String getName() {return NAME;} + String basePath = RemoteServer.getBasePath(); - @Override - public void run(TaskMonitor taskMonitor) throws Exception { - // Get the execution service - CyJobExecutionService executionService = - registrar.getService(CyJobExecutionService.class, "(title=ClusterJobExecutor)"); - CyApplicationManager appManager = registrar.getService(CyApplicationManager.class); - CyNetwork currentNetwork = appManager.getCurrentNetwork(); //gets the network presented in Cytoscape + // Get our initial job + ClusterJob job = (ClusterJob) executionService.createCyJob("ClusterJob"); //creates a new ClusterJob object + // Get the data service + CyJobDataService dataService = job.getJobDataService(); //gets the dataService of the execution service + // Add our data + CyJobData jobData = dataService.addData(null, "nodes", nodeArray); + jobData = dataService.addData(jobData, "edges", edgeArray); + job.storeClusterData(clusterAttributeName, currentNetwork, clusterManager, createGroups, GROUP_ATTRIBUTE, null, getShortName()); + // Create our handler + NetworkClusterJobHandler jobHandler = new NetworkClusterJobHandler(job, network, context.vizProperties.showUI, context.vizProperties.restoreEdges); + job.setJobMonitor(jobHandler); + // Submit the job + CyJobStatus exStatus = executionService.executeJob(job, basePath, configuration, jobData); + + CyJobStatus.Status status = exStatus.getStatus(); + System.out.println("Status: " + status); - clusterAttributeName = context.getClusterAttribute(); - createGroups = context.advancedAttributes.createGroups; - String attribute = context.getattribute().getSelectedValue(); - - HashMap nodeMap = getNetworkNodes(currentNetwork); - List nodeArray = new ArrayList<>(); - for (Long nodeSUID : nodeMap.keySet()) { - nodeArray.add(nodeMap.get(nodeSUID)); - } - - List edgeArray = getNetworkEdges(currentNetwork, nodeMap, attribute); - - String basePath = RemoteServer.getBasePath(); - - // Get our initial job - ClusterJob job = (ClusterJob) executionService.createCyJob("ClusterJob"); //creates a new ClusterJob object - // Get the data service - CyJobDataService dataService = job.getJobDataService(); //gets the dataService of the execution service - // Add our data - CyJobData jobData = dataService.addData(null, "nodes", nodeArray); - jobData = dataService.addData(jobData, "edges", edgeArray); - job.storeClusterData(clusterAttributeName, currentNetwork, clusterManager, createGroups, GROUP_ATTRIBUTE, null, getShortName()); - // Create our handler - ClusterJobHandler jobHandler = new ClusterJobHandler(job, network); - job.setJobMonitor(jobHandler); - // Submit the job - CyJobStatus exStatus = executionService.executeJob(job, basePath, null, jobData); + if (status == Status.FINISHED) { + jobHandler.loadData(job, taskMonitor); + } else if (status == Status.RUNNING + || status == Status.SUBMITTED + || status == Status.QUEUED) { + CyJobManager manager = registrar.getService(CyJobManager.class); + manager.addJob(job, jobHandler, 5); //this one shows the load button - CyJobStatus.Status status = exStatus.getStatus(); - System.out.println("Status: " + status); - if (status == Status.FINISHED) { - executionService.fetchResults(job, dataService.getDataInstance()); - if (context.vizProperties.showUI) { - taskMonitor.showMessage(TaskMonitor.Level.INFO, "Creating network"); - insertTasksAfterCurrentTask(new NewNetworkView(network, clusterManager, true, context.vizProperties.restoreEdges, false)); - } - - } else if (status == Status.RUNNING - || status == Status.SUBMITTED - || status == Status.QUEUED) { - CyJobManager manager = registrar.getService(CyJobManager.class); - manager.addJob(job, jobHandler, 5); //this one shows the load button - - } else if (status == Status.ERROR - || status == Status.UNKNOWN - || status == Status.CANCELED - || status == Status.FAILED - || status == Status.TERMINATED - || status == Status.PURGED) { - monitor.setStatusMessage("Job status: " + status); - } + } else if (status == Status.ERROR) { + monitor.showMessage(TaskMonitor.Level.ERROR, "Job error: " + exStatus.getMessage()); + } else if (status == Status.UNKNOWN + || status == Status.CANCELED + || status == Status.FAILED + || status == Status.TERMINATED + || status == Status.PURGED) { + monitor.setStatusMessage("Job status: " + status); + } - // Save our SUIDs in case we get saved and restored - SUIDUtil.saveSUIDs(job, currentNetwork, currentNetwork.getNodeList()); + // Save our SUIDs in case we get saved and restored + SUIDUtil.saveSUIDs(job, currentNetwork, currentNetwork.getNodeList()); - } + } - } +} diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/LeadingEigenVector/LEVClusterTaskFactory.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/LeadingEigenVector/LEVClusterTaskFactory.java index f322b96..f55f403 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/LeadingEigenVector/LEVClusterTaskFactory.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/LeadingEigenVector/LEVClusterTaskFactory.java @@ -28,7 +28,10 @@ public LEVClusterTaskFactory(ClusterManager clusterManager, CyServiceRegistrar r @Override public String getLongDescription() { - return ""; + return "Newman's leading eigenvector method for detecting community structure. This is the proper "+ + "implementation of the recursive, divisive algorithm: each split is done by maximizing the "+ + "modularity regarding the original network, see MEJ Newman: Finding community structure in networks "+ + "using the eigenvectors of matrices, Phys Rev E 74:036104 (2006). "; } @Override diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/LeadingEigenVector/LEVContext.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/LeadingEigenVector/LEVContext.java index e146088..19657c7 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/LeadingEigenVector/LEVContext.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/LeadingEigenVector/LEVContext.java @@ -29,6 +29,13 @@ public ListSingleSelection getattribute(){ } public void setattribute(ListSingleSelection attr) { } + @Tunable(description = "Synchronous", + longDescription = "If ```false``` the algorithm will run in the background after specified wait time", + exampleStringValue = "true", + tooltip = "If ```false``` the algorithm will run in the background after specified wait time", + groups = {"Leading Eigenvector Advanced Settings"}, gravity = 6.0) + public boolean isSynchronous = false; + @ContainsTunables public AdvancedProperties advancedAttributes; @@ -46,6 +53,7 @@ public LEVContext(LEVContext origin) { advancedAttributes = new AdvancedProperties("LeadingEigenvectorCluster", false); attribute = origin.attribute; + isSynchronous = origin.isSynchronous; } public void setNetwork(CyNetwork network) { diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Leiden/LeidenCluster.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Leiden/LeidenCluster.java index 7ee328c..565b8cc 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Leiden/LeidenCluster.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Leiden/LeidenCluster.java @@ -14,6 +14,7 @@ import org.cytoscape.model.CyTable; import org.cytoscape.service.util.CyServiceRegistrar; import org.cytoscape.work.ContainsTunables; +import org.cytoscape.work.ProvidesTitle; import org.cytoscape.work.TaskMonitor; import org.json.simple.JSONArray; import org.cytoscape.jobs.CyJob; @@ -38,9 +39,10 @@ import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJobExecutionService; import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.RemoteServer; import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJobHandler; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.NetworkClusterJobHandler; public class LeidenCluster extends AbstractNetworkClusterer { - public static String NAME = "Leiden Clusterer"; + public static String NAME = "Leiden Clusterer (remote)"; public static String SHORTNAME = "leiden"; final CyServiceRegistrar registrar; public final static String GROUP_ATTRIBUTE = "__LeidenGroups.SUID"; @@ -62,10 +64,12 @@ public LeidenCluster(LeidenContext context, ClusterManager manager, CyServiceReg public String getShortName() {return SHORTNAME;} @Override + @ProvidesTitle public String getName() {return NAME;} @Override public void run(TaskMonitor taskMonitor) throws Exception { + monitor = taskMonitor; // Get the execution service CyJobExecutionService executionService = registrar.getService(CyJobExecutionService.class, "(title=ClusterJobExecutor)"); @@ -75,6 +79,19 @@ public void run(TaskMonitor taskMonitor) throws Exception { clusterAttributeName = context.getClusterAttribute(); createGroups = context.advancedAttributes.createGroups; String attribute = context.getattribute().getSelectedValue(); + + HashMap configuration = new HashMap<>(); + if (context.isSynchronous == true) { + configuration.put("waitTime", -1); + } else { + configuration.put("waitTime", 20); + } + + // Get the arguments from our context + configuration.put("resolution", context.resolution_parameter); + configuration.put("beta", context.beta); + configuration.put("iterations", context.n_iterations); + configuration.put("objective_function", context.objective_function.getSelectedValue()); HashMap nodeMap = getNetworkNodes(currentNetwork); List nodeArray = new ArrayList<>(); @@ -95,28 +112,25 @@ public void run(TaskMonitor taskMonitor) throws Exception { jobData = dataService.addData(jobData, "edges", edgeArray); job.storeClusterData(clusterAttributeName, currentNetwork, clusterManager, createGroups, GROUP_ATTRIBUTE, null, getShortName()); // Create our handler - ClusterJobHandler jobHandler = new ClusterJobHandler(job, network); + NetworkClusterJobHandler jobHandler = new NetworkClusterJobHandler(job, network, context.vizProperties.showUI, context.vizProperties.restoreEdges); job.setJobMonitor(jobHandler); // Submit the job - CyJobStatus exStatus = executionService.executeJob(job, basePath, null, jobData); + CyJobStatus exStatus = executionService.executeJob(job, basePath, configuration, jobData); CyJobStatus.Status status = exStatus.getStatus(); System.out.println("Status: " + status); + if (status == Status.FINISHED) { - executionService.fetchResults(job, dataService.getDataInstance()); - if (context.vizProperties.showUI) { - taskMonitor.showMessage(TaskMonitor.Level.INFO, "Creating network"); - insertTasksAfterCurrentTask(new NewNetworkView(network, clusterManager, true, context.vizProperties.restoreEdges, false)); - } - + jobHandler.loadData(job, taskMonitor); } else if (status == Status.RUNNING || status == Status.SUBMITTED || status == Status.QUEUED) { CyJobManager manager = registrar.getService(CyJobManager.class); manager.addJob(job, jobHandler, 5); //this one shows the load button - } else if (status == Status.ERROR - || status == Status.UNKNOWN + } else if (status == Status.ERROR) { + monitor.showMessage(TaskMonitor.Level.ERROR, "Job error: " + exStatus.getMessage()); + } else if (status == Status.UNKNOWN || status == Status.CANCELED || status == Status.FAILED || status == Status.TERMINATED diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Leiden/LeidenClusterTaskFactory.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Leiden/LeidenClusterTaskFactory.java index 428c3d7..efa31ba 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Leiden/LeidenClusterTaskFactory.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Leiden/LeidenClusterTaskFactory.java @@ -30,7 +30,37 @@ public LeidenClusterTaskFactory(ClusterManager clusterManager, CyServiceRegistra @Override public String getLongDescription() { - return ""; + return "This function implements the Leiden "+ + "algorithm for finding community structure, "+ + "see Traag, V. A., Waltman, L., & van Eck, "+ + "N. J. (2019). From Louvain to Leiden: guaranteeing "+ + "well-connected communities. Scientific reports, 9(1), "+ + "5233. http://dx.doi.org/10.1038/s41598-019-41695-z "+ + "

"+ + "It is similar to the multilevel algorithm, often called the "+ + "Louvain algorithm, but it is faster and yields higher quality "+ + "solutions. It can optimize both modularity and the Constant Potts "+ + "Model, which does not suffer from the resolution-limit (see preprint "+ + "http://arxiv.org/abs/1104.3083). "+ + "

"+ + "The Leiden algorithm consists of three phases: (1) local moving of nodes, "+ + "(2) refinement of the partition and (3) aggregation of the network based "+ + "on the refined partition, using the non-refined partition to create "+ + "an initial partition for the aggregate network. In the local move "+ + "procedure in the Leiden algorithm, only nodes whose neighborhood has "+ + "changed are visited. The refinement is done by restarting from a singleton "+ + "partition within each cluster and gradually merging the subclusters. When "+ + "aggregating, a single cluster may then be represented by several nodes "+ + "(which are the subclusters identified in the refinement). "+ + "

"+ + "The Leiden algorithm provides several guarantees. The Leiden algorithm "+ + "is typically iterated: the output of one iteration is used as the input "+ + "for the next iteration. At each iteration all clusters are guaranteed to "+ + "be connected and well-separated. After an iteration in which nothing has "+ + "changed, all nodes and some parts are guaranteed to be locally optimally "+ + "assigned. Finally, asymptotically, all subsets of all clusters are "+ + "guaranteed to be locally optimally assigned. For more details, please "+ + "see Traag, Waltman & van Eck (2019). "; } @Override diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Leiden/LeidenContext.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Leiden/LeidenContext.java index 5af27fa..f68e622 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Leiden/LeidenContext.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Leiden/LeidenContext.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.List; import org.cytoscape.model.CyColumn; @@ -28,6 +29,7 @@ public class LeidenContext implements ClusterAlgorithmContext { @Tunable(description = "Objective function", longDescription = "Whether to use the Constant Potts Model (CPM) or modularity. Must be either \"CPM\" or \"modularity\".", exampleStringValue = "CPM", + tooltip = "Whether to use the Constant Potts Model (CPM) or modularity. Must be either 'CPM' or 'modularity'.", groups = {"Leiden Advanced Settings"}, gravity = 1.0) public ListSingleSelection objective_function = new ListSingleSelection<>("CPM", "modularity"); @@ -48,21 +50,32 @@ public void setattribute(ListSingleSelection attr) { } + "Higher resolutions lead to more smaller communities, " + "while lower resolutions lead to fewer larger communities.", exampleStringValue = "1.0", + tooltip = "The resolution parameter to use. Higher resolutions lead to more smaller communities,
" + + "while lower resolutions lead to fewer larger communities.", groups = {"Leiden Advanced Settings"}, gravity = 3.0) public double resolution_parameter = 1.0; @Tunable(description = "Beta value", longDescription = "Parameter affecting the randomness in the Leiden algorithm. This affects only the refinement step of the algorithm.", exampleStringValue = "0.01", + tooltip = "Parameter affecting the randomness in the Leiden algorithm. This affects only the refinement step of the algorithm.", groups = {"Leiden Advanced Settings"}, gravity = 4.0) public double beta = 0.01; @Tunable(description = "Number of iterations", longDescription = "The number of iterations to iterate the Leiden algorithm. Each iteration may improve the partition further.", exampleStringValue = "2", + tooltip = "The number of iterations to iterate the Leiden algorithm. Each iteration may improve the partition further.", groups = {"Leiden Advanced Settings"}, gravity = 5.0) public int n_iterations = 2; + @Tunable(description = "Synchronous", + longDescription = "If ```false``` the algorithm will run in the background after specified wait time", + exampleStringValue = "true", + tooltip = "If ```false``` the algorithm will run in the background after specified wait time", + groups = {"Leiden Advanced Settings"}, gravity = 6.0) + public boolean isSynchronous = false; + @ContainsTunables public AdvancedProperties advancedAttributes; @@ -84,6 +97,8 @@ public LeidenContext(LeidenContext origin) { resolution_parameter = origin.resolution_parameter; beta = origin.beta; n_iterations = origin.n_iterations; + isSynchronous = origin.isSynchronous; + } public void setNetwork(CyNetwork network) { diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Multilevel/MultilevelCluster.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Multilevel/MultilevelCluster.java index f738676..04db9ba 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Multilevel/MultilevelCluster.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Multilevel/MultilevelCluster.java @@ -1,118 +1,129 @@ package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.Multilevel; - import java.util.ArrayList; - import java.util.HashMap; - import java.util.List; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; - import org.cytoscape.application.CyApplicationManager; - import org.cytoscape.jobs.CyJobData; - import org.cytoscape.jobs.CyJobDataService; - import org.cytoscape.jobs.CyJobExecutionService; - import org.cytoscape.jobs.CyJobManager; - import org.cytoscape.jobs.CyJobStatus; - import org.cytoscape.jobs.SUIDUtil; - import org.cytoscape.jobs.CyJobStatus.Status; - import org.cytoscape.model.CyNetwork; - import org.cytoscape.service.util.CyServiceRegistrar; - import org.cytoscape.work.ContainsTunables; - import org.cytoscape.work.TaskMonitor; +import org.cytoscape.application.CyApplicationManager; +import org.cytoscape.jobs.CyJobData; +import org.cytoscape.jobs.CyJobDataService; +import org.cytoscape.jobs.CyJobExecutionService; +import org.cytoscape.jobs.CyJobManager; +import org.cytoscape.jobs.CyJobStatus; +import org.cytoscape.jobs.SUIDUtil; +import org.cytoscape.jobs.CyJobStatus.Status; +import org.cytoscape.model.CyNetwork; +import org.cytoscape.service.util.CyServiceRegistrar; +import org.cytoscape.work.ContainsTunables; +import org.cytoscape.work.ProvidesTitle; +import org.cytoscape.work.TaskMonitor; - import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.AbstractNetworkClusterer; - import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.Multilevel.MultilevelContext; - import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; - import edu.ucsf.rbvi.clusterMaker2.internal.ui.NewNetworkView; - import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJob; - import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJobHandler; - import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.RemoteServer; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.NodeCluster; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.AbstractNetworkClusterer; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.Multilevel.MultilevelContext; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; +import edu.ucsf.rbvi.clusterMaker2.internal.ui.NewNetworkView; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJob; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJobExecutionService; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.ClusterJobHandler; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.NetworkClusterJobHandler; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils.RemoteServer; - public class MultilevelCluster extends AbstractNetworkClusterer { +public class MultilevelCluster extends AbstractNetworkClusterer { - public static String NAME = "Multilevel Cluster"; - public static String SHORTNAME = "multilevel"; - final CyServiceRegistrar registrar; - public final static String GROUP_ATTRIBUTE = "__Multilevel.SUID"; - - @ContainsTunables - public MultilevelContext context = null; - - public MultilevelCluster(MultilevelContext context, ClusterManager manager, CyServiceRegistrar registrar) { - super(manager); - this.context = context; - if (network == null) - network = clusterManager.getNetwork(); - context.setNetwork(network); - this.registrar = registrar; - } + public static String NAME = "Multilevel Cluster (remote)"; + public static String SHORTNAME = "multilevel"; + final CyServiceRegistrar registrar; + public final static String GROUP_ATTRIBUTE = "__Multilevel.SUID"; + + @ContainsTunables + public MultilevelContext context = null; + + public MultilevelCluster(MultilevelContext context, ClusterManager manager, CyServiceRegistrar registrar) { + super(manager); + this.context = context; + if (network == null) + network = clusterManager.getNetwork(); + context.setNetwork(network); + this.registrar = registrar; + } - @Override - public String getShortName() {return SHORTNAME;} + @Override + public String getShortName() {return SHORTNAME;} - @Override - public String getName() {return NAME;} + @Override + @ProvidesTitle + public String getName() {return NAME;} - @Override - public void run(TaskMonitor taskMonitor) throws Exception { - // Get the execution service - CyJobExecutionService executionService = - registrar.getService(CyJobExecutionService.class, "(title=ClusterJobExecutor)"); - CyApplicationManager appManager = registrar.getService(CyApplicationManager.class); - CyNetwork currentNetwork = appManager.getCurrentNetwork(); //gets the network presented in Cytoscape - - clusterAttributeName = context.getClusterAttribute(); - createGroups = context.advancedAttributes.createGroups; - String attribute = context.getattribute().getSelectedValue(); - - HashMap nodeMap = getNetworkNodes(currentNetwork); - List nodeArray = new ArrayList<>(); - for (Long nodeSUID : nodeMap.keySet()) { - nodeArray.add(nodeMap.get(nodeSUID)); - } - - List edgeArray = getNetworkEdges(currentNetwork, nodeMap, attribute); - - String basePath = RemoteServer.getBasePath(); - - // Get our initial job - ClusterJob job = (ClusterJob) executionService.createCyJob("ClusterJob"); //creates a new ClusterJob object - // Get the data service - CyJobDataService dataService = job.getJobDataService(); //gets the dataService of the execution service - // Add our data - CyJobData jobData = dataService.addData(null, "nodes", nodeArray); - jobData = dataService.addData(jobData, "edges", edgeArray); - job.storeClusterData(clusterAttributeName, currentNetwork, clusterManager, createGroups, GROUP_ATTRIBUTE, null, getShortName()); - // Create our handler - ClusterJobHandler jobHandler = new ClusterJobHandler(job, network); - job.setJobMonitor(jobHandler); - // Submit the job - CyJobStatus exStatus = executionService.executeJob(job, basePath, null, jobData); - - CyJobStatus.Status status = exStatus.getStatus(); - System.out.println("Status: " + status); - if (status == Status.FINISHED) { - executionService.fetchResults(job, dataService.getDataInstance()); - if (context.vizProperties.showUI) { - taskMonitor.showMessage(TaskMonitor.Level.INFO, "Creating network"); - insertTasksAfterCurrentTask(new NewNetworkView(network, clusterManager, true, context.vizProperties.restoreEdges, false)); - } + @Override + public void run(TaskMonitor taskMonitor) throws Exception { + monitor = taskMonitor; + // Get the execution service + CyJobExecutionService executionService = + registrar.getService(CyJobExecutionService.class, "(title=ClusterJobExecutor)"); + CyApplicationManager appManager = registrar.getService(CyApplicationManager.class); + CyNetwork currentNetwork = appManager.getCurrentNetwork(); //gets the network presented in Cytoscape + + clusterAttributeName = context.getClusterAttribute(); + createGroups = context.advancedAttributes.createGroups; + String attribute = context.getattribute().getSelectedValue(); + + HashMap configuration = new HashMap<>(); + if (context.isSynchronous == true) { + configuration.put("waitTime", -1); + } else { + configuration.put("waitTime", 20); + } - } else if (status == Status.RUNNING - || status == Status.SUBMITTED - || status == Status.QUEUED) { - CyJobManager manager = registrar.getService(CyJobManager.class); - manager.addJob(job, jobHandler, 5); //this one shows the load button + HashMap nodeMap = getNetworkNodes(currentNetwork); + List nodeArray = new ArrayList<>(); + for (Long nodeSUID : nodeMap.keySet()) { + nodeArray.add(nodeMap.get(nodeSUID)); + } + + List edgeArray = getNetworkEdges(currentNetwork, nodeMap, attribute); - } else if (status == Status.ERROR - || status == Status.UNKNOWN - || status == Status.CANCELED - || status == Status.FAILED - || status == Status.TERMINATED - || status == Status.PURGED) { - monitor.setStatusMessage("Job status: " + status); - } + String basePath = RemoteServer.getBasePath(); + + // Get our initial job + ClusterJob job = (ClusterJob) executionService.createCyJob("ClusterJob"); //creates a new ClusterJob object + // Get the data service + CyJobDataService dataService = job.getJobDataService(); //gets the dataService of the execution service + // Add our data + CyJobData jobData = dataService.addData(null, "nodes", nodeArray); + jobData = dataService.addData(jobData, "edges", edgeArray); + job.storeClusterData(clusterAttributeName, currentNetwork, clusterManager, createGroups, GROUP_ATTRIBUTE, null, getShortName()); + // Create our handler + NetworkClusterJobHandler jobHandler = new NetworkClusterJobHandler(job, network, context.vizProperties.showUI, context.vizProperties.restoreEdges); + job.setJobMonitor(jobHandler); + // Submit the job + CyJobStatus exStatus = executionService.executeJob(job, basePath, configuration, jobData); + + CyJobStatus.Status status = exStatus.getStatus(); + System.out.println("Status: " + status); + + if (status == Status.FINISHED) { + jobHandler.loadData(job, taskMonitor); + } else if (status == Status.RUNNING + || status == Status.SUBMITTED + || status == Status.QUEUED) { + CyJobManager manager = registrar.getService(CyJobManager.class); + manager.addJob(job, jobHandler, 5); //this one shows the load button - // Save our SUIDs in case we get saved and restored - SUIDUtil.saveSUIDs(job, currentNetwork, currentNetwork.getNodeList()); + } else if (status == Status.ERROR) { + monitor.showMessage(TaskMonitor.Level.ERROR, "Job error: " + exStatus.getMessage()); + } else if (status == Status.UNKNOWN + || status == Status.CANCELED + || status == Status.FAILED + || status == Status.TERMINATED + || status == Status.PURGED) { + monitor.setStatusMessage("Job status: " + status); + } + + // Save our SUIDs in case we get saved and restored + SUIDUtil.saveSUIDs(job, currentNetwork, currentNetwork.getNodeList()); - } + } - } +} diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Multilevel/MultilevelClusterTaskFactory.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Multilevel/MultilevelClusterTaskFactory.java index 4967fdc..feeb4b1 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Multilevel/MultilevelClusterTaskFactory.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Multilevel/MultilevelClusterTaskFactory.java @@ -28,7 +28,35 @@ public MultilevelClusterTaskFactory(ClusterManager clusterManager, CyServiceRegi @Override public String getLongDescription() { - return ""; + return "This function implements the multi-level "+ + "modularity optimization algorithm for finding "+ + "community structure, see Blondel, V. D., "+ + "Guillaume, J.-L., Lambiotte, R., & Lefebvre, "+ + "E. (2008). Fast unfolding of communities "+ + "in large networks. Journal of Statistical "+ + "Mechanics: Theory and Experiment, 10008(10), 6. "+ + "https://doi.org/10.1088/1742-5468/2008/10/P10008 "+ + "for the details (preprint: "+ + "http://arxiv.org/abs/0803.0476). The algorithm "+ + "is sometimes known as the 'Louvain' algorithm. "+ + "

"+ + "The algorithm is based on the modularity measure and a hierarchical "+ + "approach. Initially, each vertex is assigned to a community on its own. In "+ + "every step, vertices are re-assigned to communities in a local, greedy "+ + "way: in a random order, each vertex is moved to the community with which "+ + "it achieves the highest contribution to modularity. When no vertices "+ + "can be reassigned, each community is considered a vertex on its own, "+ + "and the process starts again with the merged communities. The process "+ + "stops when there is only a single vertex left or when the modularity "+ + "cannot be increased any more in a step. "+ + "

"+ + "The resolution parameter gamma allows finding communities at different "+ + "resolutions. Higher values of the resolution parameter typically result in "+ + "more, smaller communities. Lower values typically result in fewer, larger "+ + "communities. The original definition of modularity is retrieved when "+ + "setting gamma=1. Note that the returned modularity value is calculated "+ + "using the indicated resolution parameter. See igraph_modularity() for "+ + "more details. This function was contributed by Tom Gregorovic. "; } @Override diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Multilevel/MultilevelContext.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Multilevel/MultilevelContext.java index 1053a5e..d4cd699 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Multilevel/MultilevelContext.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/Multilevel/MultilevelContext.java @@ -29,6 +29,13 @@ public ListSingleSelection getattribute(){ } public void setattribute(ListSingleSelection attr) { } + @Tunable(description = "Synchronous", + longDescription = "If ```false``` the algorithm will run in the background after specified wait time", + exampleStringValue = "true", + tooltip = "If ```false``` the algorithm will run in the background after specified wait time", + groups = {"Leiden Advanced Settings"}, gravity = 6.0) + public boolean isSynchronous = false; + @ContainsTunables public AdvancedProperties advancedAttributes; @@ -46,6 +53,7 @@ public MultilevelContext(MultilevelContext origin) { advancedAttributes = new AdvancedProperties("MultilevelCluster", false); attribute = origin.attribute; + isSynchronous = origin.isSynchronous; } public void setNetwork(CyNetwork network) { diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/TransClust/RunTransClust.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/TransClust/RunTransClust.java index b96aa5f..c19305a 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/TransClust/RunTransClust.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/TransClust/RunTransClust.java @@ -58,7 +58,7 @@ public List run(TaskMonitor monitor, CyNetwork network) count = 0; for (int i = 0; i < this.nodes.size(); i++) { CyNode cyNodeI = this.nodes.get(i); - es.startPositions[integers2proteins.get(cyNodeI.getSUID())] = count; + es.startPositions[integers2proteins.get(ModelUtils.getNodeName(network, cyNodeI))] = count; for (int j = 0; j < this.nodes.size(); j++) { CyNode cyNodeJ = this.nodes.get(j); es.sources[count] = i; @@ -69,7 +69,7 @@ public List run(TaskMonitor monitor, CyNetwork network) count++; } } - es.endPositions[integers2proteins.get(cyNodeI.getSUID())] = count-1; + es.endPositions[integers2proteins.get(ModelUtils.getNodeName(network, cyNodeI))] = count-1; } Semaphore s = new Semaphore(1); diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/TransClust/TransClustCluster.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/TransClust/TransClustCluster.java index 98e81cd..a937d9d 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/TransClust/TransClustCluster.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/TransClust/TransClustCluster.java @@ -144,6 +144,7 @@ public void run(TaskMonitor monitor) { monitor.showMessage(TaskMonitor.Level.INFO,"Clustering..."); createGroups = context.advancedAttributes.createGroups; + clusterAttributeName = context.getClusterAttribute(); //Cluster the nodes diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/TransClust/TransClusterContext.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/TransClust/TransClusterContext.java index 019d4ee..5a97917 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/TransClust/TransClusterContext.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/networkClusterers/TransClust/TransClusterContext.java @@ -32,7 +32,7 @@ public class TransClusterContext implements ClusterAlgorithmContext { public int mergeThreshold = 100; @Tunable(description= "Number of Processors:",groups={"Advanced Tuning Parameters","Parallelization"}, gravity=15.0) - public int processors = -1; + public int processors = 1; @ContainsTunables public AdvancedProperties advancedAttributes; @@ -42,6 +42,7 @@ public class TransClusterContext implements ClusterAlgorithmContext { public TransClusterContext() { advancedAttributes = new AdvancedProperties("__transclustCluster", false); + processors = Runtime.getRuntime().availableProcessors(); } public TransClusterContext(TransClusterContext origin) { diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/HITS/HITSContext.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/HITS/HITSContext.java index 40e7c20..6d4eb63 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/HITS/HITSContext.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/HITS/HITSContext.java @@ -1,7 +1,9 @@ package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.ranking.HITS; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.ranking.units.NormalizationContext; import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; import org.cytoscape.model.CyNetwork; +import org.cytoscape.work.ContainsTunables; import org.cytoscape.work.Tunable; diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/HITS/HyperlinkInducedTopicSearch.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/HITS/HyperlinkInducedTopicSearch.java index 0f8010e..8fcd69b 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/HITS/HyperlinkInducedTopicSearch.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/HITS/HyperlinkInducedTopicSearch.java @@ -12,17 +12,20 @@ import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; import edu.ucsf.rbvi.clusterMaker2.internal.api.Rank; import edu.ucsf.rbvi.clusterMaker2.internal.utils.ClusterUtils; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.ModelUtils; import org.cytoscape.model.CyEdge; import org.cytoscape.model.CyNetwork; import org.cytoscape.model.CyNode; import org.cytoscape.work.AbstractTask; import org.cytoscape.work.ContainsTunables; import org.cytoscape.work.ObservableTask; +import org.cytoscape.work.ProvidesTitle; import org.cytoscape.work.TaskMonitor; import org.cytoscape.work.Tunable; import java.util.HashMap; import java.util.List; +import java.util.Map; public class HyperlinkInducedTopicSearch extends AbstractTask implements Rank, ObservableTask { private ClusterManager manager; @@ -58,6 +61,7 @@ public String getShortName() { } @Override + @ProvidesTitle public String getName() { return NAME; } diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/MAA/MAAContext.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/MAA/MAAContext.java index 2f3c08f..9cd9346 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/MAA/MAAContext.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/MAA/MAAContext.java @@ -1,9 +1,11 @@ package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.ranking.MAA; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.ranking.units.NormalizationContext; import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; import edu.ucsf.rbvi.clusterMaker2.internal.utils.ModelUtils; import org.cytoscape.model.CyNetwork; import org.cytoscape.work.Tunable; +import org.cytoscape.work.ContainsTunables; import org.cytoscape.work.util.ListMultipleSelection; import java.util.List; @@ -19,9 +21,13 @@ public class MAAContext { @Tunable(description = "Edge attributes", groups = "Biomarker information", gravity = 10.0) public ListMultipleSelection edgeAttributes; + @ContainsTunables + public NormalizationContext normalizationContext; + public MAAContext(ClusterManager manager) { this.manager = manager; network = this.manager.getNetwork(); + normalizationContext = new NormalizationContext(manager, network); updateContext(); } @@ -72,6 +78,7 @@ private String getClusterColumnName() { public void setNetwork(CyNetwork network) { this.network = network; + normalizationContext.setNetwork(network); } public CyNetwork getNetwork() { diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/MAA/MultipleAttributeAddition.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/MAA/MultipleAttributeAddition.java index d18071c..9396ee0 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/MAA/MultipleAttributeAddition.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/MAA/MultipleAttributeAddition.java @@ -5,14 +5,17 @@ import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; import edu.ucsf.rbvi.clusterMaker2.internal.api.Rank; import edu.ucsf.rbvi.clusterMaker2.internal.utils.ClusterUtils; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.ModelUtils; import org.cytoscape.model.*; import org.cytoscape.work.AbstractTask; import org.cytoscape.work.ContainsTunables; import org.cytoscape.work.ObservableTask; +import org.cytoscape.work.ProvidesTitle; import org.cytoscape.work.TaskMonitor; import org.cytoscape.work.Tunable; import java.util.List; +import java.util.Map; public class MultipleAttributeAddition extends AbstractTask implements Rank, ObservableTask { private ClusterManager manager; @@ -44,6 +47,7 @@ public String getShortName() { } @Override + @ProvidesTitle public String getName() { return NAME; } @@ -68,10 +72,22 @@ public void run(TaskMonitor taskMonitor) { taskMonitor.setProgress(0.6); taskMonitor.showMessage(TaskMonitor.Level.INFO, "Setting node scores in clusters"); - clusters = ClusterUtils.setNodeScoresInCluster(network, clusters, nodeAttributes, clusterColumnName, false); + + // Begin by doing basic normalization -- in particular handling possible negative values + Map nodeMap = null; + if (nodeAttributes.size() > 0 && !nodeAttributes.get(0).equals(ModelUtils.NONEATTRIBUTE)) { + nodeMap = context.normalizationContext.normalize(nodeAttributes, network.getNodeList()); + clusters = ClusterUtils.setNodeScoresInCluster(network, clusters, nodeMap, clusterColumnName, false); + } + taskMonitor.setProgress(0.75); taskMonitor.showMessage(TaskMonitor.Level.INFO, "Setting edge scores in clusters"); - clusters = ClusterUtils.setEdgeScoresInCluster(network, clusters, edgeAttributes, clusterColumnName, false); + + Map edgeMap = null; + if (edgeAttributes.size() > 0 && !edgeAttributes.get(0).equals(ModelUtils.NONEATTRIBUTE)) { + edgeMap = context.normalizationContext.normalize(edgeAttributes, network.getEdgeList()); + clusters = ClusterUtils.setEdgeScoresInCluster(network, clusters, edgeMap, clusterColumnName, false); + } taskMonitor.setProgress(0.80); taskMonitor.showMessage(TaskMonitor.Level.INFO, "Sorting and ranking clusters"); ClusterUtils.ascendingSort(clusters); diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/MAM/MAMContext.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/MAM/MAMContext.java index a054bc4..6ed2dc8 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/MAM/MAMContext.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/MAM/MAMContext.java @@ -1,8 +1,10 @@ package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.ranking.MAM; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.ranking.units.NormalizationContext; import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; import edu.ucsf.rbvi.clusterMaker2.internal.utils.ModelUtils; import org.cytoscape.model.CyNetwork; +import org.cytoscape.work.ContainsTunables; import org.cytoscape.work.Tunable; import org.cytoscape.work.util.ListMultipleSelection; @@ -19,9 +21,13 @@ public class MAMContext { @Tunable(description = "Edge attributes", groups = "Biomarker information", gravity = 10.0) public ListMultipleSelection edgeAttributes; + @ContainsTunables + public NormalizationContext normalizationContext; + public MAMContext(ClusterManager manager) { this.manager = manager; network = this.manager.getNetwork(); + normalizationContext = new NormalizationContext(manager, network); updateContext(); } @@ -72,6 +78,7 @@ private String getClusterColumnName() { public void setNetwork(CyNetwork network) { this.network = network; + normalizationContext.setNetwork(network); } public CyNetwork getNetwork() { diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/MAM/MultipleAttributeMultiplicative.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/MAM/MultipleAttributeMultiplicative.java index c116fcf..bc3fed7 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/MAM/MultipleAttributeMultiplicative.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/MAM/MultipleAttributeMultiplicative.java @@ -5,14 +5,17 @@ import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; import edu.ucsf.rbvi.clusterMaker2.internal.api.Rank; import edu.ucsf.rbvi.clusterMaker2.internal.utils.ClusterUtils; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.ModelUtils; import org.cytoscape.model.*; import org.cytoscape.work.AbstractTask; import org.cytoscape.work.ContainsTunables; import org.cytoscape.work.ObservableTask; +import org.cytoscape.work.ProvidesTitle; import org.cytoscape.work.TaskMonitor; import org.cytoscape.work.Tunable; import java.util.List; +import java.util.Map; public class MultipleAttributeMultiplicative extends AbstractTask implements Rank, ObservableTask { private ClusterManager manager; @@ -44,6 +47,7 @@ public String getShortName() { } @Override + @ProvidesTitle public String getName() { return NAME; } @@ -68,10 +72,22 @@ public void run(TaskMonitor taskMonitor) { taskMonitor.setProgress(0.6); taskMonitor.showMessage(TaskMonitor.Level.INFO, "Setting node scores in clusters"); - clusters = ClusterUtils.setNodeScoresInCluster(network, clusters, nodeAttributes, clusterColumnName, true); + // Begin by doing basic normalization -- in particular handling possible negative values + Map nodeMap = null; + if (nodeAttributes.size() > 0 && !nodeAttributes.get(0).equals(ModelUtils.NONEATTRIBUTE)) { + nodeMap = context.normalizationContext.normalize(nodeAttributes, network.getNodeList()); + clusters = ClusterUtils.setNodeScoresInCluster(network, clusters, nodeMap, clusterColumnName, true); + } + taskMonitor.setProgress(0.75); taskMonitor.showMessage(TaskMonitor.Level.INFO, "Setting edge scores in clusters"); - clusters = ClusterUtils.setEdgeScoresInCluster(network, clusters, edgeAttributes, clusterColumnName, true); + + Map edgeMap = null; + if (edgeAttributes.size() > 0 && !edgeAttributes.get(0).equals(ModelUtils.NONEATTRIBUTE)) { + edgeMap = context.normalizationContext.normalize(edgeAttributes, network.getEdgeList()); + clusters = ClusterUtils.setEdgeScoresInCluster(network, clusters, edgeMap, clusterColumnName, true); + } + taskMonitor.setProgress(0.80); taskMonitor.showMessage(TaskMonitor.Level.INFO, "Sorting and ranking clusters"); ClusterUtils.ascendingSort(clusters); diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/NodeRankingTaskFactory.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/NodeRankingTaskFactory.java new file mode 100644 index 0000000..00ec04e --- /dev/null +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/NodeRankingTaskFactory.java @@ -0,0 +1,69 @@ +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.ranking; + +import java.util.Collections; +import java.util.List; + +//Cytoscape imports +import org.cytoscape.model.CyNetwork; +import org.cytoscape.work.TaskIterator; + +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterTaskFactory; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterTaskFactory.ClusterType; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterViz; +import edu.ucsf.rbvi.clusterMaker2.internal.api.RankFactory; + +public class NodeRankingTaskFactory implements RankFactory { + ClusterManager clusterManager; + + public NodeRankingTaskFactory(ClusterManager clusterManager) { + this.clusterManager = clusterManager; + } + + public String getShortName() {return null; } + public String getName() {return "--- Node Ranking Algorithms ---";} + + public ClusterViz getVisualizer() { + // return new NewNetworkView(true); + return null; + } + + public boolean isReady() { + return false; + } + + public boolean isAvailable(CyNetwork network) { + return false; + } + + public List getTypeList() { + return Collections.singletonList(ClusterType.RANKING); + } + + public TaskIterator createTaskIterator() { + // Not sure why we need to do this, but it looks like + // the tunable stuff "remembers" objects that it's already + // processed this tunable. So, we use a copy constructor + return null; + } + + @Override + public Object getContext() { + return null; + } + + @Override + public String getSupportsJSON() { return "false"; } + + @Override + public String getLongDescription() { return ""; } + + @Override + public String getExampleJSON() { return ""; } + +} + + + + + diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/PR/PR.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/PR/PR.java index a7fd5af..811fc8f 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/PR/PR.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/PR/PR.java @@ -13,18 +13,22 @@ import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; import edu.ucsf.rbvi.clusterMaker2.internal.api.Rank; import edu.ucsf.rbvi.clusterMaker2.internal.utils.ClusterUtils; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.ModelUtils; import org.cytoscape.model.CyEdge; +import org.cytoscape.model.CyIdentifiable; import org.cytoscape.model.CyNetwork; import org.cytoscape.model.CyNode; import org.cytoscape.model.CyTable; import org.cytoscape.work.AbstractTask; import org.cytoscape.work.ContainsTunables; import org.cytoscape.work.ObservableTask; +import org.cytoscape.work.ProvidesTitle; import org.cytoscape.work.TaskMonitor; import org.cytoscape.work.Tunable; import java.util.HashMap; import java.util.List; +import java.util.Map; public class PR extends AbstractTask implements Rank, ObservableTask { private ClusterManager manager; @@ -36,6 +40,7 @@ public class PR extends AbstractTask implements Rank, ObservableTask { @ContainsTunables public PRContext context; + private Hypergraph graph; private HashMap idToNode; private List nodeList; @@ -63,6 +68,7 @@ public String getShortName() { } @Override + @ProvidesTitle public String getName() { return NAME; } @@ -75,7 +81,7 @@ public Object getContext() { @Override public void run(TaskMonitor taskMonitor) { taskMonitor.setProgress(0.0); - taskMonitor.setTitle("PRWP with Priors ranking of clusters"); + taskMonitor.setTitle("PR ranking of clusters"); taskMonitor.showMessage(TaskMonitor.Level.INFO, "Fetching clusters..."); taskMonitor.setProgress(0.1); List clusters = ClusterUtils.fetchClusters(network); @@ -92,6 +98,9 @@ public void run(TaskMonitor taskMonitor) { addEdges(); taskMonitor.setProgress(0.7); + // Normalize the scores based on the source node + normalizeEdges(); + taskMonitor.showMessage(TaskMonitor.Level.INFO, "Calculating PageRank scores"); PageRank pageRank = performPageRank(); taskMonitor.setProgress(0.8); @@ -118,6 +127,21 @@ public R getResults(Class clzz) { return results.getResults(clzz); } + private void normalizeEdges() { + // The PageRank algorithm requires that edge weights represent a transition probability -- that is, + // they must sum to 1. We need to adjust our edges to reflect that + for (PRNode node: graph.getVertices()) { + double sum = 0d; + for (PREdge edge: graph.getOutEdges(node)) { + sum += edge.getScore(); + } + + for (PREdge edge: graph.getOutEdges(node)) { + edge.setScore(edge.getScore()/sum); + } + } + } + private void insertScores(List clusters, PageRank pageRank) { for (PRNode node : graph.getVertices()) { node.setPRScore(pageRank.getVertexScore(node)); @@ -138,12 +162,22 @@ private PageRank performPageRank() { } private void addEdges() { + Map edgeMap = null; + context.normalizationContext.normalize(edgeAttributes, edgeList); + if (edgeAttributes.size() > 0 && !edgeAttributes.get(0).equals(ModelUtils.NONEATTRIBUTE)) + edgeMap = context.normalizationContext.normalize(edgeAttributes, edgeList); for (CyEdge edge : edgeList) { PRNode sourceNode = idToNode.get(edge.getSource().getSUID()); PRNode targetNode = idToNode.get(edge.getTarget().getSUID()); PREdge prEdge = new PREdge(edge); - insertEdgeScore(prEdge, edgeTable, edgeAttributes); - graph.addEdge(prEdge, new Pair<>(sourceNode, targetNode), EdgeType.DIRECTED); + if (edgeMap != null) + insertEdgeScore(prEdge, edgeMap.get(edge)); + else + prEdge.setScore(0.0d); + if (edge.isDirected()) + graph.addEdge(prEdge, new Pair<>(sourceNode, targetNode), EdgeType.DIRECTED); + else + graph.addEdge(prEdge, new Pair<>(sourceNode, targetNode), EdgeType.UNDIRECTED); } } @@ -167,26 +201,12 @@ private void initVariables() { } - private void insertEdgeScore(PREdge prEdge, CyTable edgeTable, List edgeAttributes) { - Double totalEdgeScore = 0.0d; - - for (String edgeAttribute : edgeAttributes) { - double singleEdgeAttributeScore = 0.0d; + private void insertEdgeScore(PREdge prEdge, double[] edgeAttrValues) { + double totalEdgeScore = 0.0d; - try { // Double - singleEdgeAttributeScore = edgeTable.getRow(prEdge.getCyEdge().getSUID()) - .get(edgeAttribute, Double.class, 0.0d); - } catch (ClassCastException cce) { - try { // Integer - singleEdgeAttributeScore = edgeTable.getRow(prEdge.getCyEdge().getSUID()) - .get(edgeAttribute, Integer.class, 0); - } catch (Exception e) { - e.printStackTrace(); - } - } finally { - totalEdgeScore += singleEdgeAttributeScore; - } - } + for (double value: edgeAttrValues) { + totalEdgeScore += value; + } prEdge.setScore(totalEdgeScore); } diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/PR/PRContext.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/PR/PRContext.java index 83f9bc6..37ce35e 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/PR/PRContext.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/PR/PRContext.java @@ -1,8 +1,10 @@ package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.ranking.PR; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.ranking.units.NormalizationContext; import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; import edu.ucsf.rbvi.clusterMaker2.internal.utils.ModelUtils; import org.cytoscape.model.CyNetwork; +import org.cytoscape.work.ContainsTunables; import org.cytoscape.work.Tunable; import org.cytoscape.work.util.ListMultipleSelection; @@ -22,9 +24,13 @@ public class PRContext { @Tunable(description = "Iterations", groups = "PR factors", gravity = 10.0) public int iterations = 1000; + @ContainsTunables + public NormalizationContext normalizationContext; + public PRContext(ClusterManager manager) { this.manager = manager; network = this.manager.getNetwork(); + normalizationContext = new NormalizationContext(manager, network); updateContext(); } @@ -56,6 +62,7 @@ public String getClusterColumnName() { public void setNetwork(CyNetwork network) { this.network = network; + normalizationContext.setNetwork(network); } public CyNetwork getNetwork() { diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/PRWP/PRWP.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/PRWP/PRWP.java index 5a49b84..b9d138d 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/PRWP/PRWP.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/PRWP/PRWP.java @@ -13,18 +13,22 @@ import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; import edu.ucsf.rbvi.clusterMaker2.internal.api.Rank; import edu.ucsf.rbvi.clusterMaker2.internal.utils.ClusterUtils; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.ModelUtils; import org.cytoscape.model.CyEdge; +import org.cytoscape.model.CyIdentifiable; import org.cytoscape.model.CyNetwork; import org.cytoscape.model.CyNode; import org.cytoscape.model.CyTable; import org.cytoscape.work.AbstractTask; import org.cytoscape.work.ContainsTunables; import org.cytoscape.work.ObservableTask; +import org.cytoscape.work.ProvidesTitle; import org.cytoscape.work.TaskMonitor; import org.cytoscape.work.Tunable; import java.util.HashMap; import java.util.List; +import java.util.Map; public class PRWP extends AbstractTask implements Rank, ObservableTask { private ClusterManager manager; @@ -36,6 +40,7 @@ public class PRWP extends AbstractTask implements Rank, ObservableTask { @ContainsTunables public PRWPContext context; + private Hypergraph graph; private HashMap idToNode; private List nodeList; @@ -64,6 +69,7 @@ public String getShortName() { } @Override + @ProvidesTitle public String getName() { return NAME; } @@ -93,6 +99,14 @@ public void run(TaskMonitor taskMonitor) { addEdges(); taskMonitor.setProgress(0.7); + // Normalize the scores + //if (nodeAttributes.size() > 0 && !nodeAttributes.get(0).equals(ModelUtils.NONEATTRIBUTE)) + // normalizeNodes(); + + // Normalize the scores based on the source node + if (edgeAttributes.size() > 0 && !edgeAttributes.get(0).equals(ModelUtils.NONEATTRIBUTE)) + normalizeEdges(); + taskMonitor.showMessage(TaskMonitor.Level.INFO, "Calculating PageRank scores"); PageRankWithPriors pageRank = performPageRank(); taskMonitor.setProgress(0.8); @@ -119,6 +133,33 @@ public R getResults(Class clzz) { return results.getResults(clzz); } + private void normalizeNodes() { + double sum = 0d; + for (PRNode node: graph.getVertices()) { + sum += node.getScore(); + } + + for (PRNode node: graph.getVertices()) { + node.setScore(node.getScore()/sum); + } + + } + + private void normalizeEdges() { + // The PageRank algorithm requires that edge weights represent a transition probability -- that is, + // they must sum to 1. We need to adjust our edges to reflect that + for (PRNode node: graph.getVertices()) { + double sum = 0d; + for (PREdge edge: graph.getOutEdges(node)) { + sum += edge.getScore(); + } + + for (PREdge edge: graph.getOutEdges(node)) { + edge.setScore(edge.getScore()/sum); + } + } + } + private void insertScores(List clusters, PageRankWithPriors pageRank) { for (PRNode node : graph.getVertices()) { node.setPRScore(pageRank.getVertexScore(node)); @@ -139,19 +180,33 @@ private PageRankWithPriors performPageRank() { } private void addEdges() { + Map edgeMap = null; + context.normalizationContext.normalize(edgeAttributes, edgeList); + if (edgeAttributes.size() > 0 && !edgeAttributes.get(0).equals(ModelUtils.NONEATTRIBUTE)) + edgeMap = context.normalizationContext.normalize(edgeAttributes, edgeList); for (CyEdge edge : edgeList) { PRNode sourceNode = idToNode.get(edge.getSource().getSUID()); PRNode targetNode = idToNode.get(edge.getTarget().getSUID()); PREdge prEdge = new PREdge(edge); - insertEdgeScore(prEdge, edgeTable, edgeAttributes); + if (edgeMap != null) + insertEdgeScore(prEdge, edgeMap.get(edge)); + else + prEdge.setScore(0.0d); graph.addEdge(prEdge, new Pair<>(sourceNode, targetNode), EdgeType.DIRECTED); } } private void addNodes() { + Map nodeMap = null; + if (nodeAttributes.size() > 0 && !nodeAttributes.get(0).equals(ModelUtils.NONEATTRIBUTE)) + nodeMap = context.normalizationContext.normalize(nodeAttributes, nodeList); + for (CyNode node : nodeList) { PRNode prNode = new PRNode(node); - insertNodeScore(prNode, nodeTable, nodeAttributes); + if (nodeMap != null) + insertNodeScore(prNode, nodeMap.get(node)); + else + prNode.setScore(0.0d); graph.addVertex(prNode); idToNode.put(node.getSUID(), prNode); } @@ -169,9 +224,15 @@ private void initVariables() { edgeTable = network.getDefaultEdgeTable(); } - private void insertNodeScore(PRNode prNode, CyTable nodeTable, List nodeAttributes) { - Double totalNodeScore = 0.0d; + private void insertNodeScore(PRNode prNode, double[] nodeAttrValues) { + double totalNodeScore = 0.0d; + + for (double value: nodeAttrValues) { + totalNodeScore += value; + } + prNode.setScore(totalNodeScore); + /* for (String nodeAttribute : nodeAttributes) { double singleAttributeScore = 0.0d; @@ -189,13 +250,19 @@ private void insertNodeScore(PRNode prNode, CyTable nodeTable, List node totalNodeScore += singleAttributeScore; } } + */ prNode.setScore(totalNodeScore); } - private void insertEdgeScore(PREdge prEdge, CyTable edgeTable, List edgeAttributes) { - Double totalEdgeScore = 0.0d; + private void insertEdgeScore(PREdge prEdge, double[] edgeAttrValues) { + double totalEdgeScore = 0.0d; + + for (double value: edgeAttrValues) { + totalEdgeScore += value; + } + /* for (String edgeAttribute : edgeAttributes) { double singleEdgeAttributeScore = 0.0d; @@ -213,6 +280,7 @@ private void insertEdgeScore(PREdge prEdge, CyTable edgeTable, List edge totalEdgeScore += singleEdgeAttributeScore; } } + */ prEdge.setScore(totalEdgeScore); } diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/PRWP/PRWPContext.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/PRWP/PRWPContext.java index 914fd47..c90ddd5 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/PRWP/PRWPContext.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/PRWP/PRWPContext.java @@ -1,11 +1,14 @@ package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.ranking.PRWP; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.ranking.units.NormalizationContext; import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; import edu.ucsf.rbvi.clusterMaker2.internal.utils.ModelUtils; import org.cytoscape.model.CyNetwork; +import org.cytoscape.work.ContainsTunables; import org.cytoscape.work.Tunable; import org.cytoscape.work.util.ListMultipleSelection; + import java.util.List; import java.util.stream.Collectors; @@ -25,9 +28,13 @@ public class PRWPContext { @Tunable(description = "Edge attributes", groups = "Biomarker information", gravity = 30.0) public ListMultipleSelection edgeAttributes; + @ContainsTunables + public NormalizationContext normalizationContext; + public PRWPContext(ClusterManager manager) { this.manager = manager; network = this.manager.getNetwork(); + normalizationContext = new NormalizationContext(manager, network); updateContext(); } @@ -78,6 +85,7 @@ public String getClusterColumnName() { public void setNetwork(CyNetwork network) { this.network = network; + normalizationContext.setNetwork(network); } public CyNetwork getNetwork() { diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/units/NormalizationContext.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/units/NormalizationContext.java new file mode 100644 index 0000000..f983d7b --- /dev/null +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/ranking/units/NormalizationContext.java @@ -0,0 +1,127 @@ +package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.ranking.units; + +import org.cytoscape.model.CyEdge; +import org.cytoscape.model.CyIdentifiable; +import org.cytoscape.model.CyNetwork; +import org.cytoscape.model.CyNode; +import org.cytoscape.model.CyRow; +import org.cytoscape.work.Tunable; +import org.cytoscape.work.util.ListSingleSelection; + +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class NormalizationContext { + private CyNetwork network; + public ClusterManager manager; + + // Normalization + @Tunable + (description = "Normalization", + exampleStringValue = "None", + tooltip = "The algorithm assumes value between 0 and 1. Basic normalization normalizes each attribute to 0-1.", + groups = "Attribute normalization", gravity = 1.0) + public ListSingleSelection normalization = new ListSingleSelection("Basic", "None"); + + // Two-tailed attributes : absolute value, pos values, neg values + @Tunable + (description = "Two-tailed values", + exampleStringValue = "Absolute value", + tooltip = "The algorithm assumes value between 0 and 1", + groups = "Attribute normalization", gravity = 2.0) + public ListSingleSelection twoTailedValues = new ListSingleSelection("Absolute value", "Only positive values", "Only negative values"); + + public NormalizationContext(ClusterManager manager) { + this(manager, manager.getNetwork()); + } + + public NormalizationContext(ClusterManager manager, CyNetwork network) { + this.manager = manager; + this.network = network; + } + + public void setNetwork(CyNetwork network) { + this.network = network; + } + + public CyNetwork getNetwork() { + return network; + } + + public Map normalizeEdges(List attributeList) { + List edgeList = network.getEdgeList(); + return normalize(attributeList, edgeList); + } + + public Map normalizeNodes(List attributeList) { + List nodeList = network.getNodeList(); + return normalize(attributeList, nodeList); + } + + public Map normalize(List attributeList, List objs) { + Map returnMap = new HashMap<>(); + if (network == null) network = manager.getNetwork(); + + int nAttributes = attributeList.size(); + + boolean negativeOnly = twoTailedValues.getSelectedValue().equals("Only negative values"); + boolean positiveOnly = twoTailedValues.getSelectedValue().equals("Only positive values"); + + // Go through the attributes and find the minimum and maximum values + Map minMax = new HashMap<>(); + for (String attr: attributeList) { + minMax.put(attr, new double[]{Double.MAX_VALUE, Double.MIN_VALUE}); + } + + // Get all of the values + for (CyIdentifiable obj: objs) { + CyRow row = network.getRow(obj); + double[] values = new double[attributeList.size()]; + for (int index = 0; index < nAttributes; index++) { + Double v; + try { + v = row.get(attributeList.get(index), Double.class); + } catch (ClassCastException e) { + Integer intV = row.get(attributeList.get(index), Integer.class); + v = Double.valueOf(intV); + } + if (v == null) + values[index] = 0.0d; // values[index] = Double.NaN; -- if we're going to use NaN we need to account for it everywhere + else if (v < 0) { + if (positiveOnly) + values[index] = 0.0d; + else + values[index] = Math.abs(v); + } else if (negativeOnly) + values[index] = 0.0d; + else + values[index] = v; + + double[] myMinMax = minMax.get(attributeList.get(index)); + myMinMax[0] = Math.min(myMinMax[0], values[index]); + myMinMax[1] = Math.max(myMinMax[1], values[index]); + } + returnMap.put(obj, values); + } + + if (normalization.getSelectedValue().equals("None")) + return returnMap; + + // OK, now that we have the minMax, do the normalization, handling two-tailed values as we go + for (int index = 0; index < nAttributes; index++) { + double[] myMinMax = minMax.get(attributeList.get(index)); + for (CyIdentifiable obj: returnMap.keySet()) { + double value = returnMap.get(obj)[index]; + value = (value - myMinMax[0])/myMinMax[1]; + returnMap.get(obj)[index] = value; + } + } + + return returnMap; + } + +} diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/api/ClusterTaskFactory.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/api/ClusterTaskFactory.java index c9bda66..8a63b90 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/api/ClusterTaskFactory.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/api/ClusterTaskFactory.java @@ -6,7 +6,7 @@ import org.cytoscape.work.TaskFactory; public interface ClusterTaskFactory extends TaskFactory { - public enum ClusterType { NETWORK, ATTRIBUTE, FILTER, DIMRED, UI }; + public enum ClusterType { NETWORK, ATTRIBUTE, FILTER, DIMRED, UI, RANKING }; /** * Get the short name of this algorithm diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/NewNetworkView.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/NewNetworkView.java index 557fcf7..b7efbcd 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/NewNetworkView.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/NewNetworkView.java @@ -70,6 +70,7 @@ // ClusterMaker imports import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.edgeConverters.EdgeAttributeHandler; import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.edgeConverters.EdgeWeightConverter; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.FuzzyNodeCluster; import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterAlgorithm; import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterResults; @@ -102,21 +103,22 @@ public class NewNetworkView extends AbstractTask implements ClusterViz, ClusterA @ContainsTunables public NewNetworkViewContext context = null; - private CyNetworkView networkView = null;; + private CyNetworkView networkView = null; public NewNetworkView(CyNetwork network, ClusterManager manager) { - this(null, manager, true, true); + this(manager, true, true); this.network = network; } public NewNetworkView(CyNetwork network, ClusterManager manager, boolean available, boolean restoreEdges, boolean addSingletons) { - this(null, manager, true, addSingletons); + this(manager, true, addSingletons); this.network = network; this.restoreEdges = restoreEdges; + this.context = null; } - public NewNetworkView(NewNetworkViewContext context, ClusterManager manager, boolean available, boolean addSingletons) { + public NewNetworkView(ClusterManager manager, boolean available, boolean addSingletons) { this.manager = manager; checkForAvailability = available; this.addSingletons = addSingletons; @@ -124,10 +126,10 @@ public NewNetworkView(NewNetworkViewContext context, ClusterManager manager, boo network = manager.getNetwork(); if (!checkForAvailability) { - this.context = context; + this.context = new NewNetworkViewContext(true); context.setNetwork(network); } else { - this.context = null; + this.context = new NewNetworkViewContext(false); } edgeConverterList = new EdgeAttributeHandler(network, false); } @@ -231,14 +233,16 @@ public List> getResultClasses() { @SuppressWarnings("unchecked") private void createClusteredNetwork(String clusterAttribute, TaskMonitor monitor) { + boolean selectedOnly = false; + if (context != null) + selectedOnly = context.selectedOnly; boolean isFuzzy = isFuzzy(clusterAttribute); - // System.out.println("isFuzzy = "+isFuzzy); // Get the clustering parameters Map params = getParams(); List nodeList = new ArrayList(); - Map> clusterMap = getClusterMap(clusterAttribute, nodeList); + Map> clusterMap = getClusterMap(clusterAttribute, selectedOnly, nodeList); // Special handling for edge weight thresholds EdgeWeightConverter converter = @@ -281,18 +285,24 @@ private void createClusteredNetwork(String clusterAttribute, TaskMonitor monitor ModelUtils.copyLocalColumn(network, newNetwork, CyNetwork.class, "__clusterAttribute"); ModelUtils.copyLocalColumn(network, newNetwork, CyNetwork.class, "__clusterParams"); + String fuzzySeed = null; // Finally, if we're fuzzy, see if we had an initial seed and copy that over if (isFuzzy && ModelUtils.hasAttribute(network, network, "__fuzzifierSeed")) { ModelUtils.copyLocalColumn(network, newNetwork, CyNetwork.class, "__fuzzifierSeed"); - String seedAttribute = + fuzzySeed = network.getRow(network, CyNetwork.LOCAL_ATTRS).get("__fuzzifierSeed", String.class); - ModelUtils.copyLocalColumn(network, newNetwork, CyNode.class, seedAttribute); + ModelUtils.copyLocalColumn(network, newNetwork, CyNode.class, fuzzySeed); } // System.out.println("Getting the view"); networkView = ViewUtils.createView(manager, newNetwork, false); - ViewUtils.doLayout(manager, networkView, monitor, "force-directed"); + // If we fuzzy, we probably don't want to relayout the network -- we want + // to map the fuzzy nodes onto the existing network instead + if (!isFuzzy) + ViewUtils.doLayout(manager, networkView, monitor, "force-directed"); + else + ViewUtils.copyLayout(manager, manager.getNetworkView(network), networkView); // Now, if we're supposed to, restore the inter-cluster edges if (restoreEdges || (context != null && context.restoreEdges)) { @@ -309,14 +319,12 @@ private void createClusteredNetwork(String clusterAttribute, TaskMonitor monitor ViewUtils.setVisualStyle(manager, networkView, style); if(isFuzzy){ - + ModelUtils.copyLocalColumn(network, newNetwork, CyNetwork.class, clusterAttribute + "_Table.SUID"); long fuzzyClusterTableSUID = network.getRow(network).get(clusterAttribute + "_Table.SUID", Long.class); newNetwork.getRow(newNetwork).set(clusterAttribute + "_Table.SUID", fuzzyClusterTableSUID); - //System.out.println("NetworkName: "+ network.getRow(network).get(CyNetwork.NAME, String.class)); - //System.out.println("Fuzzy Table SUID: " + fuzzyClusterTableSUID ); CyTable fuzzyClusterTable = manager.getTableManager().getTable(fuzzyClusterTableSUID); - // System.out.println("Creating membership edges"); - createMembershipEdges(newNetwork,networkView,manager,fuzzyClusterTable); + List fuzzyClusters = getFuzzyClusters(network, selectedOnly, fuzzyClusterTable); + createMembershipEdges(newNetwork, selectedOnly, networkView,manager, fuzzyClusters, fuzzySeed); } ViewUtils.registerView(manager, networkView); @@ -324,7 +332,43 @@ private void createClusteredNetwork(String clusterAttribute, TaskMonitor monitor return; } - private Map> getClusterMap(String clusterAttribute, List nodeList) { + private List getFuzzyClusters(CyNetwork network, boolean selectedOnly, CyTable fuzzyClusterTable) { + List fClusters = new ArrayList<>(); + + Set columns = CyTableUtil.getColumnNames(fuzzyClusterTable); + int clusters[] = new int[columns.size()]; + int cluster_index = 0; + for (String colName: columns) { + if (colName.startsWith("Cluster_")) { + int cluster_number = Integer.parseInt(colName.substring(8)); + clusters[cluster_index++] = cluster_number; + FuzzyNodeCluster fnc = new FuzzyNodeCluster(); + fnc.setClusterNumber(cluster_number); + fClusters.add(fnc); + } + } + + List nodeList = ModelUtils.getNodeList(network,selectedOnly); + for (CyNode node: nodeList) { + CyRow row = fuzzyClusterTable.getRow(node.getSUID()); + if (row == null) continue; + + Map rowData = row.getAllValues(); + cluster_index = 0; + for (String colName: columns) { + if (!colName.startsWith("Cluster_")) + continue; + FuzzyNodeCluster fnc = fClusters.get(cluster_index++); + Double value = (Double)rowData.get(colName); + if (value != null) + fnc.add(node, value); + } + } + + return fClusters; + } + + private Map> getClusterMap(String clusterAttribute, boolean selectedOnly, List nodeList) { // Two possibilities. We may have a fuzzy cluster or a discrete cluster. Figure // that out now. boolean isFuzzy = @@ -332,7 +376,7 @@ private Map> getClusterMap(String clusterAttribute, List> clusterMap = new HashMap>(); - for (CyNode node: network.getNodeList()) { + for (CyNode node: ModelUtils.getNodeList(network, selectedOnly)) { // For each node -- see if it's in a cluster. If so, add it to our map if (ModelUtils.hasAttribute(network, node, clusterAttribute)) { if (isFuzzy) { @@ -419,75 +463,83 @@ String getParam(Map params, String key) { /** * Method to add the membership edges */ - private void createMembershipEdges(CyNetwork network, CyNetworkView networkView, - ClusterManager manager,CyTable fuzzyClusterTable){ - - List> clusterList = new ArrayList>(); // List of node lists - - int numC = fuzzyClusterTable.getColumns().size() - 1; - for(int i = 0; i < numC; i++){ - clusterList.add(new ArrayList()); - } - - List nodeList = network.getNodeList(); - for (CyNode node : nodeList){ - CyRow nodeRow = fuzzyClusterTable.getRow(node.getSUID()); - for(int i = 1; i <= numC; i++){ - if(nodeRow.get("Cluster_"+ i, Double.class) != null){ - clusterList.get(i-1).add(node); - } - } - } + private void createMembershipEdges(CyNetwork network, boolean selectedOnly, CyNetworkView networkView, + ClusterManager manager, List fuzzyClusters, String fuzzySeed) { CyTable localTable = network.getTable(CyNode.class, CyNetwork.LOCAL_ATTRS); localTable.createColumn("isFClusterNode", Boolean.class, false, Boolean.FALSE); + + Map> allEdges = new HashMap<>(); + Map centroids = new HashMap<>(); + int clusterNumber = 0; - for(List cluster :clusterList){ + for(FuzzyNodeCluster cluster :fuzzyClusters){ CyNode centroid = network.addNode(); - clusterNumber++; - network.getRow(centroid).set(CyNetwork.NAME, "FCluster" + clusterList.indexOf(cluster) ); + + network.getRow(centroid).set(CyNetwork.NAME, "FCluster" + cluster.getClusterNumber() ); network.getRow(centroid).set("isFClusterNode", Boolean.TRUE); + // For convenience, add the base cluster number to our fuzzy cluster + network.getRow(centroid).set(fuzzySeed, cluster.getClusterNumber()); + // System.out.println("Centroid SUID: " + centroid.getSUID()); //View nodeView = networkView.getNodeView(centroid); - double x = 0.0001; - double y = 0.0001; - double count = 0; - + double x = 0.0; + double y = 0.0; + double weightSum = 0; List membershipEdges = new ArrayList(); for (CyNode node : cluster) { View nodeView = networkView.getNodeView(node); //System.out.println("NodeView SUID: " + nodeView.getSUID()); - x += nodeView.getVisualProperty(BasicVisualLexicon.NODE_X_LOCATION); - y += nodeView.getVisualProperty(BasicVisualLexicon.NODE_Y_LOCATION); - //System.out.println(x); - count += 1; + + Double membership = cluster.getMembership(node); + x += nodeView.getVisualProperty(BasicVisualLexicon.NODE_X_LOCATION)*membership; + y += nodeView.getVisualProperty(BasicVisualLexicon.NODE_Y_LOCATION)*membership; + weightSum += membership; //System.out.println("Read x = "+ x +", y = "+ y); CyEdge membershipEdge = network.addEdge(centroid, node, false); - // System.out.println("Added edge: "+membershipEdge); ModelUtils.createAndSetLocal(network, membershipEdge, "MembershipEdge", Boolean.TRUE, Boolean.class, null); - Double membership = fuzzyClusterTable.getRow(node.getSUID()).get("Cluster_"+clusterNumber, Double.class); ModelUtils.createAndSetLocal(network, membershipEdge, "Membership_%", 100*membership, Double.class, null); membershipEdges.add(membershipEdge); } - networkView.updateView(); - - if(count!=0){ - x = x/count; - y = y/count; + // networkView.updateView(); + + if(weightSum!=0){ + x = x/weightSum; + y = y/weightSum; + double[] p = new double[]{x,y}; + centroids.put(centroid, p); + // View centroidView = networkView.getNodeView(centroid); + // System.out.println("CentroidView SUID: " + centroidView.getSUID()); + // System.out.println("x = "+ x +", y = "+ y); } - View centroidView = networkView.getNodeView(centroid); + // View centroidView = networkView.getNodeView(centroid); // System.out.println("CentroidView SUID: " + centroidView.getSUID()); // System.out.println("x = "+ x +", y = "+ y); - centroidView.setVisualProperty(BasicVisualLexicon.NODE_X_LOCATION, x); - centroidView.setVisualProperty(BasicVisualLexicon.NODE_Y_LOCATION, y); + // centroidView.setVisualProperty(BasicVisualLexicon.NODE_X_LOCATION, x); + // centroidView.setVisualProperty(BasicVisualLexicon.NODE_Y_LOCATION, y); - membershipEdgeStyle(networkView, clusterNumber, membershipEdges, fuzzyClusterTable); + allEdges.put(clusterNumber, membershipEdges); + clusterNumber++; + // membershipEdgeStyle(networkView, clusterNumber, membershipEdges, fuzzyClusterTable); } + networkView.updateView(); + + // First, create all of our centroid node views + for (CyNode centroid: centroids.keySet()) { + View centroidView = networkView.getNodeView(centroid); + centroidView.setVisualProperty(BasicVisualLexicon.NODE_X_LOCATION, centroids.get(centroid)[0]); + centroidView.setVisualProperty(BasicVisualLexicon.NODE_Y_LOCATION, centroids.get(centroid)[1]); + } + + System.out.println("Creating membership edges"); + for (int cluster_index = 0; cluster_index < fuzzyClusters.size(); cluster_index++) { + membershipEdgeStyle(networkView, allEdges.get(cluster_index)); + } networkView.updateView(); } @@ -499,16 +551,22 @@ private void createMembershipEdges(CyNetwork network, CyNetworkView networkView, * @param edgeList List of added membership edges for the cluster * @param fuzzyClusterTable table having information about the fuzzy clusters */ - private void membershipEdgeStyle(CyNetworkView networkView, int cNum, - List edgeList, CyTable fuzzyClusterTable){ + private void membershipEdgeStyle(CyNetworkView networkView, List edgeList) { + CyNetwork network = networkView.getModel(); for (CyEdge edge : edgeList){ + Double membership = network.getRow(edge).get("Membership_%", Double.class); + if (membership == null) + continue; + + int trans = membership.intValue() * 255/100; + View edgeView = networkView.getEdgeView(edge); + CyNode node = edge.getTarget(); //edgeView.setVisualProperty(BasicVisualLexicon.EDGE_LINE_TYPE, LineTypeVisualProperty.DASH_DOT); edgeView.setLockedValue(BasicVisualLexicon.EDGE_LINE_TYPE, LineTypeVisualProperty.DASH_DOT); + edgeView.setLockedValue(BasicVisualLexicon.EDGE_TRANSPARENCY, trans); - edgeView.setLockedValue(BasicVisualLexicon.EDGE_TRANSPARENCY, - (int)(fuzzyClusterTable.getRow(node.getSUID()).get("Cluster_"+ cNum, Double.class)*255)); } } diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/NewNetworkViewContext.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/NewNetworkViewContext.java index a5fec43..9f30b10 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/NewNetworkViewContext.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/NewNetworkViewContext.java @@ -11,13 +11,15 @@ public class NewNetworkViewContext { CyNetwork network; + boolean useAttr; //Tunables - public ListSingleSelection attribute; + public ListSingleSelection attribute = null; @Tunable(description = "Cluster Attribute to Use", groups={"New Network Options"}, gravity=1.0) public ListSingleSelection getattribute(){ + if (useAttr) return null; attribute = ModelUtils.updateEdgeAttributeList(network, attribute); return attribute; } @@ -31,7 +33,8 @@ public void setattribute(ListSingleSelection attr) { } groups={"New Network Options"}, gravity=3.0) public boolean restoreEdges = false; - public NewNetworkViewContext() { + public NewNetworkViewContext(boolean useAttr) { + this.useAttr = useAttr; } public CyNetwork getNetwork() { return network; } diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/NewNetworkViewFactory.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/NewNetworkViewFactory.java index 08398ce..0f551ec 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/NewNetworkViewFactory.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/NewNetworkViewFactory.java @@ -20,7 +20,7 @@ public class NewNetworkViewFactory implements ClusterVizFactory { boolean checkAvailable; public NewNetworkViewFactory(ClusterManager clusterManager, boolean checkAvailable) { - context = new NewNetworkViewContext(); + // context = new NewNetworkViewContext(); this.clusterManager = clusterManager; this.checkAvailable = checkAvailable; } @@ -70,7 +70,7 @@ public TaskIterator createTaskIterator() { // Not sure why we need to do this, but it looks like // the tunable stuff "remembers" objects that it's already // processed this tunable. So, we use a copy constructor - return new TaskIterator(new NewNetworkView(context, clusterManager, checkAvailable, true)); + return new TaskIterator(new NewNetworkView(clusterManager, checkAvailable, true)); } @Override diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/RankingPanel.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/RankingPanel.java index 6c9f684..6d25e9d 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/RankingPanel.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/RankingPanel.java @@ -27,7 +27,9 @@ import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics2D; +import java.awt.GridLayout; import java.awt.Image; +import java.awt.Paint; import java.awt.event.ActionEvent; import java.awt.image.BufferedImage; import java.text.NumberFormat; @@ -40,6 +42,7 @@ import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JButton; +import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; @@ -51,11 +54,15 @@ import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.AbstractTableModel; +import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableCellRenderer; +import javax.swing.UIManager; import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.NodeCluster; import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; import edu.ucsf.rbvi.clusterMaker2.internal.utils.ModelUtils; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.SpringEmbeddedLayouter; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.ViewUtils; import org.cytoscape.application.CyApplicationManager; import org.cytoscape.application.swing.CySwingApplication; @@ -68,17 +75,23 @@ import org.cytoscape.model.SavePolicy; import org.cytoscape.model.subnetwork.CyRootNetwork; import org.cytoscape.model.subnetwork.CyRootNetworkManager; +import org.cytoscape.util.color.BrewerType; +import org.cytoscape.util.color.Palette; +import org.cytoscape.util.swing.CyColorPaletteChooser; +import org.cytoscape.util.swing.CyColorPaletteChooserFactory; import org.cytoscape.view.model.CyNetworkView; import org.cytoscape.view.model.CyNetworkViewFactory; import org.cytoscape.view.model.View; import org.cytoscape.view.presentation.RenderingEngine; import org.cytoscape.view.presentation.RenderingEngineFactory; +import org.cytoscape.view.vizmap.VisualMappingFunctionFactory; import org.cytoscape.view.vizmap.VisualMappingManager; import org.cytoscape.view.vizmap.VisualStyle; import org.cytoscape.view.vizmap.VisualStyleFactory; +import org.cytoscape.view.vizmap.mappings.BoundaryRangeValues; +import org.cytoscape.view.vizmap.mappings.ContinuousMapping; import org.cytoscape.work.TaskMonitor; - public class RankingPanel extends JPanel implements CytoPanelComponent{ private static final long serialVersionUID = 868213052692609076L; @@ -169,10 +182,15 @@ public CyNetwork getNetwork() { } + private static Integer getClusterNumber(final NodeCluster cluster) { + int clusterNumber = cluster.getClusterNumber(); + return clusterNumber; + } + private static StringBuilder getRankScore(final NodeCluster cluster) { StringBuilder details = new StringBuilder(); - details.append("Score: "); + // details.append("Score: "); NumberFormat nf = NumberFormat.getInstance(); nf.setMaximumFractionDigits(3); details.append(nf.format(cluster.getRankScore())); @@ -212,9 +230,21 @@ public RankingBrowserPanel(RankingPanel component) { table = new JTable(browserModel); table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); table.setAutoCreateRowSorter(true); - table.setDefaultRenderer(StringBuffer.class, new ResultsPanel.JTextAreaRenderer(defaultRowHeight)); + // table.setDefaultRenderer(StringBuffer.class, new ResultsPanel.JTextAreaRenderer(defaultRowHeight)); + table.setDefaultRenderer(ImageIcon.class, new ImageRenderer(defaultRowHeight)); table.setIntercellSpacing(new Dimension(0, 4)); // gives a little vertical room between clusters table.setFocusable(false); // removes an outline that appears when the user clicks on the images + // + // DefaultTableCellRenderer columnRenderer = new DefaultTableCellRenderer(); + // columnRenderer.setHorizontalAlignment(JLabel.CENTER); + table.getTableHeader().setDefaultRenderer(new HeaderRenderer()); + // DefaultTableCellRenderer headerRenderer = (DefaultTableCellRenderer)table.getTableHeader().getDefaultRenderer(); + // headerRenderer.setFont(new Font("sans-serif", Font.BOLD, 10)); + // headerRenderer.setHorizontalAlignment(JLabel.CENTER); + + + table.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS); + table.getColumnModel().getColumn(0).setPreferredWidth(80); //System.out.println("CBP: after setting table params"); @@ -229,7 +259,9 @@ public RankingBrowserPanel(RankingPanel component) { add(tableScrollPane, BorderLayout.CENTER); //System.out.println("CBP: after adding JScrollPane"); + Font buttonFont = new Font("sans-serif", Font.BOLD, 10); JButton dispose = new JButton("Remove Results"); + dispose.setFont(buttonFont); dispose.addActionListener(new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { @@ -242,8 +274,40 @@ public void actionPerformed(ActionEvent e) { } }); - JPanel buttonPanel = new JPanel(); + JButton color = new JButton("Color nodes by rank"); + color.setFont(buttonFont); + color.addActionListener(new AbstractAction() { + boolean undo = false; + VisualStyle sourceStyle = null; + VisualStyle rankingStyle = null; + @Override + public void actionPerformed(ActionEvent e) { + if (!undo) { + CyColorPaletteChooserFactory chooserFactory = clusterManager.getService(CyColorPaletteChooserFactory.class); + CyColorPaletteChooser paletteChooser = chooserFactory.getColorPaletteChooser(BrewerType.SEQUENTIAL, true); + Palette colorPalette = paletteChooser.showDialog(null, "Choose color palette for node fill", null, 9); + + double[] pivots = new double[]{0.0,0.5,1.0}; + + sourceStyle = ViewUtils.getCurrentVisualStyle(clusterManager); + rankingStyle = ViewUtils.createFillStyle(clusterManager, sourceStyle, "_ranking", rankingType, colorPalette,pivots); + ViewUtils.setVisualStyle(clusterManager, clusterManager.getNetworkView(), rankingStyle); + color.setText("Remove ranking color"); + undo = true; + } else { + ViewUtils.setVisualStyle(clusterManager, clusterManager.getNetworkView(), sourceStyle); + clusterManager.getService(VisualMappingManager.class).removeVisualStyle(rankingStyle); + rankingStyle = null; + + color.setText("Color nodes by rank"); + undo = false; + } + } + }); + + JPanel buttonPanel = new JPanel(new GridLayout(2,1)); buttonPanel.add(dispose); + buttonPanel.add(color); add(buttonPanel, BorderLayout.SOUTH); } @@ -297,8 +361,9 @@ public void valueChanged(ListSelectionEvent e) { */ private class RankingBrowserPanelModel extends AbstractTableModel { - private final String[] columnNames = { "Network", "Score" }; + private final String[] columnNames = { "Network", "Cluster", "Score" }; private final Object[][] data; // the actual table data + private int rowCount = 0; public RankingBrowserPanelModel() { //System.out.println("CBTM: inside constructor"); @@ -306,18 +371,26 @@ public RankingBrowserPanelModel() { data = new Object[clusters.size()][columnNames.length]; //System.out.println("CBTM: after initialising exploreContent and data"); + VisualStyle vs = ViewUtils.getClusterStyle(clusterManager, null); + + SpringEmbeddedLayouter layouter = new SpringEmbeddedLayouter(); + for (int i = 0; i < clusters.size(); i++) { - //System.out.println("CBTM: cluster num: "+ i); + // System.out.println("CBTM: cluster num: "+ i); final NodeCluster c = clusters.get(i); + if (c.getRankScore() < 0.1) + continue; //c.setRank(i); - StringBuilder details = getRankScore(c); - data[i][1] = new StringBuffer(details); + rowCount++; + data[i][1] = getClusterNumber(c); + // StringBuilder details = getRankScore(c); + // data[i][2] = new StringBuffer(details); + data[i][2] = c.getRankScore(); - SpringEmbeddedLayouter layouter = new SpringEmbeddedLayouter(); - //System.out.println("CBTM: after invoking SpringEmbeddedLayouter"); + // System.out.println("CBTM: after invoking SpringEmbeddedLayouter"); // get an image for each cluster - make it a nice layout of the cluster - final Image image = createClusterImage(c, graphPicSize, graphPicSize, layouter, false); - //System.out.println("CBTM: after createClusterImage"); + final Image image = ViewUtils.createClusterImage(clusterManager, vs, network, c, graphPicSize, graphPicSize, layouter, false); + // System.out.println("CBTM: after createClusterImage"); data[i][0] = image != null ? new ClusterImageIcon(image, c) : new ClusterImageIcon(); } } @@ -332,7 +405,7 @@ public int getColumnCount() { } public int getRowCount() { - return data.length; + return rowCount; } public Object getValueAt(int row, int col) { @@ -351,262 +424,34 @@ public Class getColumnClass(int c) { } } - /** - * Convert a network to an image. This is used by the MCODEResultsPanel. - * - * @param cluster Input network to convert to an image - * @param height Height that the resulting image should be - * @param width Width that the resulting image should be - * @param layouter Reference to the layout algorithm - * @param layoutNecessary Determinant of cluster size growth or shrinkage, the former requires layout - * @return The resulting image + * Header renderer */ - public Image createClusterImage(final NodeCluster cluster, - final int height, - final int width, - SpringEmbeddedLayouter layouter, - boolean layoutNecessary) { - //System.out.println("CCI: inside method"); - final CyRootNetwork root = clusterManager.getService(CyRootNetworkManager.class).getRootNetwork(network); - //need to create a method get the subnetwork for a cluster - final CyNetwork net = cluster.getSubNetwork(network, root, SavePolicy.DO_NOT_SAVE); - - //System.out.println("CCI: after getting root and network "); - // Progress reporters. - // There are three basic tasks, the progress of each is calculated and then combined - // using the respective weighting to get an overall progress global progress - int weightSetupNodes = 20; // setting up the nodes and edges is deemed as 25% of the whole task - int weightSetupEdges = 5; - double weightLayout = 75.0; // layout it is 70% - double goalTotal = weightSetupNodes + weightSetupEdges; - - if (layoutNecessary) { - goalTotal += weightLayout; - } - - // keeps track of progress as a percent of the totalGoal - double progress = 0; - - final VisualStyle vs = getClusterStyle(); - //System.out.println("CCI: after getClusterStyle"); - final CyNetworkView clusterView = createNetworkView(net, vs); - //System.out.println("CCI: after createNetworkView"); - - clusterView.setVisualProperty(NETWORK_WIDTH, new Double(width)); - clusterView.setVisualProperty(NETWORK_HEIGHT, new Double(height)); - - for (View nv : clusterView.getNodeViews()) { - if (interrupted) { - //logger.debug("Interrupted: Node Setup"); - // before we short-circuit the method we reset the interruption so that the method can run without - // problems the next time around - if (layouter != null) layouter.resetDoLayout(); - resetLoading(); - - return null; - } - - // Node position - final double x; - final double y; - - // First we check if the MCODECluster already has a node view of this node (posing the more generic condition - // first prevents the program from throwing a null pointer exception in the second condition) - if (cluster.getView() != null && cluster.getView().getNodeView(nv.getModel()) != null) { - //If it does, then we take the layout position that was already generated for it - x = cluster.getView().getNodeView(nv.getModel()).getVisualProperty(NODE_X_LOCATION); - y = cluster.getView().getNodeView(nv.getModel()).getVisualProperty(NODE_Y_LOCATION); - } else { - // Otherwise, randomize node positions before layout so that they don't all layout in a line - // (so they don't fall into a local minimum for the SpringEmbedder) - // If the SpringEmbedder implementation changes, this code may need to be removed - // size is small for many default drawn graphs, thus +100 - x = (clusterView.getVisualProperty(NETWORK_WIDTH) + 100) * Math.random(); - y = (clusterView.getVisualProperty(NETWORK_HEIGHT) + 100) * Math.random(); - - if (!layoutNecessary) { - goalTotal += weightLayout; - progress /= (goalTotal / (goalTotal - weightLayout)); - layoutNecessary = true; - } - } - - nv.setVisualProperty(NODE_X_LOCATION, x); - nv.setVisualProperty(NODE_Y_LOCATION, y); - - //Might be specific to MCODE - /* - // Node shape - if (cluster.getSeedNode() == nv.getModel().getSUID()) { - nv.setLockedValue(NODE_SHAPE, NodeShapeVisualProperty.RECTANGLE); - } else { - nv.setLockedValue(NODE_SHAPE, NodeShapeVisualProperty.ELLIPSE); - } - */ - - /* - // Update loader - if (loader != null) { - progress += 100.0 * (1.0 / (double) clusterView.getNodeViews().size()) * - ((double) weightSetupNodes / (double) goalTotal); - loader.setProgress((int) progress, "Setup: nodes"); - } - - */ - } - - if (clusterView.getEdgeViews() != null) { - for (int i = 0; i < clusterView.getEdgeViews().size(); i++) { - if (interrupted) { - //logger.error("Interrupted: Edge Setup"); - if (layouter != null) layouter.resetDoLayout(); - resetLoading(); - - return null; - } - /* - if (loader != null) { - progress += 100.0 * (1.0 / (double) clusterView.getEdgeViews().size()) * - ((double) weightSetupEdges / (double) goalTotal); - loader.setProgress((int) progress, "Setup: edges"); - } - */ - } - } - - if (layoutNecessary) { - if (layouter == null) { - layouter = new SpringEmbeddedLayouter(); - } - - layouter.setGraphView(clusterView); - - // The doLayout method should return true if the process completes without interruption - if (!layouter.doLayout(weightLayout, goalTotal, progress)) { - // Otherwise, if layout is not completed, set the interruption to false, and return null, not an image - resetLoading(); - - return null; - } - } - - final Image image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); - final Graphics2D g = (Graphics2D) image.getGraphics(); - - SwingUtilities.invokeLater(new Runnable() { - //@Override - public void run() { - try { - final Dimension size = new Dimension(width, height); - - JPanel panel = new JPanel(); - panel.setPreferredSize(size); - panel.setSize(size); - panel.setMinimumSize(size); - panel.setMaximumSize(size); - panel.setBackground((Color) vs.getDefaultValue(NETWORK_BACKGROUND_PAINT)); - - JWindow window = new JWindow(); - window.getContentPane().add(panel, BorderLayout.CENTER); - - RenderingEngine re = renderingEngineFactory.createRenderingEngine(panel, clusterView); - - vs.apply(clusterView); - clusterView.fitContent(); - clusterView.updateView(); - window.pack(); - window.repaint(); - - re.createImage(width, height); - re.printCanvas(g); - g.dispose(); - - if (clusterView.getNodeViews().size() > 0) { - cluster.setView(clusterView); - } - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - }); - - layouter.resetDoLayout(); - resetLoading(); - - return image; - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public VisualStyle getClusterStyle() { - if (clusterStyle == null) { - clusterStyle = visualStyleFactory.createVisualStyle("Cluster"); - - clusterStyle.setDefaultValue(NODE_SIZE, 40.0); - clusterStyle.setDefaultValue(NODE_WIDTH, 40.0); - clusterStyle.setDefaultValue(NODE_HEIGHT, 40.0); - clusterStyle.setDefaultValue(NODE_PAINT, Color.RED); - clusterStyle.setDefaultValue(NODE_FILL_COLOR, Color.RED); - clusterStyle.setDefaultValue(NODE_BORDER_WIDTH, 0.0); - - clusterStyle.setDefaultValue(EDGE_WIDTH, 5.0); - clusterStyle.setDefaultValue(EDGE_PAINT, Color.BLUE); - clusterStyle.setDefaultValue(EDGE_UNSELECTED_PAINT, Color.BLUE); - clusterStyle.setDefaultValue(EDGE_STROKE_UNSELECTED_PAINT, Color.BLUE); - clusterStyle.setDefaultValue(EDGE_SELECTED_PAINT, Color.BLUE); - clusterStyle.setDefaultValue(EDGE_STROKE_SELECTED_PAINT, Color.BLUE); - clusterStyle.setDefaultValue(EDGE_TARGET_ARROW_SHAPE, NONE); - clusterStyle.setDefaultValue(EDGE_SOURCE_ARROW_SHAPE, NONE); - - /* - //System.out.println("GCS: before getVisual Lexicon"); - VisualLexicon lexicon = applicationMgr.getCurrentRenderingEngine().getVisualLexicon(); - VisualProperty vp = lexicon.lookup(CyEdge.class, "edgeTargetArrowShape"); - //System.out.println("CCI: after setting visual property"); - - if (vp != null) { - Object arrowValue = vp.parseSerializableString("ARROW"); - System.out.println("Edge target arrow value = "+arrowValue.toString()); - if (arrowValue != null) clusterStyle.setDefaultValue(vp, arrowValue); - } - */ - } - - return clusterStyle; - } - - public CyNetworkView createNetworkView(final CyNetwork net, VisualStyle vs) { - final CyNetworkView view = networkViewFactory.createNetworkView(net); - //System.out.println("inside createNetworkView"); - if (vs == null) vs = visualMappingMgr.getDefaultVisualStyle(); - visualMappingMgr.setVisualStyle(vs, view); - vs.apply(view); - view.updateView(); - - return view; + public static class HeaderRenderer extends DefaultTableCellRenderer { + public HeaderRenderer() { + super(); + setHorizontalAlignment(JLabel.CENTER); + setFont(new Font("sans-serif", Font.BOLD, 10)); + } + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) { + Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col); + c.setFont(new Font("sans-serif", Font.BOLD, 10)); + return c; + } } /** - * A text area renderer that creates a line wrapped, non-editable text area + * An image renderer that creates a reasonable row height */ - public static class JTextAreaRenderer extends JTextArea implements TableCellRenderer { + public static class ImageRenderer extends JLabel implements TableCellRenderer { int minHeight; - /** - * Constructor - * - * @param minHeight - * The minimum height of the row, either the size of the - * graph picture or zero - */ - public JTextAreaRenderer(int minHeight) { - //System.out.println("JTAR: inside constructor "); - this.setLineWrap(true); - this.setWrapStyleWord(true); - this.setEditable(false); - this.setFont(new Font(this.getFont().getFontName(), Font.PLAIN, 11)); - this.minHeight = minHeight; + public ImageRenderer(int minHeight) { + super(); + this.minHeight = minHeight; } /** @@ -629,9 +474,7 @@ public Component getTableCellRendererComponent(JTable table, boolean hasFocus, int row, int column) { - //System.out.println("JTAR: inside getTableCellRendererComponent"); - StringBuffer sb = (StringBuffer) value; - this.setText(sb.toString()); + this.setIcon((Icon)value); if (isSelected) { this.setBackground(table.getSelectionBackground()); @@ -642,15 +485,17 @@ public Component getTableCellRendererComponent(JTable table, } // Row height calculations + this.setHorizontalAlignment(JLabel.CENTER); + int iconPreferredHeight = (int) this.getPreferredSize().getHeight(); + int iconPreferredWidth = (int) this.getPreferredSize().getWidth(); int currentRowHeight = table.getRowHeight(row); int rowMargin = table.getRowMargin(); - this.setSize(table.getColumnModel().getColumn(column).getWidth(), currentRowHeight - (2 * rowMargin)); - int textAreaPreferredHeight = (int) this.getPreferredSize().getHeight(); + this.setSize(iconPreferredWidth+10, currentRowHeight - (2 * rowMargin)); // JTextArea can grow and shrink here - if (currentRowHeight != Math.max(textAreaPreferredHeight + (2 * rowMargin), minHeight + (2 * rowMargin))) { + if (currentRowHeight != Math.max(iconPreferredHeight + (2 * rowMargin), minHeight + (2 * rowMargin))) { table.setRowHeight(row, Math - .max(textAreaPreferredHeight + (2 * rowMargin), minHeight + (2 * rowMargin))); + .max(iconPreferredHeight + (2 * rowMargin), minHeight + (2 * rowMargin))); } return this; diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/RankingPanelTask.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/RankingPanelTask.java index 195654b..af53305 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/RankingPanelTask.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/RankingPanelTask.java @@ -86,11 +86,11 @@ public void run(TaskMonitor taskMonitor) { taskMonitor.setStatusMessage("Calculating Ranking Results..."); taskMonitor.setProgress(0.0); - clusters = ClusterUtils.fetchRankingResults(network); + clusters = ClusterUtils.fetchRankingResults(network, true); rankingPanel = new RankingPanel(clusters, network, networkView, manager, taskMonitor); addAndRegisterPanel(cytoPanel); - setNodeColors(); + // setNodeColors(); taskMonitor.setProgress(1.0); } else { taskMonitor.setTitle("Deleting all ranking panels"); diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/ResultsPanel.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/ResultsPanel.java index 7c85c10..e7e5912 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/ResultsPanel.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/ResultsPanel.java @@ -1,25 +1,6 @@ package edu.ucsf.rbvi.clusterMaker2.internal.ui; import static org.cytoscape.view.presentation.property.ArrowShapeVisualProperty.NONE; -import static org.cytoscape.view.presentation.property.BasicVisualLexicon.EDGE_PAINT; -import static org.cytoscape.view.presentation.property.BasicVisualLexicon.EDGE_SELECTED_PAINT; -import static org.cytoscape.view.presentation.property.BasicVisualLexicon.EDGE_SOURCE_ARROW_SHAPE; -import static org.cytoscape.view.presentation.property.BasicVisualLexicon.EDGE_STROKE_SELECTED_PAINT; -import static org.cytoscape.view.presentation.property.BasicVisualLexicon.EDGE_STROKE_UNSELECTED_PAINT; -import static org.cytoscape.view.presentation.property.BasicVisualLexicon.EDGE_TARGET_ARROW_SHAPE; -import static org.cytoscape.view.presentation.property.BasicVisualLexicon.EDGE_UNSELECTED_PAINT; -import static org.cytoscape.view.presentation.property.BasicVisualLexicon.EDGE_WIDTH; -import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NETWORK_BACKGROUND_PAINT; -import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NETWORK_HEIGHT; -import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NETWORK_WIDTH; -import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NODE_BORDER_WIDTH; -import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NODE_FILL_COLOR; -import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NODE_HEIGHT; -import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NODE_PAINT; -import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NODE_SIZE; -import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NODE_WIDTH; -import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NODE_X_LOCATION; -import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NODE_Y_LOCATION; import java.awt.BorderLayout; import java.awt.Color; @@ -79,6 +60,8 @@ import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.NodeCluster; import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; import edu.ucsf.rbvi.clusterMaker2.internal.utils.ModelUtils; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.SpringEmbeddedLayouter; +import edu.ucsf.rbvi.clusterMaker2.internal.utils.ViewUtils; public class ResultsPanel extends JPanel implements CytoPanelComponent{ @@ -330,6 +313,8 @@ public RankingBrowserPanelModel() { data = new Object[clusters.size()][columnNames.length]; //System.out.println("CBTM: after initialising exploreContent and data"); + VisualStyle vs = ViewUtils.getClusterStyle(clusterManager, null); + for (int i = 0; i < clusters.size(); i++) { //System.out.println("CBTM: cluster num: "+ i); final NodeCluster c = clusters.get(i); @@ -340,7 +325,7 @@ public RankingBrowserPanelModel() { SpringEmbeddedLayouter layouter = new SpringEmbeddedLayouter(); //System.out.println("CBTM: after invoking SpringEmbeddedLayouter"); // get an image for each cluster - make it a nice layout of the cluster - final Image image = createClusterImage(c, graphPicSize, graphPicSize, layouter, false); + final Image image = ViewUtils.createClusterImage(clusterManager, vs, network, c, graphPicSize, graphPicSize, layouter, false); //System.out.println("CBTM: after createClusterImage"); data[i][0] = image != null ? new ClusterImageIcon(image, c) : new ClusterImageIcon(); } @@ -375,240 +360,6 @@ public Class getColumnClass(int c) { } } - - /** - * Convert a network to an image. This is used by the MCODEResultsPanel. - * - * @param cluster Input network to convert to an image - * @param height Height that the resulting image should be - * @param width Width that the resulting image should be - * @param layouter Reference to the layout algorithm - * @param layoutNecessary Determinant of cluster size growth or shrinkage, the former requires layout - * @return The resulting image - */ - public Image createClusterImage(final NodeCluster cluster, - final int height, - final int width, - SpringEmbeddedLayouter layouter, - boolean layoutNecessary) { - //System.out.println("CCI: inside method"); - final CyRootNetwork root = clusterManager.getService(CyRootNetworkManager.class).getRootNetwork(network); - //need to create a method get the subnetwork for a cluster - final CyNetwork net = cluster.getSubNetwork(network, root, SavePolicy.DO_NOT_SAVE); - - //System.out.println("CCI: after getting root and network "); - // Progress reporters. - // There are three basic tasks, the progress of each is calculated and then combined - // using the respective weighting to get an overall progress global progress - int weightSetupNodes = 20; // setting up the nodes and edges is deemed as 25% of the whole task - int weightSetupEdges = 5; - double weightLayout = 75.0; // layout it is 70% - double goalTotal = weightSetupNodes + weightSetupEdges; - - if (layoutNecessary) { - goalTotal += weightLayout; - } - - // keeps track of progress as a percent of the totalGoal - double progress = 0; - - final VisualStyle vs = getClusterStyle(); - //System.out.println("CCI: after getClusterStyle"); - final CyNetworkView clusterView = createNetworkView(net, vs); - //System.out.println("CCI: after createNetworkView"); - - clusterView.setVisualProperty(NETWORK_WIDTH, new Double(width)); - clusterView.setVisualProperty(NETWORK_HEIGHT, new Double(height)); - - for (View nv : clusterView.getNodeViews()) { - if (interrupted) { - //logger.debug("Interrupted: Node Setup"); - // before we short-circuit the method we reset the interruption so that the method can run without - // problems the next time around - if (layouter != null) layouter.resetDoLayout(); - resetLoading(); - - return null; - } - - // Node position - final double x; - final double y; - - // First we check if the MCODECluster already has a node view of this node (posing the more generic condition - // first prevents the program from throwing a null pointer exception in the second condition) - if (cluster.getView() != null && cluster.getView().getNodeView(nv.getModel()) != null) { - //If it does, then we take the layout position that was already generated for it - x = cluster.getView().getNodeView(nv.getModel()).getVisualProperty(NODE_X_LOCATION); - y = cluster.getView().getNodeView(nv.getModel()).getVisualProperty(NODE_Y_LOCATION); - } else { - // Otherwise, randomize node positions before layout so that they don't all layout in a line - // (so they don't fall into a local minimum for the SpringEmbedder) - // If the SpringEmbedder implementation changes, this code may need to be removed - // size is small for many default drawn graphs, thus +100 - x = (clusterView.getVisualProperty(NETWORK_WIDTH) + 100) * Math.random(); - y = (clusterView.getVisualProperty(NETWORK_HEIGHT) + 100) * Math.random(); - - if (!layoutNecessary) { - goalTotal += weightLayout; - progress /= (goalTotal / (goalTotal - weightLayout)); - layoutNecessary = true; - } - } - - nv.setVisualProperty(NODE_X_LOCATION, x); - nv.setVisualProperty(NODE_Y_LOCATION, y); - - //Might be specific to MCODE - /* - // Node shape - if (cluster.getSeedNode() == nv.getModel().getSUID()) { - nv.setLockedValue(NODE_SHAPE, NodeShapeVisualProperty.RECTANGLE); - } else { - nv.setLockedValue(NODE_SHAPE, NodeShapeVisualProperty.ELLIPSE); - } - */ - - /* - // Update loader - if (loader != null) { - progress += 100.0 * (1.0 / (double) clusterView.getNodeViews().size()) * - ((double) weightSetupNodes / (double) goalTotal); - loader.setProgress((int) progress, "Setup: nodes"); - } - - */ - } - - if (clusterView.getEdgeViews() != null) { - for (int i = 0; i < clusterView.getEdgeViews().size(); i++) { - if (interrupted) { - //logger.error("Interrupted: Edge Setup"); - if (layouter != null) layouter.resetDoLayout(); - resetLoading(); - - return null; - } - /* - if (loader != null) { - progress += 100.0 * (1.0 / (double) clusterView.getEdgeViews().size()) * - ((double) weightSetupEdges / (double) goalTotal); - loader.setProgress((int) progress, "Setup: edges"); - } - */ - } - } - - if (layoutNecessary) { - if (layouter == null) { - layouter = new SpringEmbeddedLayouter(); - } - - layouter.setGraphView(clusterView); - - // The doLayout method should return true if the process completes without interruption - if (!layouter.doLayout(weightLayout, goalTotal, progress)) { - // Otherwise, if layout is not completed, set the interruption to false, and return null, not an image - resetLoading(); - - return null; - } - } - - final Image image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); - - SwingUtilities.invokeLater(new Runnable() { - //@Override - public void run() { - try { - final Dimension size = new Dimension(width, height); - - JPanel panel = new JPanel(); - panel.setPreferredSize(size); - panel.setSize(size); - panel.setMinimumSize(size); - panel.setMaximumSize(size); - panel.setBackground((Color) vs.getDefaultValue(NETWORK_BACKGROUND_PAINT)); - - JWindow window = new JWindow(); - window.getContentPane().add(panel, BorderLayout.CENTER); - - RenderingEngine re = renderingEngineFactory.createRenderingEngine(panel, clusterView); - - vs.apply(clusterView); - clusterView.fitContent(); - clusterView.updateView(); - window.pack(); - window.repaint(); - - final Image tmpImage = re.createImage(width, height); - final Graphics2D g = (Graphics2D) image.getGraphics(); - g.drawImage(tmpImage, 0, 0, null); - - if (clusterView.getNodeViews().size() > 0) { - cluster.setView(clusterView); - } - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - }); - - layouter.resetDoLayout(); - resetLoading(); - - return image; - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public VisualStyle getClusterStyle() { - if (clusterStyle == null) { - clusterStyle = visualStyleFactory.createVisualStyle("Cluster"); - - clusterStyle.setDefaultValue(NODE_SIZE, 40.0); - clusterStyle.setDefaultValue(NODE_WIDTH, 40.0); - clusterStyle.setDefaultValue(NODE_HEIGHT, 40.0); - clusterStyle.setDefaultValue(NODE_PAINT, Color.RED); - clusterStyle.setDefaultValue(NODE_FILL_COLOR, Color.RED); - clusterStyle.setDefaultValue(NODE_BORDER_WIDTH, 0.0); - - clusterStyle.setDefaultValue(EDGE_WIDTH, 5.0); - clusterStyle.setDefaultValue(EDGE_PAINT, Color.BLUE); - clusterStyle.setDefaultValue(EDGE_UNSELECTED_PAINT, Color.BLUE); - clusterStyle.setDefaultValue(EDGE_STROKE_UNSELECTED_PAINT, Color.BLUE); - clusterStyle.setDefaultValue(EDGE_SELECTED_PAINT, Color.BLUE); - clusterStyle.setDefaultValue(EDGE_STROKE_SELECTED_PAINT, Color.BLUE); - clusterStyle.setDefaultValue(EDGE_TARGET_ARROW_SHAPE, NONE); - clusterStyle.setDefaultValue(EDGE_SOURCE_ARROW_SHAPE, NONE); - - /* - //System.out.println("GCS: before getVisual Lexicon"); - VisualLexicon lexicon = applicationMgr.getCurrentRenderingEngine().getVisualLexicon(); - VisualProperty vp = lexicon.lookup(CyEdge.class, "edgeTargetArrowShape"); - //System.out.println("CCI: after setting visual property"); - - if (vp != null) { - Object arrowValue = vp.parseSerializableString("ARROW"); - System.out.println("Edge target arrow value = "+arrowValue.toString()); - if (arrowValue != null) clusterStyle.setDefaultValue(vp, arrowValue); - } - */ - } - - return clusterStyle; - } - - public CyNetworkView createNetworkView(final CyNetwork net, VisualStyle vs) { - final CyNetworkView view = networkViewFactory.createNetworkView(net); - //System.out.println("inside createNetworkView"); - if (vs == null) vs = visualMappingMgr.getDefaultVisualStyle(); - visualMappingMgr.setVisualStyle(vs, view); - vs.apply(view); - view.updateView(); - - return view; - } - /** * A text area renderer that creates a line wrapped, non-editable text area */ diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/ScatterPlot.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/ScatterPlot.java index e155164..9767e4c 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/ScatterPlot.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/ScatterPlot.java @@ -14,6 +14,7 @@ import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Point; +import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.event.MouseAdapter; @@ -25,15 +26,35 @@ import java.awt.geom.Ellipse2D; import java.awt.geom.Path2D; import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStream; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Properties; +import javax.imageio.ImageIO; import javax.swing.*; +import com.itextpdf.awt.DefaultFontMapper; +import com.itextpdf.text.Document; +import com.itextpdf.text.PageSize; +import com.itextpdf.text.pdf.PdfContentByte; +import com.itextpdf.text.pdf.PdfWriter; + +import org.apache.log4j.Logger; +import org.freehep.graphicsio.ps.PSGraphics2D; +import org.freehep.graphicsio.svg.SVGGraphics2D; +import org.freehep.graphics2d.VectorGraphics; + +import org.cytoscape.application.CyUserLog; import org.cytoscape.model.CyNetwork; import org.cytoscape.model.CyNode; + import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; import edu.ucsf.rbvi.clusterMaker2.internal.api.CyMatrix; import edu.ucsf.rbvi.clusterMaker2.internal.api.Matrix; @@ -46,6 +67,8 @@ */ @SuppressWarnings("serial") public class ScatterPlot extends JPanel implements MouseListener, MouseMotionListener{ + static final Logger logger = Logger.getLogger(CyUserLog.NAME); + private float scale = 1; private int MAX_SCORE = 1; private int MIN_SCORE = -1; @@ -56,14 +79,14 @@ public class ScatterPlot extends JPanel implements MouseListener, MouseMotionLis private static final int XSTART = BORDER_GAP+LABEL_GAP; private static final int YSTART = BORDER_GAP; private static final int GRAPH_HATCH_WIDTH = 2; - private int graph_point_width = 2; + private int graph_point_width = 1; private final Matrix loadings; private final CyMatrix[] scores; private final int xIndex; private final int yIndex; - private final Color pointColor; - private final int pointWidth; + private Color pointColor; + private int pointWidth; private final ClusterManager manager; private List graphPoints; @@ -139,6 +162,21 @@ public void mouseWheelMoved(MouseWheelEvent e) { } + public void setPointSize(int size) { + pointWidth = size; + repaint(); + } + + public void setPointColor(Color color) { + pointColor = color; + repaint(); + } + + public void setColorMap(Map map) { + colorMap = map; + repaint(); + } + @Override public Dimension getPreferredSize() { return new Dimension(PREF_W, PREF_H); } @@ -297,6 +335,10 @@ private void selectRange(int x1, int y1, int x2, int y2) { @Override protected void paintComponent(Graphics g) { super.paintComponent(g); + drawAll(g); + } + + private void drawAll(Graphics g) { String labelX = loadings.getColumnLabel(xIndex); String labelY = loadings.getColumnLabel(yIndex); @@ -305,16 +347,21 @@ protected void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D)g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - AffineTransform at = new AffineTransform(); + // AffineTransform at = new AffineTransform(); if(dragging && !shift){ int currentDX = currentX - startingX; int currentDY = currentY - startingY; - at.setToTranslation(previousDX + currentDX, previousDY + currentDY); + // at.setToTranslation(previousDX + currentDX, previousDY + currentDY); + g2.translate(previousDX + currentDX, previousDY + currentDY); + // System.out.println("Dragging"); } else { - at.setToTranslation(previousDX, previousDY); + // at.setToTranslation(previousDX, previousDY); + g2.translate(previousDX, previousDY); + // System.out.println("Not dragging: dx = "+previousDX+" dy = "+previousDY); } - at.scale(scale, scale); - g2.setTransform(at); + g2.scale(scale, scale); + // at.scale(scale, scale); + // g2.setTransform(at); drawAxes(g2, plotWidth, plotHeight, labelX, labelY); @@ -365,10 +412,13 @@ protected void paintComponent(Graphics g) { int x2 = (int) (loadings.getValue(row, xIndex) * xScale * MAX_SCORE + newX); int y2 = (int) (-1 * (loadings.getValue(row, yIndex) * yScale * MAX_SCORE - newY)); String label = loadings.getRowLabel(row); - if (colorMap.containsKey(label)) - drawArrow(g2, x1, y1, x2, y2, colorMap.get(label)); - else + if (colorMap.containsKey(label)) { + if (colorMap.get(label).getAlpha() != 0) { + drawArrow(g2, x1, y1, x2, y2, colorMap.get(label)); + } + } else { drawArrow(g2, x1, y1, x2, y2, Color.RED); + } } } @@ -542,6 +592,22 @@ public void drawArrow(Graphics2D g2, int x1, int y1, int x2, int y2, Color color g2.setTransform(oldTx); } + public void print(String format, File file) { + if (format.startsWith(".")) { + format = format.substring(1); + } + int saveWidth = pointWidth; + // pointWidth = 1; + if (format.equals("png") || format.equals("jpg") || format.equals("bmp")) + bitmapSave(format, file); + else if (format.equals("pdf")) + pdfSave(format, file); + else if (format.equals("svg")) + svgSave(format, file); + + pointWidth = saveWidth; + } + private Color getColor(CyNetwork network, CyNode node) { return ViewUtils.getColor(manager, network, node); } @@ -549,5 +615,109 @@ private Color getColor(CyNetwork network, CyNode node) { private String getLabel(CyNetwork network, CyNode node) { return ViewUtils.getLabel(manager, network, node); } + + private void pdfSave(String format, File file) { + com.itextpdf.text.Rectangle pageSize = new com.itextpdf.text.Rectangle(getWidth(), getHeight()); + logger.info("Writing PDF document to "+file.getAbsolutePath()); + System.out.println("PageSize = "+pageSize.toString()); + System.out.println("Width X Height = "+getWidth()+" X "+getHeight()); + Document document = new Document(pageSize); + float saveScale = scale; + try { + OutputStream output = new BufferedOutputStream(new FileOutputStream(file)); + PdfWriter writer = PdfWriter.getInstance(document, output); + document.open(); + PdfContentByte cb = writer.getDirectContent(); + + Graphics2D g = cb.createGraphics(pageSize.getWidth(), pageSize.getHeight(), new DefaultFontMapper()); + drawAll(g); + g.dispose(); + document.close(); + } + catch (Exception e) + { + e.printStackTrace(); + JOptionPane.showMessageDialog(this, + new JTextArea("Scatterplot export had problem " + e )); + // logger.error("Exception " + e); + } + scale = saveScale; + + document.close(); + } + + private void bitmapSave(String format, File file) { + logger.info("Writing "+format+" document to "+file.getAbsolutePath()); + float saveScale = scale; + try { + OutputStream output = new BufferedOutputStream(new FileOutputStream(file)); + + int extraWidth = BORDER_GAP*2; + int extraHeight = BORDER_GAP*2; + Rectangle destRect = new Rectangle(0,0, getWidth()*5, getHeight()*5); + + // scale = scale * 5; + + BufferedImage i; + if (format.equals("png")) + i = new BufferedImage(destRect.width + extraWidth, destRect.height + extraHeight, BufferedImage.TYPE_INT_ARGB); + else + i = new BufferedImage(destRect.width + extraWidth, destRect.height + extraHeight, BufferedImage.TYPE_INT_RGB); + Graphics g = i.getGraphics(); + + // For PNG, we want to allow a transparent background, so don't do the fill + if (!format.equals("png")) { + g.setColor(Color.white); + g.fillRect(0,0,destRect.width+1 + extraWidth, destRect.height+1+extraHeight); + } + g.setColor(Color.black); + g.translate(extraHeight/2, extraWidth/2); + + // Our X and Y values are currently in the wrong scale -- we need to fix that + ((Graphics2D)g).scale(5,5); + drawAll(g); + + ImageIO.write(i,format,output); + // ignore success, could keep window open on failure if save could indicate success. + output.close(); + } catch (Exception e) { + e.printStackTrace(); + JOptionPane.showMessageDialog(this, + new JTextArea("Scatterplot export had problem " + e )); + // logger.error("Exception " + e); + } + scale = saveScale; + } + + private void svgSave (String format, File file) { + logger.info("Writing "+format+" document to "+file.getAbsolutePath()); + com.itextpdf.text.Rectangle pageSize = PageSize.LETTER; + Properties p = new Properties(); + p.setProperty(PSGraphics2D.PAGE_SIZE,"Letter"); + p.setProperty("org.freehep.graphicsio.AbstractVectorGraphicsIO.TEXT_AS_SHAPES", + Boolean.toString(false)); + + try { + OutputStream output = new BufferedOutputStream(new FileOutputStream(file)); + SVGGraphics2D g = new SVGGraphics2D(output, getPreferredSize()); + + // Width and height aren't the same, so we need to fix this up + double imageScale = Math.min(pageSize.getWidth() / ((double) getWidth()+BORDER_GAP*2), + pageSize.getHeight() / ((double) getHeight()+BORDER_GAP*2)); + g.setProperties(p); + g.startExport(); + g.scale(imageScale, imageScale); + drawAll(g); + g.endExport(); + output.close(); + } + catch (Exception e) + { + JOptionPane.showMessageDialog(this, + new JTextArea("Scatterplot export had problem " + e )); + // logger.error("Exception " + e); + // e.printStackTrace(); + } + } } diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/ScatterPlotDialog.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/ScatterPlotDialog.java index 562c01d..dea808f 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/ScatterPlotDialog.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/ScatterPlotDialog.java @@ -7,6 +7,7 @@ import java.awt.BasicStroke; import java.awt.Color; +import java.awt.Component; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; @@ -27,20 +28,29 @@ import java.awt.event.MouseWheelEvent; import java.awt.geom.AffineTransform; import java.awt.geom.Path2D; +import java.io.File; import java.text.DecimalFormat; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.swing.*; +import javax.swing.border.EtchedBorder; +import javax.swing.filechooser.FileFilter; import org.jdesktop.swingx.JXCollapsiblePane; +import org.cytoscape.application.CyApplicationManager; import org.cytoscape.model.CyNetwork; import org.cytoscape.model.CyNode; +import org.cytoscape.util.swing.FileChooserFilter; +import org.cytoscape.util.swing.FileUtil; import org.cytoscape.view.model.CyNetworkView; import org.cytoscape.work.TaskMonitor; import org.cytoscape.work.undo.UndoSupport; +import org.cytoscape.util.swing.CyColorChooser; + import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; import edu.ucsf.rbvi.clusterMaker2.internal.api.CyMatrix; import edu.ucsf.rbvi.clusterMaker2.internal.api.Matrix; @@ -65,6 +75,7 @@ public class ScatterPlotDialog extends JDialog { private String title; private Color pointColor = Color.BLUE; + private JDialog exportDialog; private JPanel container; private JPanel panelXAxis; private JPanel panelYAxis; @@ -80,6 +91,7 @@ public class ScatterPlotDialog extends JDialog { private JComboBox comboXAxis; private JComboBox comboYAxis; private JButton buttonColor; + private JButton buttonExport; private JButton buttonLayout; private JButton buttonOptions; private JButton buttonPlot; @@ -87,41 +99,50 @@ public class ScatterPlotDialog extends JDialog { private Map loadingsColorMap; private boolean supportsLayout; + private ScatterPlot scatterPlot = null; + // For inner classes private final ScatterPlotDialog thisDialog; + // Get a handle on the CyColorChooser + private CyColorChooser chooser = null; + private boolean useLoadings; + private FileUtil fileUtil; + private Collection fileFilters; + + // Entry point for remote DR techniques (e.g. UMAP) + public ScatterPlotDialog(ClusterManager manager, String title, TaskMonitor monitor, CyNode[] nodes, double[][] coordinates) { + super(); + // Make the matrix + CyMatrix matrix = CyMatrixFactory.makeSmallMatrix(manager.getNetwork(), coordinates.length, coordinates[0].length, coordinates); + matrix.setRowNodes(nodes); + this.manager = manager; + this.scores = new CyMatrix[1]; + this.title = title; + this.variances = null; + this.loadings = CyMatrixFactory.makeSmallMatrix(manager.getNetwork(), 1, 2); + thisDialog = this; + fileUtil = manager.getService(FileUtil.class); + fileFilters = getFileChooserFilters(); + // createExportDialog(); + init(manager, title, monitor, matrix); + } + // Entry point for tSNE and related public ScatterPlotDialog(ClusterManager manager, String title, TaskMonitor monitor, CyMatrix coordinates) { super(); - this.title = title; - setTitle(title+" Scatter Plot"); - monitor.setTitle(title+" Scatter Plot"); - useLoadings = false; - supportsLayout = true; this.manager = manager; this.scores = new CyMatrix[1]; - this.scores[0] = coordinates; - + this.title = title; this.variances = null; - this.loadings = CyMatrixFactory.makeSmallMatrix(scores[0].getNetwork(), 1, 2); - loadings.setColumnLabel(0, "X Axis"); - loadings.setColumnLabel(1, "Y Axis"); + this.loadings = CyMatrixFactory.makeSmallMatrix(manager.getNetwork(), 1, 2); thisDialog = this; - - if (coordinates.nColumns() != 2) { - monitor.showMessage(TaskMonitor.Level.ERROR, "Coordinate scatterplot must have 2 columns!"); - return; - } - - container = new JPanel(); - createUI(); - getContentPane().add(container); - - pack(); - setLocationByPlatform(true); - setVisible(true); + fileUtil = manager.getService(FileUtil.class); + fileFilters = getFileChooserFilters(); + // createExportDialog(); + init(manager, title, monitor, coordinates); } // Entry point for PCoA and related @@ -149,6 +170,11 @@ public ScatterPlotDialog(ClusterManager manager, String title, TaskMonitor monit return; } + CyColorChooser chooser = manager.getService(CyColorChooser.class); + fileUtil = manager.getService(FileUtil.class); + fileFilters = getFileChooserFilters(); + // createExportDialog(); + container = new JPanel(); createUI(); getContentPane().add(container); @@ -174,6 +200,9 @@ public ScatterPlotDialog(ClusterManager manager, String title, TaskMonitor monit supportsLayout = false; initializeColors(); + fileUtil = manager.getService(FileUtil.class); + fileFilters = getFileChooserFilters(); + this.variances = varianceArray; thisDialog = this; @@ -184,6 +213,8 @@ public ScatterPlotDialog(ClusterManager manager, String title, TaskMonitor monit return; } + // createExportDialog(); + container = new JPanel(); createUI(); getContentPane().add(container); @@ -193,6 +224,36 @@ public ScatterPlotDialog(ClusterManager manager, String title, TaskMonitor monit setVisible(true); } + private void init(ClusterManager manager, String title, TaskMonitor monitor, CyMatrix matrix) { + this.title = title; + setTitle(title+" Scatter Plot"); + if (monitor != null) + monitor.setTitle(title+" Scatter Plot"); + useLoadings = false; + supportsLayout = true; + this.scores[0] = matrix; + + loadings.setColumnLabel(0, "X Axis"); + loadings.setColumnLabel(1, "Y Axis"); + + if (matrix.nColumns() != 2) { + if (monitor != null) + monitor.showMessage(TaskMonitor.Level.ERROR, "Coordinate scatterplot must have 2 columns!"); + else { + // User log? + } + return; + } + + container = new JPanel(); + createUI(); + getContentPane().add(container); + + pack(); + setLocationByPlatform(true); + setVisible(true); + } + private void createUI() { panelXAxis = new JPanel(); panelYAxis = new JPanel(); @@ -210,12 +271,12 @@ private void createUI() { buttonColor = new JButton("Get Colors"); buttonLayout = new JButton("Copy Layout"); + buttonExport = new JButton("Export"); container.setLayout(new GridBagLayout()); container.removeAll(); - ScatterPlot scatterPlot = - new ScatterPlot(manager, scores, loadings, 0, 1, pointColor, 3, loadingsColorMap, useLoadings); + scatterPlot = new ScatterPlot(manager, scores, loadings, 0, 1, pointColor, 3, loadingsColorMap, useLoadings); GridBagConstraints constraints = new GridBagConstraints(); constraints.anchor = GridBagConstraints.NORTHWEST; @@ -277,7 +338,7 @@ public JPanel createLegendPane(){ lButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { String label = e.getActionCommand(); - Color clr = JColorChooser.showDialog(thisDialog, "Choose color for "+label+" arrow", thisDialog.getBackground()); + Color clr = chooser.showDialog(thisDialog, "Choose color for "+label+" arrow", loadingsColorMap.get(label)); if (clr != null) { loadingsColorMap.put(label, clr); repaintScatterPlot(); @@ -319,8 +380,10 @@ public JXCollapsiblePane createAdvanceOptionPane(){ colorButton.addActionListener (new ActionListener () { public void actionPerformed(ActionEvent e) { Color clr = JColorChooser.showDialog(thisDialog, "Choose color of points", thisDialog.getBackground()); - if (clr != null) + if (clr != null) { pointColor = clr; + scatterPlot.setPointColor(pointColor); + } } }); @@ -410,6 +473,7 @@ public void actionPerformed(ActionEvent e) { panelButtons.add(buttonOptions); panelButtons.add(buttonPlot); panelButtons.add(buttonColor); + panelButtons.add(buttonExport); if (supportsLayout) panelButtons.add(buttonLayout); if (useLoadings) @@ -455,6 +519,8 @@ public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) { repaintScatterPlot(); + // int pointSize = Integer.parseInt(textFieldPointSize.getText()); + // scatterPlot.setPointSize(pointSize); } }); @@ -464,10 +530,32 @@ public void actionPerformed(ActionEvent e) public void actionPerformed(ActionEvent e) { pointColor = null; - repaintScatterPlot(); + scatterPlot.setPointColor(pointColor); + scatterPlot.setColorMap(loadingsColorMap); + // repaintScatterPlot(); + } + + }); + + buttonExport.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) + { + // File file = fileUtil.getFile(thisDialog, "Export Image", FileUtil.CUSTOM, null, "Export", fileFilters); + // System.out.println("File = "+file.getAbsolutePath()); + // exportDialog.setVisible(true); + createExportDialog(); } }); + + textFieldPointSize.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) + { + int pointSize = Integer.parseInt(textFieldPointSize.getText()); + scatterPlot.setPointSize(pointSize); + } + }); + if (supportsLayout) { buttonLayout.addActionListener(new ActionListener() { @@ -510,11 +598,11 @@ public void repaintScatterPlot() { yAxis = comboYAxis.getSelectedIndex(); } - ScatterPlot scatterPlot = new ScatterPlot(manager, scores, loadings, - xAxis, - yAxis, - pointColor, pointSize, loadingsColorMap, - useLoadings); + scatterPlot = new ScatterPlot(manager, scores, loadings, + xAxis, + yAxis, + pointColor, pointSize, loadingsColorMap, + useLoadings); GridBagConstraints constraints = new GridBagConstraints(); constraints.anchor = GridBagConstraints.NORTHWEST; constraints.insets = new Insets(5, 5, 5, 5); @@ -528,6 +616,125 @@ public void repaintScatterPlot() { container.updateUI(); } + public void createExportDialog() { + CyApplicationManager applicationManager = manager.getService(CyApplicationManager.class); + + JFileChooser fileChooser = new JFileChooser(); + // Clear the default file filter + for (FileFilter f: fileChooser.getChoosableFileFilters()) { + fileChooser.removeChoosableFileFilter(f); + } + fileChooser.setCurrentDirectory(applicationManager.getCurrentDirectory()); + for (FileChooserFilter filter: fileFilters) { + fileChooser.addChoosableFileFilter(filter); + } + if (fileChooser.showDialog(thisDialog, "Export") == JFileChooser.APPROVE_OPTION) { + FileChooserFilter chooserFilter = (FileChooserFilter)fileChooser.getFileFilter(); + File file = fileChooser.getSelectedFile(); + if (file == null) + file = new File(fileChooser.getCurrentDirectory(), "image"+chooserFilter.getExtensions()[0]); + + // System.out.println("File = "+file); + // System.out.println("Format = "+chooserFilter.getExtensions()[0]); + + // Check the file types and get the right format + if (!chooserFilter.accept(file)) { + FileChooserFilter newFilter = null; + for (FileChooserFilter filter: fileFilters) { + if (filter.accept(file)) { + newFilter = filter; + break; + } + } + if (newFilter == null) { + // We need to force the file to be the right type + file = new File(file.getAbsolutePath()+chooserFilter.getExtensions()[0]); + } else { + chooserFilter = newFilter; + } + } + + // Do the print + scatterPlot.print(chooserFilter.getExtensions()[0], file); + } + return; + } + + /* + public void createExportDialog() { + JComboBox formatSelection; + + // Get the format (popup dialog) + exportDialog = new JDialog(this, "Select the output file and format"); + + JPanel topPanel = new JPanel(); + topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.Y_AXIS)); + + JFileChooser fileChooser = new JFileChooser((String)null); + fileChooser.setDialogType(JFileChooser.SAVE_DIALOG); + fileChooser.setControlButtonsAreShown(false); + topPanel.add(fileChooser); + + { + Box formatBox = Box.createHorizontalBox(); + JLabel lbl = new JLabel("Export Format: "); + String[] formats = {"pdf","svg","png","jpg"}; + formatSelection = new JComboBox<>(formats); + + formatBox.add(Box.createHorizontalStrut(10)); + formatBox.add(lbl); + formatBox.add(formatSelection); + formatBox.add(Box.createHorizontalGlue()); + topPanel.add(formatBox); + } + + { + Box buttonBox = Box.createHorizontalBox(); + JButton exportButton = new JButton("Export"); + exportButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + fileChooser.approveSelection(); + String format = (String)formatSelection.getSelectedItem(); + File dir = fileChooser.getCurrentDirectory(); + File file = fileChooser.getSelectedFile(); + if (file == null) { + file = new File(dir, "image."+format); + } + System.out.println("File type = "+fileChooser.getTypeDescription(file)); + + // Do the print + scatterPlot.print(format, file); + + // Do the save + exportDialog.setVisible(false); + } + }); + JButton cancelButton = new JButton("Cancel"); + cancelButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + exportDialog.setVisible(false); + } + }); + // buttonBox.add(Box.createVerticalStrut(10)); + buttonBox.add(Box.createHorizontalGlue()); + buttonBox.add(exportButton); + buttonBox.add(Box.createHorizontalStrut(10)); + buttonBox.add(cancelButton); + buttonBox.add(Box.createHorizontalStrut(10)); + topPanel.add(buttonBox); + } + + { + Box spacerBox = Box.createHorizontalBox(); + spacerBox.add(Box.createVerticalStrut(10)); + topPanel.add(spacerBox); + } + + exportDialog.add(topPanel); + exportDialog.pack(); + } +*/ + private void initializeColors() { float hue = 0f; float saturation = .8f; @@ -548,4 +755,13 @@ private void copyLayoutToCytoscape() { view, coordinates, undo); splt.execute(); } + + private Collection getFileChooserFilters() { + List chooserList = new ArrayList<>(); + chooserList.add(new FileChooserFilter("PDF File", ".pdf")); + chooserList.add(new FileChooserFilter("PNG File", ".png")); + chooserList.add(new FileChooserFilter("JPEG File", ".jpg")); + chooserList.add(new FileChooserFilter("SVG File", ".svg")); + return chooserList; + } } diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pca/PCAMenuTaskFactory.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/VizFactory.java similarity index 51% rename from src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pca/PCAMenuTaskFactory.java rename to src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/VizFactory.java index 6926334..031a5d8 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/pca/PCAMenuTaskFactory.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/VizFactory.java @@ -1,52 +1,51 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package edu.ucsf.rbvi.clusterMaker2.internal.algorithms.pca; - -import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.AbstractClusterTaskFactory; -import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; -import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterTaskFactory; -import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterViz; +package edu.ucsf.rbvi.clusterMaker2.internal.ui; + import java.util.Collections; import java.util.List; + +//Cytoscape imports import org.cytoscape.model.CyNetwork; import org.cytoscape.work.TaskIterator; -/** - * - * @author root - */ -public class PCAMenuTaskFactory implements ClusterTaskFactory{ +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterTaskFactory; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterTaskFactory.ClusterType; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterViz; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterVizFactory; + +public class VizFactory implements ClusterVizFactory { ClusterManager clusterManager; - public PCAMenuTaskFactory(ClusterManager clusterManager) { + + public VizFactory(ClusterManager clusterManager) { this.clusterManager = clusterManager; } - - public String getShortName() {return "dimred";}; - public String getName() {return "--- Dimensionality Reduction ---";}; + + public String getShortName() {return null; } + public String getName() {return "--- Visualizations ---";} public ClusterViz getVisualizer() { + // return new NewNetworkView(true); return null; } public boolean isReady() { return false; } - - @Override + public boolean isAvailable(CyNetwork network) { return false; } - public List getTypeList() { - return Collections.singletonList(ClusterTaskFactory.ClusterType.DIMRED); + public List getTypeList() { + return Collections.singletonList(ClusterType.UI); } public TaskIterator createTaskIterator() { + // Not sure why we need to do this, but it looks like + // the tunable stuff "remembers" objects that it's already + // processed this tunable. So, we use a copy constructor return null; - } + } @Override public String getSupportsJSON() { return "false"; } @@ -56,4 +55,10 @@ public TaskIterator createTaskIterator() { @Override public String getExampleJSON() { return ""; } + } + + + + + diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/ClusterUtils.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/ClusterUtils.java index 4fc98d1..077aea3 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/ClusterUtils.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/ClusterUtils.java @@ -41,64 +41,66 @@ private static void setEdgeTableColumnValues(CyTable edgeTable, List edg } } - public static List setEdgeScoresInCluster(CyNetwork network, List clusters, List edgeAttributes, String clusterColumnName, boolean multiplicative) { + public static List setEdgeScoresInCluster(CyNetwork network, List clusters, + Map edgeMap, String clusterColumnName, boolean multiplicative) { List edges = network.getEdgeList(); CyTable nodeTable = network.getDefaultNodeTable(); CyTable edgeTable = network.getDefaultEdgeTable(); - for (String edgeAttr : edgeAttributes) { - for (CyEdge edge : edges) { - CyRow source = nodeTable.getRow(edge.getSource().getSUID()); - CyRow target = nodeTable.getRow(edge.getTarget().getSUID()); - CyRow edgeRow = edgeTable.getRow(edge.getSUID()); - int sourceClusterNumber = source.get(clusterColumnName, Integer.class, -1); - int targetClusterNumber = target.get(clusterColumnName, Integer.class, -1); - int sourceHighestClusterNumber = -1; - int targetHighestClusterNumber = -1; - - for (NodeCluster cluster : clusters) { - int clusterNumber = cluster.getClusterNumber(); - - if (clusterNumber == sourceClusterNumber && (clusterNumber < sourceHighestClusterNumber || sourceHighestClusterNumber == -1)) { - if (multiplicative) { - setRankScoreMultiplicative(edgeAttr, edgeRow, cluster); - } else { - setRankScore(edgeAttr, edgeRow, cluster); - } - sourceHighestClusterNumber = clusterNumber; - } else if (clusterNumber == targetClusterNumber && (clusterNumber < targetHighestClusterNumber || targetHighestClusterNumber == -1)) { - if (multiplicative) { - setRankScoreMultiplicative(edgeAttr, edgeRow, cluster); - } else { - setRankScore(edgeAttr, edgeRow, cluster); - } - targetHighestClusterNumber = clusterNumber; - } + for (CyEdge edge : edges) { + CyRow source = nodeTable.getRow(edge.getSource().getSUID()); + CyRow target = nodeTable.getRow(edge.getTarget().getSUID()); + CyRow edgeRow = edgeTable.getRow(edge.getSUID()); + double[] attrValues = edgeMap.get(edge); + int sourceClusterNumber = source.get(clusterColumnName, Integer.class, -1); + int targetClusterNumber = target.get(clusterColumnName, Integer.class, -1); + int sourceHighestClusterNumber = -1; + int targetHighestClusterNumber = -1; + + for (int attrIndex = 0; attrIndex < attrValues.length; attrIndex++) { + for (NodeCluster cluster : clusters) { + int clusterNumber = cluster.getClusterNumber(); + if (clusterNumber == sourceClusterNumber && (clusterNumber < sourceHighestClusterNumber || sourceHighestClusterNumber == -1)) { + if (multiplicative) { + setRankScoreMultiplicative(cluster, attrValues[attrIndex]); + } else { + setRankScore(cluster, attrValues[attrIndex]); } + sourceHighestClusterNumber = clusterNumber; + } else if (clusterNumber == targetClusterNumber && (clusterNumber < targetHighestClusterNumber || targetHighestClusterNumber == -1)) { + if (multiplicative) { + setRankScoreMultiplicative(cluster, attrValues[attrIndex]); + } else { + setRankScore(cluster, attrValues[attrIndex]); + } + targetHighestClusterNumber = clusterNumber; + } } + } } return clusters; } - public static List setNodeScoresInCluster(CyNetwork network, List clusters, List nodeAttributes, String clusterColumnName, boolean multiplicative) { + public static List setNodeScoresInCluster(CyNetwork network, List clusters, + Map nodeMap, String clusterColumnName, boolean multiplicative) { List nodes = network.getNodeList(); CyTable table = network.getDefaultNodeTable(); - for (String nodeAttr : nodeAttributes) { - for (CyNode node : nodes) { - CyRow row = table.getRow(node.getSUID()); - int nodeClusterNumber = row.get(clusterColumnName, Integer.class, -1); - - for (NodeCluster cluster : clusters) { - if (cluster.getClusterNumber() == nodeClusterNumber) { - if (multiplicative) { - setRankScoreMultiplicative(nodeAttr, row, cluster); - } else { - setRankScore(nodeAttr, row, cluster); - } - } + for (CyNode node : nodes) { + CyRow row = table.getRow(node.getSUID()); + int nodeClusterNumber = row.get(clusterColumnName, Integer.class, -1); + double[] attrValues = nodeMap.get(node); + for (int attrIndex = 0; attrIndex < attrValues.length; attrIndex++) { + for (NodeCluster cluster : clusters) { + if (cluster.getClusterNumber() == nodeClusterNumber) { + if (multiplicative) { + setRankScoreMultiplicative(cluster, attrValues[attrIndex]); + } else { + setRankScore(cluster, attrValues[attrIndex]); + } } + } } } @@ -109,8 +111,9 @@ public static List setNodeScoresInCluster(CyNetwork network, List clusters, String shortname) { - CyTable nodeTable = network.getDefaultNodeTable(); - CyTable edgeTable = network.getDefaultEdgeTable(); + + CyTable nodeTable = network.getTable(CyNode.class, CyNetwork.LOCAL_ATTRS); + CyTable edgeTable = network.getTable(CyEdge.class, CyNetwork.LOCAL_ATTRS); CyTable networkTable = network.getDefaultNetworkTable(); List edges = network.getEdgeList(); @@ -129,32 +132,15 @@ public static void insertResultsInColumns(CyNetwork network, List c ClusterUtils.setEdgeTableColumnValues(edgeTable, edges, clusters, shortname); } - private static void setRankScore(String attribute, CyRow row, NodeCluster cluster) { - try { - cluster.addScoreToAvg(cluster.getRankScore() + row.get(attribute, Double.class, 0.0)); - } catch (ClassCastException cce) { - try { - cluster.addScoreToAvg(cluster.getRankScore() + row.get(attribute, Integer.class, 0)); - } catch (Exception e) { // Not a number type! - e.printStackTrace(); - } - } + private static void setRankScore(NodeCluster cluster, double value) { + cluster.addScoreToAvg(cluster.getRankScore() + value); } - private static void setRankScoreMultiplicative(String edgeAttr, CyRow source, NodeCluster cluster) { - try { - if (cluster.getRankScore() == 0.0) { - cluster.addScoreToAvg(1.0); - } - cluster.addScoreToAvg(cluster.getRankScore() * (source.get(edgeAttr, Double.class, 0.0) + 1.0)); // assumes - // values between 0.0 and 1.0 - } catch (ClassCastException cce) { // - try { - cluster.addScoreToAvg(cluster.getRankScore() * (source.get(edgeAttr, Integer.class, 0) + 1)); - } catch (Exception e) { // Not a number type! - e.printStackTrace(); - } + private static void setRankScoreMultiplicative(NodeCluster cluster, double value) { + if (cluster.getRankScore() == 0.0) { + cluster.addScoreToAvg(1.0); } + cluster.addScoreToAvg(cluster.getRankScore() * (value + 1.0)); // assumes } public static List fetchClusters(CyNetwork network) { @@ -187,7 +173,7 @@ public static List fetchClusters(CyNetwork network, String clusterA return clusters; } - public static List fetchRankingResults(CyNetwork network) { + public static List fetchRankingResults(CyNetwork network, boolean skipSingletons) { List clusters = new ArrayList<>(); String clusterAttribute = getClusterAttribute(network); String rankingAttribute = getRankingAttribute(network); @@ -211,6 +197,8 @@ public static List fetchRankingResults(CyNetwork network) { } for (int clusterNum : clusterMap.keySet()) { + if (skipSingletons && clusterMap.get(clusterNum).size() <= 1) + continue; NodeCluster cluster = new NodeCluster(clusterMap.get(clusterNum)); cluster.setClusterNumber(clusterNum); cluster.setRankScore(clusterScoreMap.get(clusterNum)); diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/ModelUtils.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/ModelUtils.java index ad956c5..944d7e4 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/ModelUtils.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/ModelUtils.java @@ -14,6 +14,7 @@ import org.cytoscape.model.CyNode; import org.cytoscape.model.CyRow; import org.cytoscape.model.CyTable; +import org.cytoscape.model.CyTableManager; import org.cytoscape.model.CyTableUtil; import org.cytoscape.model.subnetwork.CySubNetwork; import org.cytoscape.work.util.ListMultipleSelection; @@ -63,6 +64,12 @@ public static boolean hasAttribute(CyNetwork network, CyIdentifiable value, Stri return hasAttribute(network, value, column, CyNetwork.DEFAULT_ATTRS); } + public static boolean hasColumn(CyNetwork network, CyTable table, String column) { + if (!CyTableUtil.getColumnNames(table).contains(column)) + return false; + return true; + } + public static boolean hasAttribute(CyNetwork network, CyIdentifiable value, String column, String namespace) { if (!CyTableUtil.getColumnNames(network.getRow(value, namespace).getTable()).contains(column)) return false; @@ -117,6 +124,13 @@ public static CyNetwork createChildNetwork(ClusterManager manager, CyNetwork net return newNetwork; } + public static void deleteUnassignedTable(ClusterManager manager, Long suid) { + if (suid == null) return; + + CyTableManager tableManager = manager.getTableManager(); + tableManager.deleteTable(suid); + } + public static void createAndSetLocal(CyNetwork net, CyIdentifiable obj, String column, Object value, Class type, Class elementType) { createAndSet(net, obj, column, value, type, elementType, CyNetwork.LOCAL_ATTRS); @@ -147,9 +161,9 @@ public static void copyLocalColumn(CyNetwork source, CyNetwork target, CyTable targetTable = target.getTable(clazz, CyNetwork.LOCAL_ATTRS); boolean isImmutable = sourceColumn.isImmutable(); - if (sourceColumn.getType().equals(List.class)) + if (sourceColumn.getType().equals(List.class) && !hasColumn(target, targetTable, column)) targetTable.createListColumn(column, sourceColumn.getListElementType(), isImmutable); - else + else if (!hasColumn(target, targetTable, column)) targetTable.createColumn(column, sourceColumn.getType(), isImmutable); // We need to handle things a little differently for CyNetworks... diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/MonitorableTask.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/MonitorableTask.java similarity index 97% rename from src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/MonitorableTask.java rename to src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/MonitorableTask.java index f85f822..5365c72 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/MonitorableTask.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/MonitorableTask.java @@ -1,4 +1,4 @@ -package edu.ucsf.rbvi.clusterMaker2.internal.ui; +package edu.ucsf.rbvi.clusterMaker2.internal.utils; /** * Classes that perform long tasks (like graph algorithms) can implement this interface @@ -57,4 +57,4 @@ public interface MonitorableTask { // TODO: Not sure if needed public boolean wasCanceled (); -} \ No newline at end of file +} diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/NodeDistances.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/NodeDistances.java similarity index 99% rename from src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/NodeDistances.java rename to src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/NodeDistances.java index 9afc209..8daa605 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/NodeDistances.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/NodeDistances.java @@ -1,4 +1,4 @@ -package edu.ucsf.rbvi.clusterMaker2.internal.ui; +package edu.ucsf.rbvi.clusterMaker2.internal.utils; import java.util.Arrays; import java.util.Collection; @@ -350,4 +350,4 @@ class NodeDistancesTask { calculate(); } } -} \ No newline at end of file +} diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/SpringEmbeddedLayouter.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/SpringEmbeddedLayouter.java similarity index 99% rename from src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/SpringEmbeddedLayouter.java rename to src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/SpringEmbeddedLayouter.java index 60c711f..e009513 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/ui/SpringEmbeddedLayouter.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/SpringEmbeddedLayouter.java @@ -1,4 +1,4 @@ -package edu.ucsf.rbvi.clusterMaker2.internal.ui; +package edu.ucsf.rbvi.clusterMaker2.internal.utils; import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NODE_HEIGHT; import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NODE_WIDTH; @@ -626,4 +626,4 @@ public void reset() { } } -} \ No newline at end of file +} diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/ViewUtils.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/ViewUtils.java index 7655791..8e46f61 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/ViewUtils.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/ViewUtils.java @@ -1,8 +1,35 @@ package edu.ucsf.rbvi.clusterMaker2.internal.utils; +import static org.cytoscape.view.presentation.property.ArrowShapeVisualProperty.NONE; +import static org.cytoscape.view.presentation.property.BasicVisualLexicon.EDGE_PAINT; +import static org.cytoscape.view.presentation.property.BasicVisualLexicon.EDGE_SELECTED_PAINT; +import static org.cytoscape.view.presentation.property.BasicVisualLexicon.EDGE_SOURCE_ARROW_SHAPE; +import static org.cytoscape.view.presentation.property.BasicVisualLexicon.EDGE_STROKE_SELECTED_PAINT; +import static org.cytoscape.view.presentation.property.BasicVisualLexicon.EDGE_STROKE_UNSELECTED_PAINT; +import static org.cytoscape.view.presentation.property.BasicVisualLexicon.EDGE_TARGET_ARROW_SHAPE; +import static org.cytoscape.view.presentation.property.BasicVisualLexicon.EDGE_UNSELECTED_PAINT; +import static org.cytoscape.view.presentation.property.BasicVisualLexicon.EDGE_WIDTH; +import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NETWORK_BACKGROUND_PAINT; +import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NETWORK_HEIGHT; +import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NETWORK_WIDTH; +import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NODE_BORDER_WIDTH; +import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NODE_BORDER_WIDTH; +import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NODE_FILL_COLOR; +import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NODE_HEIGHT; +import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NODE_PAINT; +import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NODE_SIZE; +import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NODE_WIDTH; +import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NODE_X_LOCATION; +import static org.cytoscape.view.presentation.property.BasicVisualLexicon.NODE_Y_LOCATION; + import java.util.HashSet; import java.awt.Color; +import java.awt.Image; +import java.awt.image.BufferedImage; +import java.awt.Paint; + +import javax.swing.SwingUtilities; import org.cytoscape.event.CyEventHelper; @@ -14,100 +41,317 @@ import org.cytoscape.model.CyEdge; import org.cytoscape.model.CyTable; import org.cytoscape.model.CyTableUtil; +import org.cytoscape.model.SavePolicy; +import org.cytoscape.model.subnetwork.CyRootNetwork; +import org.cytoscape.model.subnetwork.CyRootNetworkManager; +import org.cytoscape.util.color.Palette; import org.cytoscape.view.layout.CyLayoutAlgorithm; import org.cytoscape.view.layout.CyLayoutAlgorithmManager; import org.cytoscape.view.model.CyNetworkView; import org.cytoscape.view.model.CyNetworkViewFactory; import org.cytoscape.view.model.CyNetworkViewManager; import org.cytoscape.view.model.View; +import org.cytoscape.view.presentation.NetworkImageFactory; import org.cytoscape.view.presentation.property.BasicVisualLexicon; import org.cytoscape.view.vizmap.VisualMappingManager; +import org.cytoscape.view.vizmap.VisualMappingFunctionFactory; import org.cytoscape.view.vizmap.VisualStyle; import org.cytoscape.view.vizmap.VisualStyleFactory; +import org.cytoscape.view.vizmap.mappings.BoundaryRangeValues; +import org.cytoscape.view.vizmap.mappings.ContinuousMapping; import org.cytoscape.work.Task; import org.cytoscape.work.TaskIterator; import org.cytoscape.work.TaskMonitor; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.NodeCluster; import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; public class ViewUtils { - public static CyNetworkView createView(ClusterManager manager, CyNetwork newNetwork, boolean register) { - // Create the view - CyNetworkView view = - manager.getService(CyNetworkViewFactory.class).createNetworkView(newNetwork); + public static CyNetworkView createView(ClusterManager manager, CyNetwork newNetwork, boolean register) { + // Create the view + CyNetworkView view = + manager.getService(CyNetworkViewFactory.class).createNetworkView(newNetwork); - if (register) - registerView(manager, view); + if (register) + registerView(manager, view); - // Make sure we flush our events before we try to do anything else - CyEventHelper eventHelper = manager.getService(CyEventHelper.class); - eventHelper.flushPayloadEvents(); + // Make sure we flush our events before we try to do anything else + CyEventHelper eventHelper = manager.getService(CyEventHelper.class); + eventHelper.flushPayloadEvents(); - return view; - } + return view; + } - public static void registerView(ClusterManager manager, CyNetworkView view) { - manager.getService(CyNetworkViewManager.class).addNetworkView(view); - } + public static void registerView(ClusterManager manager, CyNetworkView view) { + manager.getService(CyNetworkViewManager.class).addNetworkView(view); + } - public static void doLayout(ClusterManager manager, CyNetworkView view, - TaskMonitor monitor, String algName) { - CyLayoutAlgorithm alg = manager.getService(CyLayoutAlgorithmManager.class).getLayout(algName); - if (alg != null) { - TaskIterator ti = alg.createTaskIterator(view, alg.getDefaultLayoutContext(), - new HashSet>(), null); - try { - while (ti.hasNext()) - ti.next().run(monitor); - } catch (Exception e) { - monitor.showMessage(TaskMonitor.Level.ERROR, "Unable to layout network: "+e.getMessage()); - } - } - } + public static void copyLayout(ClusterManager manager, CyNetworkView sourceView, CyNetworkView targetView) { + for (View sourceNodeView: sourceView.getNodeViews()) { + double x = sourceNodeView.getVisualProperty(BasicVisualLexicon.NODE_X_LOCATION); + double y = sourceNodeView.getVisualProperty(BasicVisualLexicon.NODE_Y_LOCATION); + moveNode(manager, targetView, sourceNodeView.getModel(), x, y); + } + } + public static void doLayout(ClusterManager manager, CyNetworkView view, + TaskMonitor monitor, String algName) { + CyLayoutAlgorithm alg = manager.getService(CyLayoutAlgorithmManager.class).getLayout(algName); + if (alg != null) { + TaskIterator ti = alg.createTaskIterator(view, alg.getDefaultLayoutContext(), + new HashSet>(), null); + try { + while (ti.hasNext()) + ti.next().run(monitor); + } catch (Exception e) { + monitor.showMessage(TaskMonitor.Level.ERROR, "Unable to layout network: "+e.getMessage()); + } + } + } - public static VisualStyle getCurrentVisualStyle(ClusterManager manager) { - return manager.getService(VisualMappingManager.class).getCurrentVisualStyle(); - } - public static VisualStyle copyStyle(ClusterManager manager, VisualStyle style, String suffix) { - VisualStyle newStyle = manager.getService(VisualStyleFactory.class).createVisualStyle(style); - newStyle.setTitle(style.getTitle()+suffix); - manager.getService(VisualMappingManager.class).addVisualStyle(newStyle); - return newStyle; - } + public static VisualStyle getCurrentVisualStyle(ClusterManager manager) { + return manager.getService(VisualMappingManager.class).getCurrentVisualStyle(); + } - public static void setVisualStyle(ClusterManager manager, CyNetworkView view, - VisualStyle style) { - manager.getService(VisualMappingManager.class).setVisualStyle(style, view); - view.updateView(); - } + public static VisualStyle copyStyle(ClusterManager manager, VisualStyle style, String suffix) { + VisualStyle newStyle = manager.getService(VisualStyleFactory.class).createVisualStyle(style); + newStyle.setTitle(style.getTitle()+suffix); + manager.getService(VisualMappingManager.class).addVisualStyle(newStyle); + return newStyle; + } - public static Color getColor(ClusterManager manager, CyNetwork network, CyNode node) { - CyNetworkView view = manager.getNetworkView(network); - View nodeView = view.getNodeView(node); - return (Color)nodeView.getVisualProperty(BasicVisualLexicon.NODE_FILL_COLOR); - } + // NOTE: we need at least two pivot points + public static VisualStyle createFillStyle(ClusterManager manager, VisualStyle source, String suffix, String column, Palette palette, double[] pivots) { + VisualStyle newStyle; + if (source != null) { + newStyle = copyStyle(manager, source, suffix); + } else { + VisualStyleFactory visualStyleFactory = manager.getService(VisualStyleFactory.class); + newStyle = visualStyleFactory.createVisualStyle(palette.getName()); + } - public static String getLabel(ClusterManager manager, CyNetwork network, CyNode node) { - CyNetworkView view = manager.getNetworkView(network); - View nodeView = view.getNodeView(node); - return nodeView.getVisualProperty(BasicVisualLexicon.NODE_LABEL); - } + VisualMappingFunctionFactory vmff = manager.getService(VisualMappingFunctionFactory.class, "(mapping.type=continuous)"); + ContinuousMapping mapping = (ContinuousMapping) vmff.createVisualMappingFunction(column, Double.class, + BasicVisualLexicon.NODE_FILL_COLOR); + + int nPivots = pivots.length; + Color[] paletteColors = palette.getColors(nPivots); + + // First point + BoundaryRangeValues firstRange = new BoundaryRangeValues<>(paletteColors[0], paletteColors[0], paletteColors[1]); + mapping.addPoint(pivots[0], firstRange); + + for (int i=1; i < nPivots-1; i++) { + BoundaryRangeValues range = new BoundaryRangeValues<>(paletteColors[i-1], paletteColors[i], paletteColors[i+1]); + mapping.addPoint(pivots[i], range); + } + + // Handle the last point + BoundaryRangeValues lastRange = new BoundaryRangeValues<>(paletteColors[nPivots-2], paletteColors[nPivots-1], paletteColors[nPivots-1]); + mapping.addPoint(pivots[nPivots-1], lastRange); + + newStyle.addVisualMappingFunction(mapping); + + return newStyle; + } + + public static void setVisualStyle(ClusterManager manager, CyNetworkView view, + VisualStyle style) { + manager.getService(VisualMappingManager.class).setVisualStyle(style, view); + view.updateView(); + } + + public static Color getColor(ClusterManager manager, CyNetwork network, CyNode node) { + CyNetworkView view = manager.getNetworkView(network); + View nodeView = view.getNodeView(node); + return (Color)nodeView.getVisualProperty(BasicVisualLexicon.NODE_FILL_COLOR); + } + + public static String getLabel(ClusterManager manager, CyNetwork network, CyNode node) { + CyNetworkView view = manager.getNetworkView(network); + View nodeView = view.getNodeView(node); + return nodeView.getVisualProperty(BasicVisualLexicon.NODE_LABEL); + } + + public static void moveNode(ClusterManager manager, CyNetwork network, + CyNode node, double x, double y) { + CyNetworkView view = manager.getNetworkView(network); + moveNode(manager, view, node, x, y); + } + + public static void moveNode(ClusterManager manager, CyNetworkView view, + CyNode node, double x, double y) { + View nodeView = view.getNodeView(node); + if (nodeView == null) return; + nodeView.setVisualProperty(BasicVisualLexicon.NODE_X_LOCATION, x); + nodeView.setVisualProperty(BasicVisualLexicon.NODE_Y_LOCATION, y); + } + + /** + * Convert a network to an image. This is used by the MCODEResultsPanel. + * + * @param clusterManager the clusterManager (used to get services) + * @param vs the VisualStyle to use + * @param network the network + * @param cluster Input network to convert to an image + * @param height Height that the resulting image should be + * @param width Width that the resulting image should be + * @param layouter Reference to the layout algorithm + * @param layoutNecessary Determinant of cluster size growth or shrinkage, the former requires layout + * @return The resulting image + */ + public static Image createClusterImage(final ClusterManager clusterManager, + final VisualStyle vs, + final CyNetwork network, + final NodeCluster cluster, + final int height, + final int width, + SpringEmbeddedLayouter layouter, + boolean layoutNecessary) { + //System.out.println("CCI: inside method"); + final CyRootNetwork root = clusterManager.getService(CyRootNetworkManager.class).getRootNetwork(network); + final NetworkImageFactory networkImageFactory = clusterManager.getService(NetworkImageFactory.class); + //need to create a method get the subnetwork for a cluster + final CyNetwork net = cluster.getSubNetwork(network, root, SavePolicy.DO_NOT_SAVE); + + //System.out.println("CCI: after getting root and network "); + // Progress reporters. + // There are three basic tasks, the progress of each is calculated and then combined + // using the respective weighting to get an overall progress global progress + int weightSetupNodes = 20; // setting up the nodes and edges is deemed as 25% of the whole task + int weightSetupEdges = 5; + double weightLayout = 75.0; // layout it is 70% + double goalTotal = weightSetupNodes + weightSetupEdges; - public static void moveNode(ClusterManager manager, CyNetwork network, - CyNode node, double x, double y) { - CyNetworkView view = manager.getNetworkView(network); - moveNode(manager, view, node, x, y); + if (layoutNecessary) { + goalTotal += weightLayout; + } + + // keeps track of progress as a percent of the totalGoal + double progress = 0; + + //System.out.println("CCI: after getClusterStyle"); + final CyNetworkView clusterView = createNetworkView(clusterManager, net, vs); + //System.out.println("CCI: after createNetworkView"); + + clusterView.setVisualProperty(NETWORK_WIDTH, new Double(width)); + clusterView.setVisualProperty(NETWORK_HEIGHT, new Double(height)); + + for (View nv : clusterView.getNodeViews()) { + /* + if (interrupted) { + //logger.debug("Interrupted: Node Setup"); + // before we short-circuit the method we reset the interruption so that the method can run without + // problems the next time around + if (layouter != null) layouter.resetDoLayout(); + resetLoading(); + + return null; + } + */ + + // Node position + final double x; + final double y; + + // First we check if the MCODECluster already has a node view of this node (posing the more generic condition + // first prevents the program from throwing a null pointer exception in the second condition) + if (cluster.getView() != null && cluster.getView().getNodeView(nv.getModel()) != null) { + //If it does, then we take the layout position that was already generated for it + x = cluster.getView().getNodeView(nv.getModel()).getVisualProperty(NODE_X_LOCATION); + y = cluster.getView().getNodeView(nv.getModel()).getVisualProperty(NODE_Y_LOCATION); + } else { + // Otherwise, randomize node positions before layout so that they don't all layout in a line + // (so they don't fall into a local minimum for the SpringEmbedder) + // If the SpringEmbedder implementation changes, this code may need to be removed + // size is small for many default drawn graphs, thus +100 + x = (clusterView.getVisualProperty(NETWORK_WIDTH) + 100) * Math.random(); + y = (clusterView.getVisualProperty(NETWORK_HEIGHT) + 100) * Math.random(); + + if (!layoutNecessary) { + goalTotal += weightLayout; + progress /= (goalTotal / (goalTotal - weightLayout)); + layoutNecessary = true; + } + } + + nv.setVisualProperty(NODE_X_LOCATION, x); + nv.setVisualProperty(NODE_Y_LOCATION, y); + } + + if (layoutNecessary) { + if (layouter == null) { + layouter = new SpringEmbeddedLayouter(); + } + + layouter.setGraphView(clusterView); + + // The doLayout method should return true if the process completes without interruption + if (!layouter.doLayout(weightLayout, goalTotal, progress)) { + // Otherwise, if layout is not completed, set the interruption to false, and return null, not an image + return null; + } + } + + // final Image image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + final Image image = networkImageFactory.createImage(clusterView, width, height); + + + return image; + } + + public static VisualStyle getClusterStyle(final ClusterManager clusterManager, VisualStyle clusterStyle) { + if (clusterStyle == null) { + VisualStyleFactory visualStyleFactory = clusterManager.getService(VisualStyleFactory.class); + clusterStyle = visualStyleFactory.createVisualStyle("Cluster"); + + clusterStyle.setDefaultValue(NODE_SIZE, 40.0); + clusterStyle.setDefaultValue(NODE_WIDTH, 40.0); + clusterStyle.setDefaultValue(NODE_HEIGHT, 40.0); + clusterStyle.setDefaultValue(NODE_PAINT, Color.RED); + clusterStyle.setDefaultValue(NODE_FILL_COLOR, Color.RED); + clusterStyle.setDefaultValue(NODE_BORDER_WIDTH, 0.0); + + clusterStyle.setDefaultValue(EDGE_WIDTH, 5.0); + clusterStyle.setDefaultValue(EDGE_PAINT, Color.BLUE); + clusterStyle.setDefaultValue(EDGE_UNSELECTED_PAINT, Color.BLUE); + clusterStyle.setDefaultValue(EDGE_STROKE_UNSELECTED_PAINT, Color.BLUE); + clusterStyle.setDefaultValue(EDGE_SELECTED_PAINT, Color.BLUE); + clusterStyle.setDefaultValue(EDGE_STROKE_SELECTED_PAINT, Color.BLUE); + clusterStyle.setDefaultValue(EDGE_TARGET_ARROW_SHAPE, NONE); + clusterStyle.setDefaultValue(EDGE_SOURCE_ARROW_SHAPE, NONE); + + /* + //System.out.println("GCS: before getVisual Lexicon"); + VisualLexicon lexicon = applicationMgr.getCurrentRenderingEngine().getVisualLexicon(); + VisualProperty vp = lexicon.lookup(CyEdge.class, "edgeTargetArrowShape"); + //System.out.println("CCI: after setting visual property"); + + if (vp != null) { + Object arrowValue = vp.parseSerializableString("ARROW"); + System.out.println("Edge target arrow value = "+arrowValue.toString()); + if (arrowValue != null) clusterStyle.setDefaultValue(vp, arrowValue); + } + */ + } + + return clusterStyle; } - public static void moveNode(ClusterManager manager, CyNetworkView view, - CyNode node, double x, double y) { - View nodeView = view.getNodeView(node); - nodeView.setVisualProperty(BasicVisualLexicon.NODE_X_LOCATION, x); - nodeView.setVisualProperty(BasicVisualLexicon.NODE_Y_LOCATION, y); + public static CyNetworkView createNetworkView(final ClusterManager clusterManager, final CyNetwork net, VisualStyle vs) { + CyNetworkViewFactory networkViewFactory = clusterManager.getService(CyNetworkViewFactory.class); + VisualMappingManager visualMappingMgr = clusterManager.getService(VisualMappingManager.class); + final CyNetworkView view = networkViewFactory.createNetworkView(net); + //System.out.println("inside createNetworkView"); + if (vs == null) vs = visualMappingMgr.getDefaultVisualStyle(); + visualMappingMgr.setVisualStyle(vs, view); + vs.apply(view); + view.updateView(); + + return view; } } diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/remoteUtils/ClusterJobExecutionService.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/remoteUtils/ClusterJobExecutionService.java index dab464b..58b63ba 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/remoteUtils/ClusterJobExecutionService.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/remoteUtils/ClusterJobExecutionService.java @@ -23,6 +23,8 @@ import org.cytoscape.jobs.CyJobDataService; import org.cytoscape.jobs.CyJobStatus; import org.cytoscape.jobs.CyJobStatus.Status; +import org.cytoscape.model.CyColumn; +import org.cytoscape.model.CyIdentifiable; import org.cytoscape.model.CyNetwork; import org.cytoscape.model.CyNode; import org.cytoscape.model.CyTable; @@ -41,6 +43,7 @@ import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.AbstractNetworkClusterer; import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.Leiden.LeidenCluster; import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; +import edu.ucsf.rbvi.clusterMaker2.internal.ui.ScatterPlotDialog; /** * The main interface to the RBVI network cluster REST service. The @@ -134,7 +137,10 @@ public CyJobStatus cancelJob(CyJob job) { public CyJobStatus checkJobStatus(CyJob job) { if (job instanceof ClusterJob) { JSONObject result = handleCommand((ClusterJob)job, Command.CHECK, null); - return getStatus(result, null); + CyJobStatus status = getStatus(result, null); + if (status.getStatus() == Status.ERROR) + logger.error("Job "+job.getJobId()+" experienced and error: "+status.getMessage()); + return status; } return new CyJobStatus(Status.ERROR, "CyJob is not a ClusterJob"); } @@ -143,67 +149,78 @@ public CyJobStatus checkJobStatus(CyJob job) { @Override public CyJobStatus executeJob(CyJob job, String basePath, Map configuration, //configuration comes from network data CyJobData inputData) { - if (!(job instanceof ClusterJob)) return new CyJobStatus(Status.ERROR, "CyJob is not a ClusterJob"); //error message if not clusterjob ClusterJob clJob = (ClusterJob)job; //converts CyJob into ClusterJob Map queryMap = convertConfiguration(configuration); //converts configuration into Map - + String serializedData = dataService.getSerializedData(inputData); //gets serialized data (JSON) using dataService - System.out.println("Serialized data in execution service: " + serializedData); - queryMap.put("inputData", serializedData.toString()); //...and puts it into queryMap as key: "inputData", value: String of the data - queryMap.put(COMMAND, Command.SUBMIT.toString()); //puts key: COMMAND, value: SUBMIT in the queryMap --> queryMap has two keys + // System.out.println("Serialized data in execution service: " + serializedData); + // queryMap.put("inputData", serializedData.toString()); //...and puts it into queryMap as key: "inputData", value: String of the data + // queryMap.put(COMMAND, Command.SUBMIT.toString()); //puts key: COMMAND, value: SUBMIT in the queryMap --> queryMap has two keys JSONParser parser = new JSONParser(); JSONObject jsonData = null; try { jsonData = (JSONObject) parser.parse(serializedData); } catch (ParseException e1) { - System.out.println("Data to JSONObject conversion failed: " + e1.getMessage()); + // System.out.println("Data to JSONObject conversion failed: " + e1.getMessage()); + logger.error("Data to JSONObject conversion failed: " + e1.getMessage()); + return new CyJobStatus(Status.ERROR, "Data to JSONObject conversion failed: "+e1.getMessage()); } + // System.out.println("jsonData (posted on server): " + jsonData); + Object value = null; String jobName = (String) clJob.getClusterData().get("shortName"); try { - value = RemoteServer.postFile(RemoteServer.getServiceURI(jobName), jsonData); + value = RemoteServer.postFile(RemoteServer.getServiceURIWithArgs(jobName,queryMap), jsonData); } catch (Exception e) { - System.out.println("Error in postFile method: " + e.getMessage()); + logger.error("Error in postFile method: " + e.getMessage()); + return new CyJobStatus(Status.ERROR, "Error in postFile method: "+e.getMessage()); } - System.out.println("JSON Job ID: " + value); + logger.info("JSON Job ID: " + value); if (value == null) return new CyJobStatus(Status.ERROR, "Job submission failed!"); + JSONObject json = (JSONObject) value; if (!json.containsKey(JOBID)) { - System.out.println("JSON returned: "+json.toString()); + // System.out.println("JSON returned: "+json.toString()); + logger.error("Server didn't return an ID!"); return new CyJobStatus(Status.ERROR, "Server didn't return an ID!"); } - System.out.println("JSONObject postFile(): " + json); + // System.out.println("JSONObject postFile(): " + json); String jobId = json.get(JOBID).toString(); //gets the job ID from the JSON Object clJob.setJobId(jobId); //...and sets it to the ClusterJob - System.out.println("ClusterJob jobID: " + clJob.getJobId()); + logger.info("ClusterJob jobID: " + clJob.getJobId()); //everything above this is to get the job ID from the JSON jobID repsonse from postFile() and put it in the ClusterJob object clJob.setBasePath(basePath); //...and also sets the basePath to the Cluster Job - System.out.println("ClusterJob BasePath: " + clJob.getBasePath()); + logger.info("ClusterJob BasePath: " + clJob.getBasePath()); //getting status - int waitTime = 20; + int waitTime = Integer.parseInt(queryMap.get("waitTime")); CyJobStatus status = checkJobStatus(clJob); - for (int i = 0; i < waitTime; i += 5) { - if (jobDone(status)) return status; - try { - TimeUnit.SECONDS.sleep(5); - } catch (InterruptedException e) { - return new CyJobStatus(Status.ERROR, "Interrupted"); - } - - status = checkJobStatus(clJob); - } + boolean done = false; + int i = 0; + while (!done) { + if (jobDone(status)) return status; + try { + TimeUnit.SECONDS.sleep(5); + } catch (InterruptedException e) { + return new CyJobStatus(Status.ERROR, "Interrupted"); + } + + status = checkJobStatus(clJob); + i += 5; + if ((waitTime >= 0) && (i >= waitTime)) + return status; + } return status; } @@ -214,41 +231,31 @@ private boolean jobDone(CyJobStatus status) { st == Status.CANCELED || st == Status.FAILED || st == Status.TERMINATED || + st == Status.ERROR || st == Status.PURGED) return true; return false; } - + + //fetches JSON object, deserializes the data and puts it to CyJobData @Override public CyJobStatus fetchResults(CyJob job, CyJobData data) { if (job instanceof ClusterJob) { + ClusterJob clusterJob = (ClusterJob) job; //handleCommand gives whatever HttpGET gives. - JSONObject result = handleCommand(clusterJob, Command.FETCH, null); //handles command FETCH --> argMap is null --> JSON object runs the command + JSONObject result = handleCommand(clusterJob, Command.FETCH, null); //handles command FETCH --> argMap is null --> JSON object runs the command + // System.out.println("fetchResults() JSONObject result: " + result); // Get the unserialized data, dataService deserializes the data (the JSON object), CyJobData is basically a HashMap - CyJobData newData = dataService.deserialize(result); - System.out.println("CyJobData results: " + newData.getAllValues()); + CyJobData newData = dataService.deserialize(result); + // System.out.println("CyJobData results: " + newData.getAllValues()); // Merge it in, move the information from newData to data - for (String key: newData.keySet()) { - data.put(key, newData.get(key)); - } - - Map clusterData = clusterJob.getClusterData().getAllValues(); - String clusterAttributeName = (String) clusterData.get("clusterAttributeName"); - CyNetwork network = (CyNetwork) clusterData.get("network"); - ClusterManager clusterManager = (ClusterManager) clusterData.get("clusterManager"); - Boolean createGroups = (Boolean) clusterData.get("createGroups"); - String group_attr = (String) clusterData.get("group_attr"); - List params = (List) clusterData.get("params"); - String shortName = (String) clusterData.get("shortName"); - List nodeClusters = createClusters(data, clusterAttributeName, network); //move this to remote utils - System.out.println("NodeClusters: " + nodeClusters); - - AbstractNetworkClusterer.createGroups(network, nodeClusters, group_attr, clusterAttributeName, - clusterManager, createGroups, params, shortName); + for (String key: newData.keySet()) { + data.put(key, newData.get(key)); + } CyJobStatus resultStatus = getStatus(result, null); if (resultStatus == null) @@ -258,19 +265,26 @@ public CyJobStatus fetchResults(CyJob job, CyJobData data) { return new CyJobStatus(Status.ERROR, "CyJob is not a ClusterJob"); //if not a clusterjob } + //returns a list of NodeCluster objects that have a number and a list of nodes belonging to it public static List createClusters(CyJobData data, String clusterAttributeName, CyNetwork network) { JSONArray partitions = (JSONArray) data.get("partitions"); + // System.out.println("Found "+partitions.size()+" partitions"); + + // Build a map of node names + Map nodeNameMap = new HashMap<>(); + for (CyNode cyNode: network.getNodeList()) { + String name = network.getRow(cyNode).get(CyNetwork.NAME, String.class); + nodeNameMap.put(name, cyNode); + } List nodeClusters = new ArrayList<>(); - int i = 1; + int i = 1; //each cluster is assigned a number for (Object partition : partitions) { + // System.out.println("Cluster "+i); List cluster = (ArrayList) partition; List cyNodes = new ArrayList<>(); for (String nodeName : cluster) { - for (CyNode cyNode : network.getNodeList()) - if (network.getRow(cyNode).get(CyNetwork.NAME, String.class).equals(nodeName)) { - cyNodes.add(cyNode); - } + cyNodes.add(nodeNameMap.get(nodeName)); } NodeCluster nodeCluster = new NodeCluster(i, cyNodes); @@ -325,18 +339,21 @@ public void saveJobInSession(CyJob job, File sessionFile) { } } - - + //compare f ex "done" and map that to the status ENUM //added return new CyJobStatus private CyJobStatus getStatus(JSONObject obj, String message) { - if (obj.containsKey(STATUS)) { + if (obj.containsKey(ERROR)) { + return new CyJobStatus(Status.ERROR, (String)obj.get(ERROR)); + } else if (obj.containsKey(STATUS)) { Status status = Status.UNKNOWN; if (obj.get(STATUS).equals("done")) { status = Status.FINISHED; } else if (obj.get(STATUS).equals("running")) { status = Status.RUNNING; + } else if (obj.get(STATUS).equals("error")) { + status = Status.ERROR; } // Did we get any information about our status? if (obj.containsKey(STATUS_MESSAGE)) { @@ -345,8 +362,6 @@ private CyJobStatus getStatus(JSONObject obj, String message) { return new CyJobStatus(status, message); } return new CyJobStatus(status, message); - } else if (obj.containsKey(ERROR)) { - return new CyJobStatus(Status.ERROR, (String)obj.get(ERROR)); } return null; } @@ -357,12 +372,13 @@ private JSONObject handleCommand(ClusterJob job, Command command, Map networkMap = new HashMap<>(); @@ -33,5 +44,6 @@ public void loadData(CyJob job, TaskMonitor monitor) { data.put("job", job); // Now we need to extract the network from the data CyNetwork network = job.getJobDataService().getNetworkData(data, "network"); - } + } + } diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/remoteUtils/DimensionalityReductionJobHandler.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/remoteUtils/DimensionalityReductionJobHandler.java new file mode 100644 index 0000000..70fb054 --- /dev/null +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/remoteUtils/DimensionalityReductionJobHandler.java @@ -0,0 +1,94 @@ +package edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils; + +import java.util.Map; + +import org.cytoscape.jobs.CyJob; +import org.cytoscape.jobs.CyJobData; +import org.cytoscape.jobs.CyJobStatus; +import org.cytoscape.model.CyColumn; +import org.cytoscape.model.CyNetwork; +import org.cytoscape.model.CyNode; +import org.cytoscape.model.CyTable; +import org.cytoscape.work.TaskMonitor; +import org.json.simple.JSONArray; + +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; +import edu.ucsf.rbvi.clusterMaker2.internal.ui.ScatterPlotDialog; + +public class DimensionalityReductionJobHandler extends ClusterJobHandler { + public boolean showScatterPlot = false; + + public DimensionalityReductionJobHandler(CyJob job, CyNetwork network, Boolean showScatterPlot) { + super(job, network); + this.showScatterPlot = showScatterPlot; + } + + @Override + public void loadData(CyJob job, TaskMonitor monitor) { + CyJobData data = job.getJobDataService().getDataInstance(); + CyJobStatus status = job.getJobExecutionService().fetchResults(job, data); + data.put("job", job); + //CyNetwork network = job.getJobDataService().getNetworkData(data, "network"); + CyNetwork network = networkMap.get(job); + System.out.println("network: " + network); + + // arranging the dimensionality reduction data into coordinates[] and nodes[] columns + CyTable nodeTable = network.getDefaultNodeTable(); + JSONArray embedding = (JSONArray) data.get("embedding"); //getting the relevant data from the data object + int size = embedding.size(); + + CyNode[] nodes = new CyNode[size-1]; + double[][] coordinates = new double[size-1][2]; + + for (int i = 1; i < size; i++) { + JSONArray nodeData = (JSONArray) embedding.get(i); + String nodeName = (String) nodeData.get(0); + + for (CyNode cyNode : network.getNodeList()) {// getting the cyNode object with the name of the node + if (network.getRow(cyNode).get(CyNetwork.NAME, String.class).equals(nodeName)) { + nodes[i-1] = cyNode; + + } + } + + double x = (Double) nodeData.get(1); + double y = (Double) nodeData.get(2); + coordinates[i-1][0] = x; + coordinates[i-1][1] = y; + } + + String newmapX = job.getJobName() + "_x"; + String newmapY = job.getJobName() + "_y"; + + Boolean columnExists = false; + for(CyColumn col : nodeTable.getColumns()) { + if (col.getName().equals(newmapX) || col.getName().equals(newmapY)) { + columnExists = true; + break; + } + } + + if (!columnExists) { + nodeTable.createColumn(newmapX, Double.class, false); + nodeTable.createColumn(newmapY, Double.class, false); + } + + + for (int i = 0; i < nodes.length; i++) { + if (nodes[i] != null) { + network.getRow(nodes[i]).set(newmapX, coordinates[i][0]); + // System.out.println("X value from the table : " + network.getRow(nodes[i]).get(newmapX, Double.class)); + network.getRow(nodes[i]).set(newmapY, coordinates[i][1]); + // System.out.println("Y value from the table : " + network.getRow(nodes[i]).get(newmapY, Double.class)); + } + } + + + Map clusterData = ((ClusterJob) job).getClusterData().getAllValues(); + if (showScatterPlot) { + ClusterManager manager = (ClusterManager) clusterData.get("manager"); + ScatterPlotDialog scatter = new ScatterPlotDialog(manager, job.getJobName(), null, nodes, coordinates); + } + } + +} diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/remoteUtils/NetworkClusterJobHandler.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/remoteUtils/NetworkClusterJobHandler.java new file mode 100644 index 0000000..d0e9075 --- /dev/null +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/remoteUtils/NetworkClusterJobHandler.java @@ -0,0 +1,68 @@ +package edu.ucsf.rbvi.clusterMaker2.internal.utils.remoteUtils; + +import java.util.List; +import java.util.Map; + +import org.cytoscape.jobs.CyJob; +import org.cytoscape.jobs.CyJobData; +import org.cytoscape.jobs.CyJobStatus; +import org.cytoscape.model.CyNetwork; +import org.cytoscape.work.SynchronousTaskManager; +import org.cytoscape.work.TaskIterator; +import org.cytoscape.work.TaskManager; +import org.cytoscape.work.TaskMonitor; + +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.NodeCluster; +import edu.ucsf.rbvi.clusterMaker2.internal.algorithms.networkClusterers.AbstractNetworkClusterer; +import edu.ucsf.rbvi.clusterMaker2.internal.api.ClusterManager; +import edu.ucsf.rbvi.clusterMaker2.internal.ui.NewNetworkView; + +public class NetworkClusterJobHandler extends ClusterJobHandler { + public boolean showUI; + public boolean restoreEdges; + + public NetworkClusterJobHandler(CyJob job, CyNetwork network, Boolean showUI, Boolean restoreEdges) { + super(job, network); + this.showUI = showUI; + this.restoreEdges = restoreEdges; + } + + @Override + public void loadData(CyJob job, TaskMonitor monitor) { + // System.out.println("Loading the data"); + CyJobData data = job.getJobDataService().getDataInstance(); + // System.out.println("Calling 'fetchResults'"); + CyJobStatus status = job.getJobExecutionService().fetchResults(job, data); + // System.out.println("done"); + data.put("job", job); + //CyNetwork network = job.getJobDataService().getNetworkData(data, "network"); + CyNetwork network = networkMap.get(job); + // System.out.println("network: " + network); + + // network clustering algorithm + + // System.out.println("Getting all values"); + Map clusterData = ((ClusterJob) job).getClusterData().getAllValues(); + // System.out.println("done"); + + String clusterAttributeName = (String) clusterData.get("clusterAttributeName"); + Boolean createGroups = (Boolean) clusterData.get("createGroups"); + String group_attr = (String) clusterData.get("group_attr"); + List params = (List) clusterData.get("params"); + ClusterManager clusterManager = (ClusterManager) clusterData.get("manager"); + + // System.out.println("Creating the clusters"); + List nodeClusters = ClusterJobExecutionService.createClusters(data, clusterAttributeName, network); //move this to remote utils + // System.out.println("NodeClusters: " + nodeClusters); + + AbstractNetworkClusterer.createGroups(network, nodeClusters, group_attr, clusterAttributeName, + clusterManager, createGroups, params, job.getJobName()); + + if (showUI) { + // System.out.println("Creating the new network view"); + TaskManager manager = clusterManager.getService(SynchronousTaskManager.class); + manager.execute(new TaskIterator(new NewNetworkView(network, clusterManager, true, restoreEdges, false))); + } + } + +} diff --git a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/remoteUtils/RemoteServer.java b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/remoteUtils/RemoteServer.java index e8cd85d..a17116b 100644 --- a/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/remoteUtils/RemoteServer.java +++ b/src/main/java/edu/ucsf/rbvi/clusterMaker2/internal/utils/remoteUtils/RemoteServer.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Scanner; import org.apache.http.HttpEntity; @@ -25,46 +26,39 @@ public class RemoteServer { static private String LOCAL_PATH = "http://localhost:8000/"; - static private String PROD_PATH = "http://webservices.rbvi.ucsf.edu/rest/api/v1/"; + static private String PROD_PATH = "https://webservices.rbvi.ucsf.edu/rest/api/v1/"; static public String getBasePath() { return PROD_PATH; } + // service is the shortname of the algorithm //make the choice between different clustering algorithms/servers, this works static public String getServiceURI(String service) { - - String server = PROD_PATH; - - if (service.equals("leiden")) { - return server + "service/leiden?objective_function=modularity&iterations=4"; - } else if (service.equals("fastgreedy")) { - return server + "service/fastgreedy"; - } else if (service.equals("infomap")) { - return server + "service/infomap"; - } else if (service.equals("labelpropagation")) { - return server + "service/labelpropagation"; - } else if (service.equals("leadingeigenvector")) { - return server + "service/leadingeigenvector"; - } else if (service.equals("multilevel")) { - return server + "service/multilevel"; - } else { - System.out.println("Unknown service"); - } - - return null; + return PROD_PATH + "service/" + service; + } + + static public String getServiceURIWithArgs(String service, Map args) { + String uri = getServiceURI(service) + '?'; + for (String key: args.keySet()) { + uri += key+"="+args.get(key)+'&'; + } + return uri.substring(0, uri.length()-1); + } + + //sends the data //you should use a JSONParser to parse the reply and return that. //the URI is the whole URI of the service //returns the status code and JSONObject (JobID) static public JSONObject postFile(String uri, JSONObject jsonData) throws Exception { - System.out.println("Posting on: " + uri); + // System.out.println("Posting on: " + uri); CloseableHttpClient httpClient = HttpClients.createDefault(); //client = browser --> executes in the default browser of my computer? CloseableHttpResponse response = getPOSTresponse(uri, jsonData, httpClient); - System.out.println("HttpPOST response: " + response.toString()); + // System.out.println("HttpPOST response: " + response.toString()); int statusCode = response.getStatusLine().getStatusCode(); if (statusCode != 200 && statusCode != 202) { @@ -106,17 +100,31 @@ static public JSONObject fetchJSON(String uri, Command command) throws Exception int statusCode = response.getStatusLine().getStatusCode(); if (statusCode != 200 && statusCode != 202) { + System.out.println("Status code not 200!"); return null; } HttpEntity entity = response.getEntity(); BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent())); - JSONObject json= new JSONObject(); + JSONObject json = null; if (command == Command.CHECK) { + System.out.println("Creating parser"); + JSONParser parser = new JSONParser(); + System.out.println("Parsing data"); + try { + json = (JSONObject) parser.parse(reader); + } catch (Exception e) { + e.printStackTrace(); + } + System.out.println("Status: "+json.toString()); + if (json.containsKey("status")) { + json.put("jobStatus", json.get("status")); + } + /* String line = ""; Object message = null; while ((line = reader.readLine()) != null) { - System.out.println(line); + System.out.println("fetchJSON: "+line); json.put("jobStatus", line); if (json.containsKey("message")) { message = json.get("message"); @@ -125,13 +133,12 @@ static public JSONObject fetchJSON(String uri, Command command) throws Exception json.put("message", line); } } + */ } else if (command == Command.FETCH) { JSONParser parser = new JSONParser(); json = (JSONObject) parser.parse(reader); } - - - + return json; //= dictionary, take it and poll from this the status key Map and for key status there is some answer, JSON similar to xml but easier } diff --git a/src/test/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/matrix/MatrixTest.java b/src/test/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/matrix/MatrixTest.java index 5e667d1..407c8b5 100644 --- a/src/test/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/matrix/MatrixTest.java +++ b/src/test/java/edu/ucsf/rbvi/clusterMaker2/internal/algorithms/matrix/MatrixTest.java @@ -308,6 +308,8 @@ private void initialize() { } coltMatrix2 = new ColtMatrix((SimpleMatrix)simpleMatrix2); ojAlgoMatrix2 = new OjAlgoMatrix((SimpleMatrix)simpleMatrix2); + + Matrix ojAlgoMatrixX = new OjAlgoMatrix(10,10); } private double sumByRows(Matrix mat) {