Skip to content

Commit

Permalink
weighted interventions for optimize (#4986)
Browse files Browse the repository at this point in the history
Co-authored-by: Cole Blanchard <cblanchard@Cole-Blanchards-MacBook-Pro.local>
  • Loading branch information
blanchco and Cole Blanchard authored Oct 2, 2024
1 parent 1474151 commit 62fc195
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 24 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<template>
<div>
<label v-if="label">{{ label }}</label>
<Dropdown :model-value="modelValue" :options="options" @update:model-value="updateValue">
<template #value="{ value }">
{{ value.toString() }}
</template>
</Dropdown>
<!-- Signal Bars -->
<ul>
<li v-for="n in numberOfBars" :key="n" :class="{ active: n <= modelValue }" :style="getBarStyle(n)" />
</ul>
</div>
</template>

<script setup lang="ts">
import Dropdown from 'primevue/dropdown';
import { ref } from 'vue';
const baseDelay = 0.03; // transition delay for each bar
const barWidth = 2; // width for each bar
const maxNumberOfBars = 15; // just setting a max number of bars arbitrarily
const props = defineProps({
modelValue: {
type: Number,
required: true
},
numberOfBars: {
type: Number,
default: 10
},
label: {
type: String,
default: ''
}
});
const emit = defineEmits(['update:modelValue']);
const previousValue = ref(props.modelValue);
const numberOfBars = Math.min(props.numberOfBars, maxNumberOfBars);
const options: number[] = Array.from({ length: numberOfBars + 1 }, (_, i) => i);
// fun styling for the bars :)
const getBarStyle = (n: number) => {
const isIncreasing = props.modelValue > previousValue.value;
const delay = isIncreasing
? (n - Math.min(props.modelValue, previousValue.value)) * baseDelay
: (Math.max(props.modelValue, previousValue.value) - n) * baseDelay;
// might be worth to make the height based on the nuyber of bars
return {
height: `${n * barWidth}px`,
transitionDelay: `${delay}s`
};
};
const updateValue = (value: number) => {
previousValue.value = props.modelValue;
emit('update:modelValue', value);
};
</script>

<style scoped>
div {
display: flex;
align-items: center;
gap: var(--gap-2);
}
ul {
display: flex;
align-items: flex-end;
gap: var(--gap-0-5);
list-style: none;
}
li {
width: 4px;
background-color: var(--surface-border);
border-radius: var(--border-radius);
transition:
height 0.5s ease,
background-color 0.5s ease;
&.active {
background-color: var(--primary-color);
}
}
/* static width for dropdown */
:deep(.p-dropdown) {
width: 5rem;
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export interface InterventionPolicyGroupForm {
lowerBoundValue: number;
upperBoundValue: number;
initialGuessValue: number;
isActive: boolean;
relativeImportance: number;
optimizationType: OptimizationInterventionObjective;
objectiveFunctionOption: InterventionObjectiveFunctions;
intervention: Intervention;
Expand Down Expand Up @@ -100,7 +100,7 @@ export const blankInterventionPolicyGroup: InterventionPolicyGroupForm = {
lowerBoundValue: 0,
upperBoundValue: 0,
initialGuessValue: 0,
isActive: true,
relativeImportance: 5,
optimizationType: OptimizationInterventionObjective.startTime,
objectiveFunctionOption: InterventionObjectiveFunctions.initialGuess,
intervention: blankIntervention
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
<template>
<div class="policy-group">
<div class="form-header">
<label class="mr-auto" tag="h5"> {{ config.intervention?.name ?? `Intervention` }}</label>
<aside>
<label for="active">Optimize</label>
<InputSwitch v-model="knobs.isActive" :disabled="true" @change="emit('update-self', knobs)" />
</aside>
<h6 class="mr-auto">{{ config.intervention?.name ?? `Intervention` }}</h6>
<tera-signal-bars
v-if="!!knobs.relativeImportance"
v-model="knobs.relativeImportance"
@update:model-value="emit('update-self', knobs)"
label="Relative importance"
/>
</div>
<p>
Set the {{ dynamicInterventions[0].type }}&nbsp; <strong>{{ dynamicInterventions[0].appliedTo }}</strong> to
Expand All @@ -15,10 +17,9 @@
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import { computed, ref } from 'vue';
import { DynamicIntervention } from '@/types/Types';
import { InterventionPolicyGroupForm } from '@/components/workflow/ops/optimize-ciemss/optimize-ciemss-operation';
import InputSwitch from 'primevue/inputswitch';

const props = defineProps<{
config: InterventionPolicyGroupForm;
Expand All @@ -29,7 +30,7 @@ const emit = defineEmits(['update-self']);
const dynamicInterventions = computed<DynamicIntervention[]>(() => props.config.intervention.dynamicInterventions);

const knobs = ref({
isActive: props.config.isActive ?? false
relativeImportance: props.config.relativeImportance
});
</script>
<style>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,9 +518,13 @@ const outputPanel = ref(null);
const chartSize = computed(() => drilldownChartSize(outputPanel.value));
const cancelRunId = computed(() => props.node.state.inProgressPostForecastId || props.node.state.inProgressOptimizeId);
const activePolicyGroups = computed(() => knobs.value.interventionPolicyGroups.filter((ele) => ele.isActive));
const activePolicyGroups = computed(() =>
knobs.value.interventionPolicyGroups.filter((ele) => !!ele.relativeImportance)
);
const inactivePolicyGroups = computed(() => knobs.value.interventionPolicyGroups.filter((ele) => !ele.isActive));
const inactivePolicyGroups = computed(() =>
knobs.value.interventionPolicyGroups.filter((ele) => !ele.relativeImportance)
);
let pyciemssMap: Record<string, string> = {};
const showSpinner = computed<boolean>(
Expand Down Expand Up @@ -716,7 +720,7 @@ const setInterventionPolicyGroups = (interventionPolicy: InterventionPolicy) =>
const newIntervention = _.cloneDeep(blankInterventionPolicyGroup);
newIntervention.id = interventionPolicy.id;
newIntervention.intervention = intervention;
newIntervention.isActive = !isNotActive;
newIntervention.relativeImportance = isNotActive ? 0 : 5;
newIntervention.startTimeGuess = intervention.staticInterventions[0]?.timestep;
newIntervention.initialGuessValue = intervention.staticInterventions[0]?.value;
knobs.value.interventionPolicyGroups.push(newIntervention);
Expand All @@ -739,13 +743,15 @@ const runOptimize = async () => {
const listBoundsInterventions: number[][] = [];
const initialGuess: number[] = [];
const objectiveFunctionOption: string[] = [];
const relativeImportance: number[] = [];
activePolicyGroups.value.forEach((ele) => {
// Only allowed to optimize on interventions that arent grouped aka staticInterventions' length is 1
paramNames.push(ele.intervention.staticInterventions[0].appliedTo);
paramValues.push(ele.intervention.staticInterventions[0].value);
startTime.push(ele.intervention.staticInterventions[0].timestep);
objectiveFunctionOption.push(ele.objectiveFunctionOption);
relativeImportance.push(ele.relativeImportance);
if (ele.optimizationType === OptimizationInterventionObjective.startTime) {
initialGuess.push(ele.startTimeGuess);
Expand Down Expand Up @@ -775,7 +781,8 @@ const runOptimize = async () => {
startTime,
paramValues,
initialGuess,
objectiveFunctionOption
objectiveFunctionOption,
relativeImportance
};
// These are interventions to be considered but not optimized over.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,6 @@ watch(
The following are the key attributes and findings of an optimization process for a ODE epidemilogy model, the goal is to find the best values or time points that satisfy a set of constraints.
- The succss constraints are, in JSON: ${JSON.stringify(state.constraintGroups)}
- We want to optimize: ${JSON.stringify(state.interventionPolicyGroups.filter((d) => d.isActive === true))}
- The fixed/static intervenations: ${JSON.stringify(state.interventionPolicyGroups.filter((d) => d.isActive === false))}
- The best guesses are: ${optimizationResult.x}
- The result is ${optimizationResult.success}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
<div class="policy-group">
<div class="form-header">
<h6 class="mr-auto">{{ config.intervention?.name ?? `Intervention` }}</h6>
<div>
<label for="active">Optimize</label>
<InputSwitch v-model="knobs.isActive" :disabled="isNotEditable" @change="emit('update-self', knobs)" />
</div>
<tera-signal-bars
v-if="staticInterventions.length === 1"
v-model="knobs.relativeImportance"
@update:model-value="emit('update-self', knobs)"
label="Relative importance"
/>
</div>
<template v-if="knobs.isActive">
<template v-if="staticInterventions.length === 1 && knobs.relativeImportance">
<section class="input-row">
<p>
Find the
Expand Down Expand Up @@ -120,7 +122,6 @@
<script setup lang="ts">
import Dropdown from 'primevue/dropdown';
import TeraInputNumber from '@/components/widgets/tera-input-number.vue';
import InputSwitch from 'primevue/inputswitch';
import { computed, ref, watch } from 'vue';
import { StaticIntervention } from '@/types/Types';
import {
Expand All @@ -129,6 +130,7 @@ import {
OPTIMIZATION_TYPE_MAP,
OBJECTIVE_FUNCTION_MAP
} from '@/components/workflow/ops/optimize-ciemss/optimize-ciemss-operation';
import TeraSignalBars from '@/components/widgets/tera-signal-bars.vue';

const props = defineProps<{
config: InterventionPolicyGroupForm;
Expand All @@ -142,8 +144,6 @@ const knobs = ref<InterventionPolicyGroupForm>({
...props.config
});

const isNotEditable = computed(() => staticInterventions.value.length !== 1);

const showStartTimeOptions = computed(
() =>
knobs.value.optimizationType === OptimizationInterventionObjective.startTime ||
Expand Down
1 change: 1 addition & 0 deletions packages/client/hmi-client/src/types/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,7 @@ export interface OptimizeInterventions {
startTime?: number[];
objectiveFunctionOption?: string[];
initialGuess?: number[];
relativeImportance?: number[];
}

export interface OptimizeQoi {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import com.fasterxml.jackson.annotation.JsonAlias;
import java.util.List;
import java.util.stream.Collectors;
import lombok.Data;
import lombok.experimental.Accessors;
import software.uncharted.terarium.hmiserver.annotations.TSIgnore;
import software.uncharted.terarium.hmiserver.annotations.TSModel;
import software.uncharted.terarium.hmiserver.annotations.TSOptional;

Expand Down Expand Up @@ -36,6 +38,10 @@ public class OptimizeInterventions {
@JsonAlias("initial_guess")
private List<Double> initialGuess;

@TSOptional
@JsonAlias("relative_importance")
private List<Double> relativeImportance;

@Override
public String toString() {
return (" { Parameter Names: " + this.paramNames + " start time: " + startTime.toString() + " } ");
Expand Down

0 comments on commit 62fc195

Please sign in to comment.