/* * The MIT License * * Copyright 2020 The OpenNARS authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "Inference.h" #include "Term.h" #define DERIVATION_STAMP(a,b) Stamp conclusionStamp = Stamp_make(&a->stamp, &b->stamp); \ long creationTime = MAX(a->creationTime, b->creationTime); #define DERIVATION_STAMP_AND_TIME(a,b) DERIVATION_STAMP(a,b) \ long conclusionTime = b->occurrenceTime; \ Truth truthA = Truth_Projection(a->truth, a->occurrenceTime, conclusionTime); \ Truth truthB = b->truth; static double weighted_average(double a1, double a2, double w1, double w2) { return (a1*w1+a2*w2)/(w1+w2); } //{Event a., Event b.} |- Event (&/,a,b). Event Inference_BeliefIntersection(Event *a, Event *b, bool *success) { assert(b->occurrenceTime >= a->occurrenceTime, "after(b,a) violated in Inference_BeliefIntersection"); DERIVATION_STAMP_AND_TIME(a,b) Term conclusionTerm = Narsese_Sequence(&a->term, &b->term, success); return *success ? (Event) { .term = conclusionTerm, .type = EVENT_TYPE_BELIEF, .truth = Truth_Intersection(truthA, truthB), .stamp = conclusionStamp, .occurrenceTime = conclusionTime, .creationTime = creationTime } : (Event) {0}; } //{Event a., Event b., after(b,a)} |- Implication b>. Implication Inference_BeliefInduction(Event *a, Event *b, bool *success) { assert(b->occurrenceTime > a->occurrenceTime, "after(b,a) violated in Inference_BeliefInduction"); DERIVATION_STAMP_AND_TIME(a,b) Term term = {0}; term.atoms[0] = Narsese_AtomicTermIndex("$"); *success = Term_OverrideSubterm(&term, 1, &a->term) && Term_OverrideSubterm(&term, 2, &b->term); return *success ? (Implication) { .term = term, .truth = Truth_Eternalize(Truth_Induction(truthB, truthA)), .stamp = conclusionStamp, .occurrenceTimeOffset = b->occurrenceTime - a->occurrenceTime, .creationTime = creationTime } : (Implication) {0}; } //{Event a., Event a.} |- Event a. //{Event a!, Event a!} |- Event a! static Event Inference_EventRevision(Event *a, Event *b) { DERIVATION_STAMP_AND_TIME(a,b) return (Event) { .term = a->term, .type = a->type, .truth = Truth_Revision(truthA, truthB), .stamp = conclusionStamp, .occurrenceTime = conclusionTime, .creationTime = creationTime }; } //{Implication b>., b>.} |- Implication b>. Implication Inference_ImplicationRevision(Implication *a, Implication *b) { DERIVATION_STAMP(a,b) double occurrenceTimeOffsetAvg = weighted_average(a->occurrenceTimeOffset, b->occurrenceTimeOffset, Truth_c2w(a->truth.confidence), Truth_c2w(b->truth.confidence)); return (Implication) { .term = a->term, .truth = Truth_Revision(a->truth, b->truth), .stamp = conclusionStamp, .occurrenceTimeOffset = occurrenceTimeOffsetAvg, .sourceConcept = a->sourceConcept, .sourceConceptId = a->sourceConceptId, .creationTime = creationTime, .isUserKnowledge = a->isUserKnowledge || b->isUserKnowledge }; } //{Event b!, Implication b>.} |- Event a! Event Inference_GoalDeduction(Event *component, Implication *compound) { assert(Narsese_copulaEquals(compound->term.atoms[0],'$'), "Not a valid implication term!"); DERIVATION_STAMP(component,compound) Term precondition = Term_ExtractSubterm(&compound->term, 1); //extract precondition: (plus unification once vars are there) return (Event) { .term = Narsese_GetPreconditionWithoutOp(&precondition), .type = EVENT_TYPE_GOAL, .truth = Truth_Deduction(compound->truth, component->truth), .stamp = conclusionStamp, .occurrenceTime = component->occurrenceTime - compound->occurrenceTimeOffset, .creationTime = creationTime }; } //{Event a.} |- Event a. updated to currentTime Event Inference_EventUpdate(Event *ev, long currentTime) { Event ret = *ev; ret.truth = Truth_Projection(ret.truth, ret.occurrenceTime, currentTime); ret.occurrenceTime = currentTime; return ret; } //{Event (&/,a,op())!, Event a.} |- Event op()! Event Inference_OperationDeduction(Event *compound, Event *component, long currentTime) { DERIVATION_STAMP(component,compound) Event compoundUpdated = Inference_EventUpdate(compound, currentTime); Event componentUpdated = Inference_EventUpdate(component, currentTime); return (Event) { .term = compound->term, .type = EVENT_TYPE_GOAL, .truth = Truth_Deduction(compoundUpdated.truth, componentUpdated.truth), .stamp = conclusionStamp, .occurrenceTime = compound->occurrenceTime, .creationTime = creationTime }; } //{Event a!, Event a!} |- Event a! (revision and choice) Event Inference_RevisionAndChoice(Event *existing_potential, Event *incoming_spike, long currentTime, bool *revised) { if(revised != NULL) { *revised = false; } if(existing_potential->type == EVENT_TYPE_DELETED) { return *incoming_spike; } else { double confExisting = Inference_EventUpdate(existing_potential, currentTime).truth.confidence; double confIncoming = Inference_EventUpdate(incoming_spike, currentTime).truth.confidence; //check if there is evidental overlap bool overlap = Stamp_checkOverlap(&incoming_spike->stamp, &existing_potential->stamp); //if there is or the terms aren't equal, apply choice, keeping the stronger one: if(overlap || (existing_potential->occurrenceTime != OCCURRENCE_ETERNAL && existing_potential->occurrenceTime != incoming_spike->occurrenceTime) || !Term_Equal(&existing_potential->term, &incoming_spike->term)) { if(confIncoming > confExisting) { return *incoming_spike; } } else //and else revise, increasing the "activation potential" { Event revised_spike = Inference_EventRevision(existing_potential, incoming_spike); if(revised_spike.truth.confidence >= existing_potential->truth.confidence) { if(revised != NULL) { *revised = true; } return revised_spike; } //lower, also use choice if(confIncoming > confExisting) { return *incoming_spike; } } } return *existing_potential; } //{Event a., Implication b>.} |- Event b. Event Inference_BeliefDeduction(Event *component, Implication *compound) { assert(Narsese_copulaEquals(compound->term.atoms[0],'$'), "Not a valid implication term!"); DERIVATION_STAMP(component,compound) Term postcondition = Term_ExtractSubterm(&compound->term, 2); return (Event) { .term = postcondition, .type = EVENT_TYPE_BELIEF, .truth = Truth_Deduction(compound->truth, component->truth), .stamp = conclusionStamp, .occurrenceTime = component->occurrenceTime == OCCURRENCE_ETERNAL ? OCCURRENCE_ETERNAL : component->occurrenceTime + compound->occurrenceTimeOffset, .creationTime = creationTime }; }