<commit_before>[[constraintStreams]]
= Constraint Streams Score Calculation
:doctype: book
:imagesdir: ..
:sectnums:
:toc: left
:icons: font
:experimental:


Constraint streams are an Functional Programming form of incremental score calculation in plain Java 8+
that is fast, scalable and debuggable.

[[constraintStreamsIntroduction]]
== Introduction

Using Java 8's Streams API, we could implement an <<easyJavaScoreCalculation,easy score calculator>>
that uses a functional approach:

[source,java,options="nowrap"]
----
List<Talk> talkList = schedule.getTalkList();
talkList.stream()
        .filter(talk -> talk.hasAnyUnavailableSpeaker())
        .forEach(talk -> penalize(talk.getDurationInMinutes()));
----

However, that scales poorly because it doesn't do an <<incrementalScoreCalculation,incremental calculation>>.
When the planning variable of a single `Talk` changes, to recalculate the score,
the normal Streams API has to execute the entire stream from scratch.
So it doesn't do <<incrementalScoreCalculation,deltas>>.
Nor does it support <<explainingTheScore,justifications>>.

The ConstraintStreams API does do <<incrementalScoreCalculation,deltas>> and support <<explainingTheScore,justifications>>.

*Constraints stream will be implemented soon.*
<commit_msg>Constraint streams docs: introduction<commit_after>[[constraintStreams]]
= Constraint Streams Score Calculation
:doctype: book
:imagesdir: ..
:sectnums:
:toc: left
:icons: font
:experimental:


Constraint streams are an Functional Programming form of incremental score calculation in plain Java 8+
that is fast, scalable and debuggable.
The API should feel familiar if you've worked with Java 8 Streams or SQL.

[WARNING]
====
The ConstraintStreams/ConstraintProvider API is TECH PREVIEW.
It works but it has many API gaps.
Therefore, it is not rich enough yet to handle complex constraints.
====

[[constraintStreamsIntroduction]]
== Introduction

Using Java 8's Streams API, we could implement an <<easyJavaScoreCalculation,easy score calculator>>
that uses a functional approach:

[source,java,options="nowrap"]
----
    private int speakerUnavailableTimeslot() {
        int softScore = 0;
        schedule.getTalkList().stream()
                .filter(Talk::hasAnyUnavailableSpeaker)
                .forEach(talk -> {
                    softScore -= talk.getDurationInMinutes();
                });
        return softScore;
    }
----

However, that scales poorly because it doesn't do an <<incrementalScoreCalculation,incremental calculation>>:
When the planning variable of a single `Talk` changes, to recalculate the score,
the normal Streams API has to execute the entire stream from scratch.
So it doesn't do <<incrementalScoreCalculation,deltas>>.
Nor does it support <<explainingTheScore,justifications>>.

On the other hand, the ConstraintStreams API uses <<incrementalScoreCalculation,deltas>>
and supports <<explainingTheScore,justifications>>:

[source,java,options="nowrap"]
----
    private Constraint speakerUnavailableTimeslot(ConstraintFactory factory) {
        return factory.from(Talk.class)
                .filter(Talk::hasAnyUnavailableSpeaker)
                .penalizeConfigurable("Speaker unavailable timeslot",
                        HardSoftScore.ONE_SOFT,
                        Talk::getDurationInMinutes);
    }
----

{'fraction_non_alphanumeric': 0.09112709832134293, 'fraction_numerical': 0.0017985611510791368, 'mean_word_length': 4.330670926517572, 'pattern_counts': {'":': 0, '<': 28, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '379967', 'n_tokens_mistral': 926, 'n_tokens_neox': 848, 'n_words': 306}