-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds the InconBehavior interface and some of the surrounding machinery to connect it. The thrust of this idea is to wire together incon reading, fincon writing, and cell creation, such that the easiest way for a modeler to create a new cell is to correctly wire up the incons. This means that wiring up those incons should be relatively painless, it should minimize duplicating necessary information, it should eliminate specifying any unnecessary information, and side-stepping the incons process should require rebuilding a large portion of the resource stack, as a strong discouragement to doing so. This commit does not compile. The status of various classes is: 1. I'm mostly happy with InconBehavior itself. It's a neatly packaged bit of behavior, it composes well (see InconBehavior.map), and I think it's legible as-is. 2. I may want to remove InconBehaviors static constructors "constant" and "serialized". I think those belong in MutableResource, as part of the "notSaving" and "serializing" helper methods instead. 3. I'm not happy with MutableResource yet, as we're now duplicating information for the incon behavior (name and ValueMapper) that we'll give later if/when we register it. I'm wondering if we should roll registration into this method as well, perhaps with a flag or alternate constructor to not register a resource... 4. Everywhere we build MutableResources needs to be updated, pending what we decide to do for the step above. Additionally, we'll need to document how this changes the MutableResource interface and include that in the migration notes should this be adopted by Aerie.
- Loading branch information
David Legg
committed
Oct 30, 2024
1 parent
6d30dd1
commit d442c50
Showing
5 changed files
with
300 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
62 changes: 62 additions & 0 deletions
62
contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/core/InconBehavior.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
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; | ||
|
||
/** | ||
* 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); | ||
|
||
void writeFincon(T state, InitialConditionManager.FinalConditions finalConditions); | ||
|
||
static <T> InconBehavior<T> of(Function<InitialConditionManager.InitialConditions, T> getIncon, BiConsumer<T, InitialConditionManager.FinalConditions> writeFincon) { | ||
return new InconBehavior<T>() { | ||
@Override | ||
public T getIncon(InitialConditionManager.InitialConditions initialConditions) { | ||
return getIncon.apply(initialConditions); | ||
} | ||
|
||
@Override | ||
public void writeFincon(T state, InitialConditionManager.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> | ||
* In particular, this can be used to apply {@link Resource} wrappers around the initial state. | ||
* </p> | ||
*/ | ||
default <U> InconBehavior<U> map(InvertibleFunction<T, U> f) { | ||
return InconBehavior.of( | ||
f.compose(this::getIncon), | ||
(state, fincons) -> this.writeFincon(f.inverse().apply(state), fincons)); | ||
} | ||
} |
76 changes: 76 additions & 0 deletions
76
...rib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/core/InitialConditionManager.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package gov.nasa.jpl.aerie.contrib.streamline.core; | ||
|
||
import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue; | ||
|
||
import java.util.*; | ||
import java.util.function.Consumer; | ||
|
||
public final class InitialConditionManager { | ||
private InitialConditionManager() {} | ||
|
||
private static boolean initialized = false; | ||
private static InitialConditions initialConditions; | ||
private static Consumer<Map<String, SerializedValue>> finconHandler; | ||
private static List<Consumer<FinalConditions>> finconHooks; | ||
|
||
public static void init(InitialConditions initialConditions, Consumer<Map<String, SerializedValue>> finconHandler) { | ||
if (initialized) { | ||
throw new IllegalStateException("InitialConditionManager has already been initialized"); | ||
} | ||
|
||
InitialConditionManager.initialConditions = initialConditions; | ||
InitialConditionManager.finconHandler = finconHandler; | ||
InitialConditionManager.finconHooks = new ArrayList<>(); | ||
initialized = true; | ||
} | ||
|
||
// The "correct" way to get an initial value also registers a way to write the final value. | ||
// This is intended to remind the modeler that these operations are closely coupled. | ||
public static <T> T register(InconBehavior<T> behavior) { | ||
if (!initialized) { | ||
throw new IllegalStateException("InitialConditionManager has not been initialized"); | ||
} | ||
|
||
var result = behavior.getIncon(initialConditions); | ||
// This looks admittedly strange, but the intent is for result to be something like a resource, | ||
// which is a stable handle for a state that changes over the course of the simulation. | ||
// This closure captures that stable handle, with the intent of querying it later to capture the final state. | ||
finconHooks.add($ -> behavior.writeFincon(result, $)); | ||
return result; | ||
} | ||
|
||
public static void writeFincon() { | ||
var transparentFincons = new HashMap<String, SerializedValue>(); | ||
// opaqueFincons is a write-only view of transparentFincons | ||
var opaqueFincons = FinalConditions.of(transparentFincons); | ||
// Each fincon hook appends its portion of the fincons | ||
for (var finconHook : finconHooks) { | ||
finconHook.accept(opaqueFincons); | ||
} | ||
// Finally we write the full object out | ||
finconHandler.accept(transparentFincons); | ||
} | ||
|
||
|
||
public interface InitialConditions { | ||
Optional<SerializedValue> get(String key); | ||
|
||
static InitialConditions of(Map<String, SerializedValue> map) { | ||
return key -> Optional.ofNullable(map.get(key)); | ||
} | ||
} | ||
|
||
public interface FinalConditions { | ||
void put(String key, SerializedValue value); | ||
|
||
static FinalConditions of(Map<String, SerializedValue> map) { | ||
return (key, value) -> { | ||
if (map.putIfAbsent(key, value) != null) { | ||
throw new IllegalStateException(String.format( | ||
"Final condition has already been written for %s", key)); | ||
} | ||
}; | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
39 changes: 39 additions & 0 deletions
39
contrib/src/main/java/gov/nasa/jpl/aerie/contrib/streamline/utils/InvertibleFunction.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package gov.nasa.jpl.aerie.contrib.streamline.utils; | ||
|
||
import java.util.function.Function; | ||
|
||
public interface InvertibleFunction<A, B> extends Function<A, B> { | ||
InvertibleFunction<B, A> inverse(); | ||
|
||
default <C> InvertibleFunction<C, B> compose(InvertibleFunction<C, A> before) { | ||
return new InvertibleFunction<>() { | ||
@Override | ||
public InvertibleFunction<B, C> inverse() { | ||
return before.inverse().<B>compose(InvertibleFunction.this.inverse()); | ||
} | ||
|
||
@Override | ||
public B apply(C c) { | ||
return InvertibleFunction.this.apply(before.apply(c)); | ||
} | ||
}; | ||
} | ||
|
||
default <C> InvertibleFunction<A, C> andThen(InvertibleFunction<B, C> after) { | ||
return after.compose(this); | ||
} | ||
|
||
static <A, B> InvertibleFunction<A, B> of(Function<A, B> map, Function<B, A> inverse) { | ||
return new InvertibleFunction<>() { | ||
@Override | ||
public B apply(A a) { | ||
return map.apply(a); | ||
} | ||
|
||
@Override | ||
public InvertibleFunction<B, A> inverse() { | ||
return InvertibleFunction.of(inverse, map); | ||
} | ||
}; | ||
} | ||
} |