/** * Let's add temporal constraints to the formula whenever the formula is activated: * * <p>duration = end - start; * * <p>start >= origin; * * <p>end {@literal <=} horizon; * * <p>duration {@literal >=} 0; * * <p>We also constraint the amount to be greater than or equal to 0: * * <p>amount {@literal >=} 0; * * <p>If the formula is a charge formula, we also add a constraint that forces the c_amount to be * less or equal to the amount: * * <p>c_amount {@literal <=} amount; * * <p>c_amount {@literal >=} 0; * * @param formula the formula that has been created. */ @Override public void formulaActivated(IFormula formula) { IConstraintNetwork network = solver.getConstraintNetwork(); final INumber start = formula.get(Constants.START); final INumber end = formula.get(Constants.END); final INumber duration = formula.get(Constants.DURATION); final INumber amount = formula.get(AMOUNT); final INumber origin = solver.get(Constants.ORIGIN); final INumber horizon = solver.get(Constants.HORIZON); if (formula.getType().getName().equals(CHARGE_PREDICATE_NAME)) { final INumber c_amount = formula.get(C_AMOUNT); network.assertFacts( network.eq(duration, network.subtract(end, start)), network.geq(start, origin), network.leq(end, horizon), network.geq(duration, network.newReal("0")), network.geq(amount, network.newReal("0")), network.leq(c_amount, amount), network.geq(c_amount, network.newReal("0"))); } else { network.assertFacts( network.eq(duration, network.subtract(end, start)), network.geq(start, origin), network.leq(end, horizon), network.geq(duration, network.newReal("0")), network.geq(amount, network.newReal("0"))); } if (!lazy_scheduling) { throw new UnsupportedOperationException( "Eager scheduling for batteries is not supported yet.."); } }
@Override public List<IBool> checkConsistency(IModel model) { if (!lazy_scheduling) { return Collections.emptyList(); } else { List<IBool> constraints = new ArrayList<>(); IConstraintNetwork network = solver.getConstraintNetwork(); final Map<IObject, Collection<IFormula>> formulas = getFormulas(model); IStaticCausalGraph causal_graph = solver.getStaticCausalGraph(); if (!closed_batteries && solver .getCurrentNode() .getFlaws() .stream() .map( flaw -> { if (flaw instanceof IGoal) { return causal_graph.getNode(((IGoal) flaw).getFormula().getType()); } else if (flaw instanceof IFact) { return causal_graph.getNode(((IFact) flaw).getFormula().getType()); } else if (flaw instanceof IDisjunctionFlaw) { return causal_graph.getNode(((IDisjunctionFlaw) flaw).getDisjunction()); } else if (flaw instanceof IPreferenceFlaw) { return causal_graph.getNode(((IPreferenceFlaw) flaw).getPreference()); } else { throw new AssertionError( "Flaw " + flaw.getClass().getName() + " is supported yet.."); } }) .noneMatch( node -> causal_graph.existsPath(node, causal_graph.getNode(charge_predicate)) || causal_graph.existsPath( node, causal_graph.getNode(consume_predicate)))) { // We need a resolver in order to re-open the resource when backtracking solver .getCurrentNode() .addResolver( new IResolver() { private boolean resolved = false; @Override public double getKnownCost() { return 0; } @Override public void resolve() { assert !resolved; // Let's close the batteries closed_batteries = true; resolved = true; } @Override public boolean isResolved() { return resolved; } @Override public void retract() { assert resolved; closed_batteries = false; resolved = false; } }); } if (closed_batteries) { instances.forEach( battery -> { Collection<IFormula> c_formulas = formulas.get(battery); BatteryTimeline timeline = new BatteryTimeline(solver, model, battery, c_formulas); for (int i = 0; i < timeline.values.size(); i++) { // <editor-fold defaultstate="collapsed" desc="battery overcharge"> if (model.evaluate( network.gt(timeline.values.get(i).max_amount, timeline.capacity))) { // We have a battery overcharge so we need to anticipate consumptions to charges Collection<IFormula> good_charges = new ArrayList<>(c_formulas.size()); Collection<IFormula> good_consumptions = new ArrayList<>(c_formulas.size()); for (IFormula f : c_formulas) { switch (f.getType().getName()) { case CHARGE_PREDICATE_NAME: if (model.evaluate( network.leq( (INumber) f.get(Constants.START), network.newReal(timeline.pulses.get(i).toString())))) { // Charges that affect current overcharge are all those that start before // this timeline value good_charges.add(f); } break; case CONSUME_PREDICATE_NAME: if (model.evaluate( network.geq( (INumber) f.get(Constants.END), network.newReal(timeline.pulses.get(i + 1).toString())))) { // Consumptions that might resolve the current overcharge are all those // that end after this timeline value good_consumptions.add(f); } break; default: throw new AssertionError(f.getType().getName()); } } List<IBool> or = new ArrayList<>(good_charges.size() * good_consumptions.size()); good_consumptions.forEach( (cons) -> { good_charges.forEach( (charge) -> { or.add( network.leq( cons.get(Constants.END), charge.get(Constants.START))); or.add(network.not(cons.getScope().eq(charge.getScope()))); }); }); or.add(network.geq(battery.get(CAPACITY), timeline.values.get(i).max_amount)); constraints.add(network.or(or.toArray(new IBool[or.size()]))); } // </editor-fold> // <editor-fold defaultstate="collapsed" desc="battery overconsumption"> if (model.evaluate( network.lt(timeline.values.get(i).min_amount, network.newReal("0")))) { // We have a battery overconsumption so we need to anticipate charges to // consumption Collection<IFormula> good_charges = new ArrayList<>(c_formulas.size()); Collection<IFormula> good_consumptions = new ArrayList<>(c_formulas.size()); for (IFormula f : c_formulas) { switch (f.getType().getName()) { case CHARGE_PREDICATE_NAME: if (model.evaluate( network.geq( (INumber) f.get(Constants.END), network.newReal(timeline.pulses.get(i + 1).toString())))) { // Charges that might resolve the current overconsumption are all those // that end after this timeline value good_charges.add(f); } break; case CONSUME_PREDICATE_NAME: if (model.evaluate( network.leq( (INumber) f.get(Constants.START), network.newReal(timeline.pulses.get(i).toString())))) { // Consumptions that affect current overconsumption are all those that // start before this timeline value good_consumptions.add(f); } break; default: throw new AssertionError(f.getType().getName()); } } List<IBool> or = new ArrayList<>(good_charges.size() * good_consumptions.size()); good_consumptions.forEach( (cons) -> { good_charges.forEach( (charge) -> { or.add( network.leq( charge.get(Constants.END), cons.get(Constants.START))); or.add(network.not(charge.getScope().eq(cons.getScope()))); }); }); or.add(network.leq(network.newReal("0"), timeline.values.get(i).min_amount)); constraints.add(network.or(or.toArray(new IBool[or.size()]))); } // </editor-fold> } if (timeline.values.isEmpty() ? model.evaluate(network.not(timeline.initial_amount.eq(timeline.final_amount))) : model.evaluate( network.not( timeline .values .get(timeline.values.size() - 1) .final_amount .eq(timeline.final_amount)))) { // The initial amount plus the sum of charges and consumptions is not equal to the // final amount final List<INumber> sum = new ArrayList<>(c_formulas.size() + 1); sum.add(battery.get(INITIAL_AMOUNT)); sum.addAll( c_formulas .stream() .map( f -> { switch (f.getType().getName()) { case CHARGE_PREDICATE_NAME: return f.get(C_AMOUNT); case CONSUME_PREDICATE_NAME: return network.negate((INumber) f.get(AMOUNT)); default: throw new AssertionError(f.getType().getName()); } }) .collect(Collectors.toList())); final List<IBool> or = new ArrayList<>(); c_formulas.forEach( formula -> { or.add(network.not(battery.eq(formula.getScope()))); }); instances .stream() .filter(instance -> (instance != battery)) .forEach( instance -> { formulas .get(instance) .forEach( formula -> { or.add(battery.eq(formula.getScope())); }); }); if (sum.size() == 1) { or.add(network.eq(sum.get(0), (INumber) battery.get(FINAL_AMOUNT))); } else { or.add( network.eq( network.add(sum.toArray(new INumber[sum.size()])), (INumber) battery.get(FINAL_AMOUNT))); } constraints.add(network.or(or.toArray(new IBool[or.size()]))); } }); } return constraints; } }
@Override public void goalCreated(IFormula goal) { goal.getType().applyRule(goal); }
@Override public void factCreated(IFormula fact) { fact.getType().applyRule(fact); }