From e6ae82f5ea27d96768c3d1148424b638db723a3c Mon Sep 17 00:00:00 2001 From: Ferdinando Villa Date: Sat, 14 Sep 2024 10:29:38 +0200 Subject: [PATCH] Strategies get back to the resolver --- .../objects}/ObservationStrategyImpl.java | 4 +- .../services/scopes/ServiceContextScope.java | 4 + .../services/scopes/ServiceSessionScope.java | 9 + .../reasoner/ObservationReasoner.java | 6 +- .../services/resolver/ResolverService.java | 205 +++++++++++------- .../klab/services/runtime/RuntimeService.java | 52 +++-- 6 files changed, 172 insertions(+), 108 deletions(-) rename {klab.services.reasoner/src/main/java/org/integratedmodelling/klab/services/reasoner => klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/resolver/objects}/ObservationStrategyImpl.java (95%) diff --git a/klab.services.reasoner/src/main/java/org/integratedmodelling/klab/services/reasoner/ObservationStrategyImpl.java b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/resolver/objects/ObservationStrategyImpl.java similarity index 95% rename from klab.services.reasoner/src/main/java/org/integratedmodelling/klab/services/reasoner/ObservationStrategyImpl.java rename to klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/resolver/objects/ObservationStrategyImpl.java index a3dc9b04..ea6e92f6 100644 --- a/klab.services.reasoner/src/main/java/org/integratedmodelling/klab/services/reasoner/ObservationStrategyImpl.java +++ b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/services/resolver/objects/ObservationStrategyImpl.java @@ -1,12 +1,10 @@ -package org.integratedmodelling.klab.services.reasoner; +package org.integratedmodelling.klab.api.services.resolver.objects; import org.integratedmodelling.klab.api.data.Metadata; import org.integratedmodelling.klab.api.knowledge.Observable; import org.integratedmodelling.klab.api.knowledge.ObservationStrategy; import org.integratedmodelling.klab.api.lang.Contextualizable; import org.integratedmodelling.klab.api.lang.kim.KimObservationStrategy; -import org.integratedmodelling.klab.api.scope.ContextScope; -import org.integratedmodelling.klab.api.services.Reasoner; import java.util.ArrayList; import java.util.List; diff --git a/klab.core.services/src/main/java/org/integratedmodelling/klab/services/scopes/ServiceContextScope.java b/klab.core.services/src/main/java/org/integratedmodelling/klab/services/scopes/ServiceContextScope.java index 2045a586..24d16a63 100644 --- a/klab.core.services/src/main/java/org/integratedmodelling/klab/services/scopes/ServiceContextScope.java +++ b/klab.core.services/src/main/java/org/integratedmodelling/klab/services/scopes/ServiceContextScope.java @@ -179,6 +179,10 @@ public ServiceContextScope withObserver(Observation observer) { @Override public Task observe(Observation observation) { + if (!isOperative()) { + return null; + } + long id = submitObservation(observation); // create task before resolution starts so we guarantee a response diff --git a/klab.core.services/src/main/java/org/integratedmodelling/klab/services/scopes/ServiceSessionScope.java b/klab.core.services/src/main/java/org/integratedmodelling/klab/services/scopes/ServiceSessionScope.java index f1c37633..0a796ec3 100644 --- a/klab.core.services/src/main/java/org/integratedmodelling/klab/services/scopes/ServiceSessionScope.java +++ b/klab.core.services/src/main/java/org/integratedmodelling/klab/services/scopes/ServiceSessionScope.java @@ -18,6 +18,7 @@ public class ServiceSessionScope extends ServiceUserScope implements SessionScope { private String name; + private boolean operative = true; public void setName(String name) { this.name = name; @@ -73,6 +74,14 @@ public void logout() { // TODO } + public boolean isOperative() { + return operative; + } + + public void setOperative(boolean operative) { + this.operative = operative; + } + public boolean initializeAgents(String scopeId) { // setting the ID here is dirty as technically this is still being set and will be set again later, // but diff --git a/klab.services.reasoner/src/main/java/org/integratedmodelling/klab/services/reasoner/ObservationReasoner.java b/klab.services.reasoner/src/main/java/org/integratedmodelling/klab/services/reasoner/ObservationReasoner.java index 0e64f5d0..4c831c7f 100644 --- a/klab.services.reasoner/src/main/java/org/integratedmodelling/klab/services/reasoner/ObservationReasoner.java +++ b/klab.services.reasoner/src/main/java/org/integratedmodelling/klab/services/reasoner/ObservationReasoner.java @@ -13,6 +13,7 @@ import org.integratedmodelling.klab.api.scope.ContextScope; import org.integratedmodelling.klab.api.services.Language; import org.integratedmodelling.klab.api.services.Reasoner; +import org.integratedmodelling.klab.api.services.resolver.objects.ObservationStrategyImpl; import org.integratedmodelling.klab.configuration.ServiceConfiguration; import org.integratedmodelling.klab.utilities.Utils; @@ -177,7 +178,6 @@ public List computeMatchingStrategies(Observation observati for (var operation : strategy.getOperations()) { var op = new ObservationStrategyImpl.OperationImpl(); - op.setType(operation.getType()); if (operation.getObservable() != null) { @@ -186,16 +186,12 @@ public List computeMatchingStrategies(Observation observati reasoner.declareObservable(operation.getObservable(), patternVariableValues)); } - for (var function : operation.getFunctions()) { op.getContextualizables().add(new ContextualizableImpl(function)); } - os.getOperations().add(op); } - ret.add(os); - } } diff --git a/klab.services.resolver/src/main/java/org/integratedmodelling/klab/services/resolver/ResolverService.java b/klab.services.resolver/src/main/java/org/integratedmodelling/klab/services/resolver/ResolverService.java index bc0f7989..a7d4783b 100644 --- a/klab.services.resolver/src/main/java/org/integratedmodelling/klab/services/resolver/ResolverService.java +++ b/klab.services.resolver/src/main/java/org/integratedmodelling/klab/services/resolver/ResolverService.java @@ -128,11 +128,7 @@ public Capabilities capabilities(Scope scope) { @Override public Dataflow resolve(Observation observation, ContextScope contextScope) { - - System.out.println("RISOLVIAMO STA MERDA " + observation); - var resolution = computeResolution(observation, contextScope); - if (!resolution.isEmpty()) { return compile(observation, resolution, contextScope); } @@ -177,77 +173,72 @@ public Resolution computeResolution(Observation observation, ContextScope scope) } -// /** -// * Top-level resolution, resolve and return an independent resolution graph. This creates a new resolution -// * graph which will contain any observations that were already resolved within the context observation in -// * the scope, if any. -// * -// * @param knowledge -// * @param scope -// * @return -// */ -// public Resolution computeResolution(Resolvable knowledge, ContextScope scope) { -// -// Geometry resolutionGeometry = Geometry.EMPTY; -// -// Resolvable observable = switch (knowledge) { -// case Concept concept -> Observable.promote(concept); -// case Model model -> model.getObservables().get(0); -// case Observable obs -> obs; -// case Observation observation -> { -// resolutionGeometry = observation.getGeometry(); -// yield observation.getObservable(); -// } -// default -> null; -// }; -// -// if (observable == null) { -// // FIXME this should just set the resolution to an error state and return it -// throw new KlabIllegalStateException("knowledge " + knowledge + " is not resolvable"); -// } -// -// if (scope.getContextObservation() != null) { -// resolutionGeometry = scope.getContextObservation().getGeometry(); -// } else if (resolutionGeometry.isEmpty() && scope.getObserver() != null) { -// resolutionGeometry = scope.getObserver().getObserverGeometry(); -// } -// -// var scale = Scale.create(resolutionGeometry); -// -// ResolutionImpl ret = new ResolutionImpl(observable, scale, scope); -// if (knowledge instanceof Model) { -// resolveModel((Model) knowledge, observable, scale, -// scope.withResolutionNamespace(((Model) knowledge).getNamespace()), -// ret); -// } else if (observable instanceof Observable obs) { -// resolveObservable(obs, scale, scope, ret, null); -// } // TODO the rest -// -// return ret; -// -// } + // /** + // * Top-level resolution, resolve and return an independent resolution graph. This creates a new + // resolution + // * graph which will contain any observations that were already resolved within the context + // observation in + // * the scope, if any. + // * + // * @param knowledge + // * @param scope + // * @return + // */ + // public Resolution computeResolution(Resolvable knowledge, ContextScope scope) { + // + // Geometry resolutionGeometry = Geometry.EMPTY; + // + // Resolvable observable = switch (knowledge) { + // case Concept concept -> Observable.promote(concept); + // case Model model -> model.getObservables().get(0); + // case Observable obs -> obs; + // case Observation observation -> { + // resolutionGeometry = observation.getGeometry(); + // yield observation.getObservable(); + // } + // default -> null; + // }; + // + // if (observable == null) { + // // FIXME this should just set the resolution to an error state and return it + // throw new KlabIllegalStateException("knowledge " + knowledge + " is not resolvable"); + // } + // + // if (scope.getContextObservation() != null) { + // resolutionGeometry = scope.getContextObservation().getGeometry(); + // } else if (resolutionGeometry.isEmpty() && scope.getObserver() != null) { + // resolutionGeometry = scope.getObserver().getObserverGeometry(); + // } + // + // var scale = Scale.create(resolutionGeometry); + // + // ResolutionImpl ret = new ResolutionImpl(observable, scale, scope); + // if (knowledge instanceof Model) { + // resolveModel((Model) knowledge, observable, scale, + // scope.withResolutionNamespace(((Model) knowledge).getNamespace()), + // ret); + // } else if (observable instanceof Observable obs) { + // resolveObservable(obs, scale, scope, ret, null); + // } // TODO the rest + // + // return ret; + // + // } private Coverage resolveObservation(Observation observation, Scale scale, ContextScope scope, - ResolutionImpl parent, - Model parentModel) { + ResolutionImpl parent, Model parentModel) { var observable = observation.getObservable(); Coverage ret = Coverage.create(scale, 0.0); - // infinite recursion is nice but wastes time - if (parent.checkResolving(observable)) { - return Coverage.universal(); - } - - // done already, nothing to do here - if (parent.getResolved(observable) != null) { + // observation may have been resolved already. Also it could be being resolved from upstream, and + // infinite recursion is fun but helps nobody. + if (observation.isResolved() || parent.checkResolving(observable)) { return Coverage.universal(); } - // see what the reasoner thinks of this observable for (ObservationStrategy strategy : - scope.getService(Reasoner.class).computeObservationStrategies(observation, - scope)) { + scope.getService(Reasoner.class).computeObservationStrategies(observation, scope)) { // this merges any useful strategy and returns the coverage ResolutionImpl resolution = resolveStrategy(strategy, scale, scope, parent, parentModel); ret = ret.merge(resolution.getCoverage(), LogicalConnector.UNION); @@ -267,18 +258,17 @@ private Coverage resolveObservation(Observation observation, Scale scale, Contex } - /** - * We always resolve an observable first. This only reports coverage as it does not directly create a - * resolution graph; this is done when resolving a model, which creates a graph and merges it with the - * parent graph if successful. - * - * @param observable - * @param parent - * @return - */ + /** + * We always resolve an observable first. This only reports coverage as it does not directly create a + * resolution graph; this is done when resolving a model, which creates a graph and merges it with the + * parent graph if successful. + * + * @param observable + * @param parent + * @return + */ private Coverage resolveObservable(Observable observable, Scale scale, ContextScope scope, - ResolutionImpl parent, - Model parentModel) { + ResolutionImpl parent, Model parentModel) { /** * Make graph merging parent Set coverage to scale, 0; Strategies/models: foreach model: @@ -340,7 +330,66 @@ private ResolutionImpl resolveStrategy(ObservationStrategy strategy, Scale scale ResolutionImpl parent, Model parentModel) { - Coverage coverage = Coverage.create(scale, 0.0); + var coverage = Coverage.create(scale, 0.0); + ResolutionImpl ret = null; + + for (var operation : strategy.getOperations()) { + switch (operation.getType()) { + case RESOLVE -> { + /* + Additional resolution for a different observable, have the runtime produce the + observation, if resolved we're done, otherwise invoke resolution recursively + */ + ret = new ResolutionImpl(operation.getObservable(), scale, scope, + parent); + // TODO have the runtime create the observation + // TODO resolve it and merge the resolution + } + case OBSERVE -> { + + /* + Observation is there, find models and compile them in, merge resolutions + */ + ret = new ResolutionImpl(operation.getObservable(), scale, scope, + parent); + for (Model model : queryModels(operation.getObservable(), scope, + scale)) { + ResolutionImpl resolution = resolveModel(model, operation.getObservable(), + scale, + scope.withResolutionNamespace(model.getNamespace()), parent); + coverage = coverage.merge(resolution.getCoverage(), LogicalConnector.UNION); + if (coverage.getGain() < MINIMUM_WORTHWHILE_CONTRIBUTION) { + continue; + } + // merge the model at root level within the local resolution + resolution.merge(model, coverage, operation.getObservable(), + ResolutionType.DIRECT); + if (coverage.isRelevant()) { + // merge the resolution with the parent resolution + ret.merge(parentModel, resolution, ResolutionType.DIRECT); + if (parent.getCoverage().isComplete()) { + break; + } + } + } + + } + case APPLY -> { + // resolve the contextualizers merging the necessary resource set, coverage is + // unchanged unless contextualizers are not available + } + } + + // add any deferrals to the compiled strategy node + if (!ret.isEmpty()) { + for (var deferral : operation.getContextualStrategies()) { + + } + } + } + + return ret; + // ResolutionImpl ret = new ResolutionImpl(strategy.getOriginalObservable(), scale, scope, // parent); // @@ -383,7 +432,7 @@ private ResolutionImpl resolveStrategy(ObservationStrategy strategy, Scale scale // } // // return ret; - return null; + // return null; } /** diff --git a/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/RuntimeService.java b/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/RuntimeService.java index 21c3273b..f49452dc 100644 --- a/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/RuntimeService.java +++ b/klab.services.runtime/src/main/java/org/integratedmodelling/klab/services/runtime/RuntimeService.java @@ -17,10 +17,12 @@ import org.integratedmodelling.klab.api.services.KlabService; import org.integratedmodelling.klab.api.services.Reasoner; import org.integratedmodelling.klab.api.services.Resolver; +import org.integratedmodelling.klab.api.services.ResourcesService; import org.integratedmodelling.klab.api.services.runtime.Dataflow; import org.integratedmodelling.klab.api.services.runtime.Message; import org.integratedmodelling.klab.api.services.runtime.Notification; import org.integratedmodelling.klab.api.services.runtime.extension.Library; +import org.integratedmodelling.klab.api.view.UI; import org.integratedmodelling.klab.configuration.ServiceConfiguration; import org.integratedmodelling.klab.services.ServiceStartupOptions; import org.integratedmodelling.klab.services.base.BaseService; @@ -204,19 +206,19 @@ public String registerSession(SessionScope sessionScope) { serviceSessionScope.getServices(RuntimeService.class).add(this); } - /* - register the session with all the necessary services - resolver and reasoner, the latter - can be substituted by our connected reasoner. TODO check if we need others. - - FIXME this doesn't add the client for the current service to the request. Need a way to - tell the client of each service that the request to register the session comes from a - service. - */ - for (var service : serviceSessionScope.getServices(Resolver.class)) { - service.registerSession(serviceSessionScope); - } - for (var service : serviceSessionScope.getServices(Reasoner.class)) { - service.registerSession(serviceSessionScope); + // all other services need to know the session we created + for (var serviceClass : List.of(Resolver.class, Reasoner.class, ResourcesService.class)) { + Thread.ofVirtual().start(() -> { + for (var service : serviceSessionScope.getServices(serviceClass)) { + // if things are OK, the service repeats the ID back + if (!service.registerSession(serviceSessionScope).equals(serviceSessionScope.getId())) { + serviceSessionScope.send(Notification.error("Error registering session with " + + serviceClass.getName() + ": session is inoperative", + UI.Interactivity.DISPLAY)); + serviceSessionScope.setOperative(false); + } + } + }); } return serviceSessionScope.getId(); @@ -238,17 +240,23 @@ public String registerContext(ContextScope contextScope) { serviceContextScope.getServices(RuntimeService.class).add(this); } - /* - register the context with all the necessary services - resolver and reasoner, the latter - can be substituted by our connected reasoner. TODO check if we need others. - */ - for (var service : serviceContextScope.getServices(Resolver.class)) { - service.registerContext(serviceContextScope); - } - for (var service : serviceContextScope.getServices(Reasoner.class)) { - service.registerContext(serviceContextScope); + // all other services need to know the context we created. TODO we may also need to + // register with the stats services and maybe any independent authorities + for (var serviceClass : List.of(Resolver.class, Reasoner.class, ResourcesService.class)) { + Thread.ofVirtual().start(() -> { + for (var service : serviceContextScope.getServices(serviceClass)) { + // if things are OK, the service repeats the ID back + if (!service.registerContext(serviceContextScope).equals(serviceContextScope.getId())) { + serviceContextScope.send(Notification.error("Error registering context with " + + serviceClass.getName() + ": context is inoperative", + UI.Interactivity.DISPLAY)); + serviceContextScope.setOperative(false); + } + } + }); } + return serviceContextScope.getId(); }