Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
David Legg committed Oct 30, 2024
1 parent d442c50 commit b20c1b4
Show file tree
Hide file tree
Showing 10 changed files with 99 additions and 37 deletions.
Original file line number Diff line number Diff line change
@@ -1,53 +1,35 @@
package gov.nasa.jpl.aerie.contrib.streamline.core;

import gov.nasa.jpl.aerie.contrib.streamline.utils.InvertibleFunction;
import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue;

import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

import static gov.nasa.jpl.aerie.contrib.streamline.core.InitialConditionManager.*;

/**
* Combines the operations of getting initial state and saving final state,
* since these operations are intentionally closely coupled.
*/
public interface InconBehavior<T> {
T getIncon(InitialConditionManager.InitialConditions initialConditions);
T getIncon(InitialConditions initialConditions);

void writeFincon(T state, InitialConditionManager.FinalConditions finalConditions);
void writeFincon(T state, FinalConditions finalConditions);

static <T> InconBehavior<T> of(Function<InitialConditionManager.InitialConditions, T> getIncon, BiConsumer<T, InitialConditionManager.FinalConditions> writeFincon) {
return new InconBehavior<T>() {
static <T> InconBehavior<T> of(Function<InitialConditions, T> getIncon, BiConsumer<T, FinalConditions> writeFincon) {
return new InconBehavior<>() {
@Override
public T getIncon(InitialConditionManager.InitialConditions initialConditions) {
public T getIncon(InitialConditions initialConditions) {
return getIncon.apply(initialConditions);
}

@Override
public void writeFincon(T state, InitialConditionManager.FinalConditions finalConditions) {
public void writeFincon(T state, FinalConditions finalConditions) {
writeFincon.accept(state, finalConditions);
}
};
}

/**
* The null case of incon behavior, when in fact the state does not get saved out.
*/
static <T> InconBehavior<T> constant(T value) {
return InconBehavior.of($ -> value, (s, f) -> {});
}

/**
* The standard case of incon behavior, where the state is serialized out under a single key.
*/
static <T> InconBehavior<T> serialized(String key, Function<Optional<SerializedValue>, T> constructor, Function<T, SerializedValue> serializer) {
return InconBehavior.of(
incons -> constructor.apply(incons.get(key)),
(state, fincons) -> fincons.put(key, serializer.apply(state)));
}

/**
* Extend this {@link InconBehavior} with an invertible function.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,14 @@ static <D> InconBehavior<ErrorCatching<Expiring<D>>> notSaving(D initialValue) {
}

static <D> InconBehavior<ErrorCatching<Expiring<D>>> notSaving(ErrorCatching<Expiring<D>> initialValue) {
return InconBehavior.constant(initialValue);
return InconBehavior.of($ -> initialValue, (s, f) -> {});
}

// TODO - It would be nice if the name we set here could somehow auto-populate the name of the resource,
// and also be the name we register the resource as. Same for the value mapper, it would be nice to just use that for registration too.
// Alternatively, we could demand a name for every MutableResource, and combine that with the other info here later...?
// On reflection, I think the discrete resource and linear resource constructors are the place to combine all this info.
// Those would know which registrar method to call, and what value mapper to use.
static <D> InconBehavior<ErrorCatching<Expiring<D>>> serializing(String key, D defaultValue, ValueMapper<D> mapper) {
return serializing(key, pure(defaultValue), standardDynamicsMapper(mapper));
}
Expand Down Expand Up @@ -131,10 +133,9 @@ public Result<ErrorCatching<Expiring<D>>, String> deserializeValue(SerializedVal
}

static <D> InconBehavior<ErrorCatching<Expiring<D>>> serializing(String key, ErrorCatching<Expiring<D>> defaultValue, ValueMapper<ErrorCatching<Expiring<D>>> mapper) {
return InconBehavior.serialized(
key,
serializedValue -> serializedValue.map($ -> mapper.deserializeValue($).getSuccessOrThrow()).orElse(defaultValue),
mapper::serializeValue);
return InconBehavior.of(
incons -> incons.get(key).map($ -> mapper.deserializeValue($).getSuccessOrThrow()).orElse(defaultValue),
(state, fincons) -> fincons.put(key, mapper.serializeValue(state)));
}

static <D extends Dynamics<?, D>> void set(MutableResource<D> resource, D newDynamics) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.util.Optional;

import static gov.nasa.jpl.aerie.contrib.streamline.core.MutableResource.notSaving;
import static gov.nasa.jpl.aerie.contrib.streamline.core.Resources.currentValue;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.black_box.Approximation.approximate;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.black_box.DifferentiableResources.asDifferentiable;
Expand Down Expand Up @@ -130,7 +131,7 @@ public final class Demo {
Resource<Linear> approxQuotient = approximate(quotient, secantApproximation(IntervalFunctions.<Unstructured<Double>>byBoundingError(
1e-6, Duration.SECOND, Duration.HOUR.times(24), errorByOptimization())));

Resource<Unstructured<Pair<Vector3D, Vector3D>>> positionAndVelocity = resource(Unstructured.timeBased(t -> /* some spice call */ null));
Resource<Unstructured<Pair<Vector3D, Vector3D>>> positionAndVelocity = resource(notSaving(Unstructured.timeBased(t -> /* some spice call */ null)));
Resource<Discrete<Pair<Vector3D, Vector3D>>> approxPosVel = approximate(
positionAndVelocity,
DiscreteApproximation.<Pair<Vector3D, Vector3D>, Unstructured<Pair<Vector3D, Vector3D>>>discreteApproximation(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import java.util.function.BiFunction;
import java.util.function.Function;

import static gov.nasa.jpl.aerie.contrib.streamline.core.MutableResource.notSaving;
import static gov.nasa.jpl.aerie.contrib.streamline.core.MutableResource.resource;
import static gov.nasa.jpl.aerie.contrib.streamline.core.Expiring.expiring;
import static gov.nasa.jpl.aerie.contrib.streamline.core.Reactions.whenever;
Expand All @@ -32,7 +33,9 @@ private Approximation() {}
*/
public static <D extends Dynamics<?, D>, E extends Dynamics<?, E>> Resource<E> approximate(
Resource<D> resource, Function<Expiring<D>, Expiring<E>> approximation) {
var result = resource(resource.getDynamics().map(approximation));
// We should, in general, not care about saving the state of an approximation.
// Instead, we should restore the underlying resource and re-approximate
var result = resource(notSaving(resource.getDynamics().map(approximation)));
// Register the "updates" and "expires" conditions separately
// so that the "updates" condition isn't triggered spuriously.
wheneverUpdates(resource, newResourceDynamics -> updateApproximation(newResourceDynamics, approximation, result));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package gov.nasa.jpl.aerie.contrib.streamline.modeling.clocks;

import gov.nasa.jpl.aerie.contrib.streamline.core.Dynamics;
import gov.nasa.jpl.aerie.merlin.framework.annotations.AutoValueMapper;
import gov.nasa.jpl.aerie.merlin.protocol.types.Duration;

@AutoValueMapper.Record
public record Clock(Duration extract) implements Dynamics<Duration, Clock> {
@Override
public Clock step(Duration t) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package gov.nasa.jpl.aerie.contrib.streamline.modeling.clocks;

import gov.nasa.jpl.aerie.contrib.streamline.core.MutableResource;
import gov.nasa.jpl.aerie.contrib.streamline.core.monads.ResourceMonad;
import gov.nasa.jpl.aerie.contrib.streamline.modeling.discrete.Discrete;
import gov.nasa.jpl.aerie.contrib.streamline.core.Resource;
import gov.nasa.jpl.aerie.contrib.streamline.modeling.linear.Linear;
import gov.nasa.jpl.aerie.merlin.protocol.types.Duration;

import static gov.nasa.jpl.aerie.contrib.streamline.core.MutableResource.resource;
import static gov.nasa.jpl.aerie.contrib.streamline.core.Expiring.*;
import static gov.nasa.jpl.aerie.contrib.streamline.core.MutableResource.*;
import static gov.nasa.jpl.aerie.contrib.streamline.core.Resources.signalling;
import static gov.nasa.jpl.aerie.contrib.streamline.core.monads.ResourceMonad.bind;
import static gov.nasa.jpl.aerie.contrib.streamline.modeling.discrete.Discrete.discrete;
Expand All @@ -23,8 +22,8 @@ private ClockResources() {}
/**
* Create a clock starting at zero time.
*/
public static Resource<Clock> clock() {
return resource(Clock.clock(ZERO));
public static Resource<Clock> clock(String name) {
return resource(serializing(name, Clock.clock(ZERO), null /* TODO - get auto value mapper for Clock type */));
}

public static Resource<Discrete<Boolean>> lessThan(Resource<Clock> clock, Resource<Discrete<Duration>> threshold) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package gov.nasa.jpl.aerie.contrib.streamline.modeling.clocks;

import gov.nasa.jpl.aerie.contrib.streamline.core.Dynamics;
import gov.nasa.jpl.aerie.merlin.framework.annotations.AutoValueMapper;
import gov.nasa.jpl.aerie.merlin.protocol.types.Duration;

import java.time.Instant;
Expand All @@ -12,6 +13,7 @@
* A variation on {@link Clock} that represents an absolute {@link Instant}
* instead of a relative {@link Duration}.
*/
@AutoValueMapper.Record
public record InstantClock(Instant extract) implements Dynamics<Instant, InstantClock> {
@Override
public InstantClock step(Duration t) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package gov.nasa.jpl.aerie.contrib.streamline.modeling.clocks;

import gov.nasa.jpl.aerie.contrib.streamline.core.Dynamics;
import gov.nasa.jpl.aerie.merlin.framework.annotations.AutoValueMapper;
import gov.nasa.jpl.aerie.merlin.protocol.types.Duration;

import static gov.nasa.jpl.aerie.merlin.protocol.types.Duration.ZERO;

@AutoValueMapper.Record
public record VariableClock(Duration extract, int multiplier) implements Dynamics<Duration, VariableClock> {
@Override
public VariableClock step(final Duration t) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package gov.nasa.jpl.aerie.contrib.streamline.modeling.clocks;

import gov.nasa.jpl.aerie.contrib.streamline.core.Dynamics;
import gov.nasa.jpl.aerie.merlin.framework.annotations.AutoValueMapper;
import gov.nasa.jpl.aerie.merlin.protocol.types.Duration;

import java.time.Instant;
Expand All @@ -11,6 +12,7 @@
* A variation on {@link VariableClock} that represents an absolute {@link Instant}
* instead of a relative {@link Duration}.
*/
@AutoValueMapper.Record
public record VariableInstantClock(Instant extract, int multiplier) implements Dynamics<Instant, VariableInstantClock> {
@Override
public VariableInstantClock step(Duration t) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import gov.nasa.jpl.aerie.contrib.streamline.core.*;
import gov.nasa.jpl.aerie.contrib.streamline.core.CellRefV2.CommutativityTestInput;
import gov.nasa.jpl.aerie.contrib.streamline.modeling.Registrar;
import gov.nasa.jpl.aerie.contrib.streamline.modeling.clocks.Clock;
import gov.nasa.jpl.aerie.contrib.streamline.modeling.discrete.monads.DiscreteDynamicsMonad;
import gov.nasa.jpl.aerie.contrib.streamline.modeling.discrete.monads.DiscreteMonad;
Expand All @@ -11,6 +12,8 @@
import gov.nasa.jpl.aerie.contrib.streamline.unit_aware.Unit;
import gov.nasa.jpl.aerie.contrib.streamline.unit_aware.UnitAware;
import gov.nasa.jpl.aerie.contrib.streamline.unit_aware.UnitAwareResources;
import gov.nasa.jpl.aerie.merlin.framework.ValueMapper;
import gov.nasa.jpl.aerie.merlin.protocol.model.EffectTrait;
import gov.nasa.jpl.aerie.merlin.protocol.types.Duration;

import java.time.Instant;
Expand All @@ -24,6 +27,7 @@
import static gov.nasa.jpl.aerie.contrib.streamline.core.Expiring.expiring;
import static gov.nasa.jpl.aerie.contrib.streamline.core.Expiry.expiry;
import static gov.nasa.jpl.aerie.contrib.streamline.core.MutableResource.resource;
import static gov.nasa.jpl.aerie.contrib.streamline.core.MutableResource.serializing;
import static gov.nasa.jpl.aerie.contrib.streamline.core.Reactions.every;
import static gov.nasa.jpl.aerie.contrib.streamline.core.Reactions.whenever;
import static gov.nasa.jpl.aerie.contrib.streamline.core.Resources.*;
Expand All @@ -46,6 +50,68 @@ public static <T> Resource<Discrete<T>> constant(T value) {
return result;
}

public static <T> DiscreteResourceBuilder<T> discreteResource(T initialValue) {

}

public class DiscreteResourceBuilder<T> {
private ErrorCatching<Expiring<Discrete<T>>> defaultValue;
private String name;
private ValueMapper<T> valueMapper;
private InconBehavior<ErrorCatching<Expiring<Discrete<T>>>> inconBehavior;
private EffectTrait<DynamicsEffect<Discrete<T>>> effectTrait = autoEffects();

public DiscreteResourceBuilder<T> defaultValue(T defaultValue) {
return defaultValue(DiscreteDynamicsMonad.pure(defaultValue));
}

public DiscreteResourceBuilder<T> defaultValue(ErrorCatching<Expiring<Discrete<T>>> defaultValue) {
assertNotSet("default value", this.defaultValue);
this.defaultValue = defaultValue;
return this;
}

public DiscreteResourceBuilder<T> name(String name) {
assertNotSet("name", this.name);
this.name = name;
return this;
}

public DiscreteResourceBuilder<T> valueMapper(ValueMapper<T> valueMapper) {
assertNotSet("value mapper", this.valueMapper);
this.valueMapper = valueMapper;
return this;
}

public DiscreteResourceBuilder<T> inconBehavior(InconBehavior<ErrorCatching<Expiring<Discrete<T>>>> inconBehavior) {
assertNotSet("incon behavior", this.inconBehavior);
this.inconBehavior = inconBehavior;
return this;
}

private void assertNotSet(String name, Object thing) {
if (thing != null)
throw new IllegalStateException(String.format("%s has already been set on this builder!", name));
}

// Terminal methods - these build and return the resource

// TODO - would it be more convenient to make Registrar a singleton, like the incon manager?
// Then it wouldn't have to be passed around just to give to these builders.
// We already assume you have exactly one registrar, because building it initializes all the singletons...
public Resource<Discrete<T>> registered(Registrar registrar) {
var result = notRegistered();
registrar.discrete(name, result, valueMapper);
return result;
}

public Resource<Discrete<T>> notRegistered() {
return resource(inconBehavior, effectTrait);
}
}

// --- REWRITE START ---

// General discrete cell resource constructor
public static <T> MutableResource<Discrete<T>> discreteResource(T initialValue) {
return resource(discrete(initialValue));
Expand All @@ -66,6 +132,8 @@ public static MutableResource<Discrete<Double>> discreteResource(double initialV
input.rightResult().extract()))));
}

// --- REWRITE END ---

/**
* Returns a condition that's satisfied whenever this resource is true.
*/
Expand Down

0 comments on commit b20c1b4

Please sign in to comment.