001/*
002 * Copyright (c) 2015-2020, Oracle and/or its affiliates. All rights reserved.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.tribuo.anomaly;
018
019import com.oracle.labs.mlrg.olcut.provenance.Provenance;
020import org.tribuo.ImmutableOutputInfo;
021import org.tribuo.MutableOutputInfo;
022import org.tribuo.OutputFactory;
023import org.tribuo.anomaly.Event.EventType;
024import org.tribuo.anomaly.evaluation.AnomalyEvaluation;
025import org.tribuo.anomaly.evaluation.AnomalyEvaluator;
026import org.tribuo.evaluation.Evaluator;
027import org.tribuo.provenance.OutputFactoryProvenance;
028
029import java.util.Map;
030
031/**
032 * A factory for generating events.
033 */
034public final class AnomalyFactory implements OutputFactory<Event> {
035    private static final long serialVersionUID = 1L;
036
037    /**
038     * The unknown event. Used at inference time.
039     */
040    public static final Event UNKNOWN_EVENT = new Event(EventType.UNKNOWN);
041
042    /**
043     * The expected event. Used for things which are not anomalous.
044     */
045    public static final Event EXPECTED_EVENT = new Event(EventType.EXPECTED);
046
047    /**
048     * The anomalous event. It's anomalous.
049     */
050    public static final Event ANOMALOUS_EVENT = new Event(EventType.ANOMALOUS);
051
052    private static final AnomalyEvaluator evaluator = new AnomalyEvaluator();
053
054    @Override
055    public <V> Event generateOutput(V label) {
056        if (label.toString().equalsIgnoreCase(EventType.ANOMALOUS.toString())) {
057            return ANOMALOUS_EVENT;
058        } else {
059            return EXPECTED_EVENT;
060        }
061    }
062
063    @Override
064    public Event getUnknownOutput() {
065        return UNKNOWN_EVENT;
066    }
067
068    @Override
069    public MutableOutputInfo<Event> generateInfo() {
070        return new MutableAnomalyInfo();
071    }
072
073    @Override
074    public ImmutableOutputInfo<Event> constructInfoForExternalModel(Map<Event,Integer> mapping) {
075        // Validate inputs are dense
076        OutputFactory.validateMapping(mapping);
077
078        Integer expectedMapping = mapping.get(EXPECTED_EVENT);
079        Integer anomalousMapping = mapping.get(ANOMALOUS_EVENT);
080
081        if (((expectedMapping != null) && (expectedMapping != EventType.EXPECTED.getID())) ||
082        ((anomalousMapping != null) && anomalousMapping != EventType.ANOMALOUS.getID())){
083            throw new IllegalArgumentException("Anomaly detection requires that anomalous events have id " + EventType.ANOMALOUS.getID() + ", and expected events have id " + EventType.EXPECTED.getID());
084        }
085
086        MutableAnomalyInfo info = new MutableAnomalyInfo();
087        return info.generateImmutableOutputInfo();
088    }
089
090    @Override
091    public Evaluator<Event, AnomalyEvaluation> getEvaluator() {
092        return evaluator;
093    }
094
095    @Override
096    public OutputFactoryProvenance getProvenance() {
097        return new AnomalyFactoryProvenance();
098    }
099
100    /**
101     * Provenance for {@link AnomalyFactory}.
102     */
103    public final static class AnomalyFactoryProvenance implements OutputFactoryProvenance {
104        private static final long serialVersionUID = 1L;
105
106        /**
107         * Constructs an anomaly factory provenance.
108         */
109        AnomalyFactoryProvenance() {}
110
111        /**
112         * Constructs an anomaly factory provenance from the marshalled form.
113         * @param map An empty map.
114         */
115        public AnomalyFactoryProvenance(Map<String, Provenance> map) { }
116
117        @Override
118        public String getClassName() {
119            return AnomalyFactory.class.getName();
120        }
121
122        @Override
123        public String toString() {
124            return generateString("OutputFactory");
125        }
126
127        @Override
128        public boolean equals(Object other) {
129            return other instanceof AnomalyFactoryProvenance;
130        }
131
132        @Override
133        public int hashCode() {
134            return 31;
135        }
136    }
137}