Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement connectTo #1149

Merged
merged 1 commit into from
Sep 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,17 @@ static JsonParser<SpansFromWindows> spansFromWindowsF(JsonParser<Expression<Wind
$ -> tuple(Unit.UNIT, $.expression()));
}

static JsonParser<SpansConnectTo> connectTo(JsonParser<Expression<Spans>> spansExpressionP) {
return productP
.field("kind", literalP("SpansExpressionConnectTo"))
.field("from", spansExpressionP)
.field("to", spansExpressionP)
.map(
untuple((kind, from, to) -> new SpansConnectTo(from, to)),
$ -> tuple(Unit.UNIT, $.from(), $.to())
);
}

private static final JsonParser<SpansInterval> spansIntervalP =
productP
.field("kind", literalP("SpansExpressionInterval"))
Expand All @@ -607,6 +618,7 @@ private static JsonParser<Expression<Spans>> spansExpressionF(JsonParser<Express
spansIntervalP,
startsF(selfP),
endsF(selfP),
connectTo(selfP),
shiftEdgesF(selfP),
splitF(selfP),
splitF(windowsP),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,30 @@ public boolean isStrictlyBefore(Interval x){
return compareEndToStart(this,x) < 0;
}

public int compareStarts(Interval other) {
final var timeComparison = this.start.compareTo(other.start);
if (timeComparison != 0) return timeComparison;
else if (this.startInclusivity == other.startInclusivity) return 0;
else if (this.startInclusivity == Inclusive) return -1;
else return 1;
}

public int compareEnds(Interval other) {
final var timeComparison = this.end.compareTo(other.end);
if (timeComparison != 0) return timeComparison;
else if (this.startInclusivity == other.startInclusivity) return 0;
else if (this.startInclusivity == Inclusive) return 1;
else return -1;
}

public int compareEndToStart(Interval other) {
final var timeComparison = this.end.compareTo(other.start);
if (timeComparison != 0) return timeComparison;
else if (this.endInclusivity != other.startInclusivity) return 0;
else if (this.endInclusivity == Inclusive) return 1;
else return -1;
}

public boolean contains(Duration d){
return !intersect(this, at(d)).isEmpty();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package gov.nasa.jpl.aerie.constraints.tree;

import gov.nasa.jpl.aerie.constraints.model.EvaluationEnvironment;
import gov.nasa.jpl.aerie.constraints.model.SimulationResults;
import gov.nasa.jpl.aerie.constraints.time.Interval;
import gov.nasa.jpl.aerie.constraints.time.Spans;
import gov.nasa.jpl.aerie.merlin.protocol.types.Duration;

import java.util.Set;
import java.util.stream.StreamSupport;

public record SpansConnectTo(Expression<Spans> from, Expression<Spans> to) implements Expression<Spans> {

@Override
public Spans evaluate(final SimulationResults results, final Interval bounds, final EvaluationEnvironment environment) {
final var from = this.from.evaluate(results, bounds, environment);
final var sortedFrom = StreamSupport.stream(from.spliterator(), true).sorted((l, r) -> l.interval().compareEnds(r.interval())).toList();
final var to = this.to.evaluate(results, bounds, environment);
final var sortedTo = StreamSupport.stream(to.spliterator(), true).sorted((l, r) -> l.interval().compareStarts(r.interval())).toList();
final var result = new Spans();
var toIndex = 0;
for (final var span: sortedFrom) {
final var startTime = span.interval().end;
while (toIndex < sortedTo.size() && span.interval().compareEndToStart(sortedTo.get(toIndex).interval()) == 1) {
toIndex++;
}
final Duration endTime;
final Interval.Inclusivity endInlusivity;
if (toIndex == sortedTo.size()) {
endTime = bounds.end;
endInlusivity = bounds.endInclusivity;
}
else {
endTime = sortedTo.get(toIndex).interval().start;
endInlusivity = Interval.Inclusivity.Inclusive;
}
result.add(
Interval.between(
startTime,
Interval.Inclusivity.Inclusive,
endTime,
endInlusivity
),
span.value()
);
}
return result;
}

@Override
public void extractResources(final Set<String> names) {
this.from.extractResources(names);
this.to.extractResources(names);
}

@Override
public String prettyPrint(final String prefix) {
return String.format(
"\n%s(connect from %s to %s)",
prefix,
this.from.prettyPrint(),
this.to.prettyPrint()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1127,6 +1127,68 @@ public void testShiftWindowsEdgesBoundsAdjustment() {
assertIterableEquals(expected2, result2);
}

@Test
void testSpansConnectTo() {
final var simResults = new SimulationResults(
Instant.EPOCH, Interval.between(0, Inclusive, 20, Exclusive, SECONDS),
List.of(),
Map.of(),
Map.of()
);

final var result = new SpansConnectTo(
Supplier.of(new Spans(
Interval.between(0, 1, SECONDS),
Interval.between(5, 7, SECONDS),
Interval.between(10, Inclusive, 11, Exclusive, SECONDS),
Interval.between(13, 14, SECONDS)
)),
Supplier.of(new Spans(
Interval.between(2, 3, SECONDS),
Interval.between(4, 6, SECONDS),
Interval.between(8, 9, SECONDS),
Interval.between(11, 12, SECONDS)
))
).evaluate(simResults);

final var expected = new Spans(
Interval.between(1, 2, SECONDS),
Interval.between(7, 8, SECONDS),
Interval.at(11, SECONDS),
Interval.between(14, Inclusive, 20, Exclusive, SECONDS)
);

assertIterableEquals(expected, result);
}

@Test
void testSpansConnectToMetadata() {
final var simResults = new SimulationResults(
Instant.EPOCH, Interval.between(0, Inclusive, 20, Exclusive, SECONDS),
List.of(),
Map.of(),
Map.of()
);

final var result = new SpansConnectTo(
Supplier.of(new Spans(
Segment.of(Interval.between(0, 1, SECONDS), Optional.of(new Spans.Metadata(new ActivityInstance(2, "2", Map.of(), FOREVER)))),
Segment.of(Interval.between(5, 7, SECONDS), Optional.empty())
)),
Supplier.of(new Spans(
Interval.between(2, 3, SECONDS),
Interval.between(8, 9, SECONDS)
))
).evaluate(simResults);

final var expected = new Spans(
Segment.of(Interval.between(1, 2, SECONDS), Optional.of(new Spans.Metadata(new ActivityInstance(2, "2", Map.of(), FOREVER)))),
Segment.of(Interval.between(7, 8, SECONDS), Optional.empty())
);

assertIterableEquals(expected, result);
}

@Test
public void testRollingThresholdExcess() {
final var simResults = new SimulationResults(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export enum NodeKind {
WindowsExpressionFromSpans = 'WindowsExpressionFromSpans',
SpansExpressionFromWindows = 'SpansExpressionFromWindows',
SpansExpressionSplit = 'SpansExpressionSplit',
SpansExpressionConnectTo = 'SpansExpressionConnectTo',
SpansExpressionInterval = 'SpansExpressionInterval',
SpansSelectWhenTrue = 'SpansSelectWhenTrue',
ExpressionEqual = 'ExpressionEqual',
Expand Down Expand Up @@ -122,8 +123,9 @@ export type SpansExpression =
| IntervalsExpressionShiftEdges
| SpansExpressionFromWindows
| ForEachActivitySpans
| SpansExpressionInterval
| SpansSelectWhenTrue;
| SpansSelectWhenTrue
| SpansExpressionConnectTo
| SpansExpressionInterval;

export interface SpansSelectWhenTrue {
kind: NodeKind.SpansSelectWhenTrue,
Expand Down Expand Up @@ -250,6 +252,12 @@ export interface SpansExpressionSplit {
internalEndInclusivity: API.Inclusivity
}

export interface SpansExpressionConnectTo {
kind: NodeKind.SpansExpressionConnectTo,
from: SpansExpression,
to: SpansExpression
}

export interface SpansExpressionInterval {
kind: NodeKind.SpansExpressionInterval,
interval: IntervalExpression
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,28 @@ export class Spans {
})
}

/**
* Connects the end of each of these spans to the start of the nearest span in the argument.
*
* This operation creates a new spans object. For each span `s` in `this`, it produces a span from
* the end of `s` to the start of the first span in `other` that occurs after the end of `s`.
*
* If `s` and the nearest subsequent span in `other` meet exactly, with no intersection and no
* space between them, a singleton span (containing exactly one time) is still created at the meeting point.
*
* If there are no spans in `other` that occur after `s`, a span is still created from the end of `s` until the
* end of the plan.
*
* @param other
*/
public connectTo(other: Spans): Spans {
return new Spans({
kind: AST.NodeKind.SpansExpressionConnectTo,
from: this.__astNode,
to: other.__astNode
})
}

/**
* Replaces each Span with its start point.
*/
Expand Down Expand Up @@ -1335,6 +1357,22 @@ declare global {
*/
public static FromInterval(interval: Interval): Spans;

/**
* Connects the end of each of these spans to the start of the nearest span in the argument.
*
* This operation creates a new spans object. For each span `s` in `this`, it produces a span from
* the end of `s` to the start of the first span in `other` that occurs after the end of `s`.
*
* If `s` and the nearest subsequent span in `other` meet exactly, with no intersection and no
* space between them, a singleton span (containing exactly one time) is still created at the meeting point.
*
* If there are no spans in `other` that occur after `s`, a span is still created from the end of `s` until the
* end of the plan.
*
* @param other
*/
public connectTo(other: Spans): Spans;

/**
* Returns the instantaneous start points of the these spans.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import gov.nasa.jpl.aerie.constraints.tree.ShiftBy;
import gov.nasa.jpl.aerie.constraints.tree.ShiftEdges;
import gov.nasa.jpl.aerie.constraints.tree.ShorterThan;
import gov.nasa.jpl.aerie.constraints.tree.SpansConnectTo;
import gov.nasa.jpl.aerie.constraints.tree.SpansFromWindows;
import gov.nasa.jpl.aerie.constraints.tree.SpansSelectWhenTrue;
import gov.nasa.jpl.aerie.constraints.tree.Split;
Expand Down Expand Up @@ -1378,4 +1379,25 @@ export default() => {
)
);
}
@Test
void testSpansConnectTo() {
checkSuccessfulCompilation(
"""
export default () => {
return Windows.Value(true).spans().connectTo(
Windows.Value(false).spans()
).windows();
}
""",
new ViolationsOfWindows(
new WindowsFromSpans(
new SpansConnectTo(
new SpansFromWindows(new WindowsValue(true)),
new SpansFromWindows(new WindowsValue(false))
)
)
)
);
}

}
Loading