/* * 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 "Decision.h" double DECISION_THRESHOLD = DECISION_THRESHOLD_INITIAL; double ANTICIPATION_THRESHOLD = ANTICIPATION_THRESHOLD_INITIAL; double ANTICIPATION_CONFIDENCE = ANTICIPATION_CONFIDENCE_INITIAL; double MOTOR_BABBLING_CHANCE = MOTOR_BABBLING_CHANCE_INITIAL; //Inject action event after execution or babbling void Decision_Execute(Decision *decision) { assert(decision->operationID > 0, "Operation 0 is reserved for no action"); decision->op = operations[decision->operationID-1]; //and add operator feedback if(decision->arguments.atoms[0] > 0) //operation with args { Term operation = {0}; operation.atoms[0] = Narsese_AtomicTermIndex(":"); // ^op> if(!Term_OverrideSubterm(&operation, 1, &decision->arguments) || !Term_OverrideSubterm(&operation, 2, &decision->op.term)) { return; } NAR_AddInputBelief(operation); } else //atomic operation / operator { NAR_AddInputBelief(decision->op.term); } (*decision->op.action)(decision->arguments); } //"reflexes" to try different operations, especially important in the beginning static Decision Decision_MotorBabbling() { Decision decision = (Decision) {0}; int n_ops = 0; for(int i=0; i 0) { decision.operationID = 1+(myrand() % (n_ops)); IN_DEBUG ( printf(" NAR BABBLE %d\n", decision.operationID); ) decision.execute = true; } return decision; } static Decision Decision_ConsiderImplication(long currentTime, Event *goal, int considered_opi, Implication *imp) { Decision decision = (Decision) {0}; IN_DEBUG ( printf("CONSIDERED IMPLICATION: impTruth=(%f, %f)", imp->truth.frequency, imp->truth.confidence); Narsese_PrintTerm(&imp->term); puts(""); ) //now look at how much the precondition is fulfilled Concept *prec = imp->sourceConcept; Event *precondition = &prec->belief_spike; //a. :|: if(precondition != NULL) { Event ContextualOperation = Inference_GoalDeduction(goal, imp); //(&/,a,op())! :\: double operationGoalTruthExpectation = Truth_Expectation(Inference_OperationDeduction(&ContextualOperation, precondition, currentTime).truth); //op()! :|: IN_DEBUG ( printf("CONSIDERED PRECON: desire=%f ", operationGoalTruthExpectation); Narsese_PrintTerm(&prec->term); fputs("\nCONSIDERED PRECON truth ", stdout); Truth_Print(&precondition->truth); fputs("CONSIDERED goal truth ", stdout); Truth_Print(&goal->truth); fputs("CONSIDERED imp truth ", stdout); Truth_Print(&imp->truth); printf("CONSIDERED time %ld\n", precondition->occurrenceTime); Narsese_PrintTerm(&precondition->term); puts(""); ) //<(precon &/ ^op>) =/> postcon>. -> [$ , postcon precon : _ _ _ _ args ^op Term operation = Term_ExtractSubterm(&imp->term, 4); //^op or [: args ^op] if(!Narsese_isOperator(operation.atoms[0])) //it is an operation with args, not just an atomic operator, so remember the args { assert(Narsese_isOperator(operation.atoms[2]), "If it's not atomic, it needs to be an operation with args here"); decision.arguments = Term_ExtractSubterm(&imp->term, 9); } decision.operationID = considered_opi; decision.desire = operationGoalTruthExpectation; } return decision; } int stampID = -1; Decision Decision_BestCandidate(Concept *goalconcept, Event *goal, long currentTime) { Decision decision = (Decision) {0}; Implication bestImp = {0}; long bestComplexity = COMPOUND_TERM_SIZE_MAX+1; Concept *cbest_predicate = NULL; Decision decisionGeneral = (Decision) {0}; Implication bestImpGeneral = {0}; long bestComplexityGeneral = COMPOUND_TERM_SIZE_MAX+1; Concept *cbest_predicateGeneral = NULL; Substitution subs = Variable_Unify(&goalconcept->term, &goal->term); if(subs.success) { for(int opi=1; opi<=OPERATIONS_MAX && operations[opi-1].action != 0; opi++) { for(int j=0; jprecondition_beliefs[opi].itemsAmount; j++) { if(!Memory_ImplicationValid(&goalconcept->precondition_beliefs[opi].array[j])) { Table_Remove(&goalconcept->precondition_beliefs[opi], j--); continue; } Implication imp = goalconcept->precondition_beliefs[opi].array[j]; bool impHasVariable = Variable_hasVariable(&imp.term, true, true, true); bool success; imp.term = Variable_ApplySubstitute(imp.term, subs, &success); if(success) { assert(Narsese_copulaEquals(imp.term.atoms[0], '$'), "This should be an implication!"); Term left_side_with_op = Term_ExtractSubterm(&imp.term, 1); Term left_side = Narsese_GetPreconditionWithoutOp(&left_side_with_op); //might be something like <#1 --> a> for(int cmatch_k=0; cmatch_kterm, true, true, true)) { Substitution subs2 = Variable_Unify(&left_side, &cmatch->term); if(subs2.success) { Implication specific_imp = imp; //can only be completely specific bool success; specific_imp.term = Variable_ApplySubstitute(specific_imp.term, subs2, &success); if(success && !Variable_hasVariable(&specific_imp.term, true, true, true)) { specific_imp.sourceConcept = cmatch; specific_imp.sourceConceptId = cmatch->id; Decision considered = Decision_ConsiderImplication(currentTime, goal, opi, &specific_imp); int specific_imp_complexity = Term_Complexity(&specific_imp.term); if(impHasVariable) { if(considered.desire > decisionGeneral.desire || (considered.desire == decisionGeneral.desire && specific_imp_complexity < bestComplexityGeneral)) { decisionGeneral = considered; cbest_predicateGeneral = cmatch; bestComplexityGeneral = specific_imp_complexity; bestImpGeneral = imp; } } else { if(considered.desire > decision.desire || (considered.desire == decision.desire && specific_imp_complexity < bestComplexity)) { decision = considered; decision.specialized = true; cbest_predicate = cmatch; bestComplexity = specific_imp_complexity; bestImp = imp; } } } } } } } } } } //use general solution only if the specific solution doesn't exceed the threshold if(decisionGeneral.desire > decision.desire && decision.desire < DECISION_THRESHOLD) { decision = decisionGeneral; cbest_predicate = cbest_predicateGeneral; bestImp = bestImpGeneral; } if(decision.desire < DECISION_THRESHOLD) { return (Decision) {0}; } //increase usefulness assert(cbest_predicate != NULL, "Above decision threshold but postcondition concept is NULL!"); cbest_predicate->usage = Usage_use(cbest_predicate->usage, currentTime, false); Term subject_with_op = Term_ExtractSubterm(&bestImp.term, 1); Term subject = Narsese_GetPreconditionWithoutOp(&subject_with_op); Concept *cbest_subject = Memory_Conceptualize(&subject, currentTime); if(cbest_subject != NULL) { cbest_subject->usage = Usage_use(cbest_subject->usage, currentTime, false); } //set execute and return execution printf("decision expectation %f impTruth=(%f, %f): future=%ld ", decision.desire, bestImp.truth.frequency, bestImp.truth.confidence, bestImp.occurrenceTimeOffset); Narsese_PrintTerm(&bestImp.term); puts(""); decision.execute = true; return decision; } void Decision_AssumptionOfFailure(int operationID, long currentTime) { assert(operationID >= 0 && operationID <= OPERATIONS_MAX, "Wrong operation id, did you inject an event manually?"); for(int j=0; jprecondition_beliefs[operationID].itemsAmount; h++) { if(!Memory_ImplicationValid(&postc->precondition_beliefs[operationID].array[h])) { Table_Remove(&postc->precondition_beliefs[operationID], h); h--; continue; } Implication imp = postc->precondition_beliefs[operationID].array[h]; //(&/,a,op) =/> b. Concept *current_prec = imp.sourceConcept; Event *precondition = ¤t_prec->belief_spike; //a. :|: if(precondition != NULL && precondition->type != EVENT_TYPE_DELETED) { assert(precondition->occurrenceTime != OCCURRENCE_ETERNAL, "Precondition should not be eternal!"); Event updated_precondition = Inference_EventUpdate(precondition, currentTime); Event op = { .type = EVENT_TYPE_BELIEF, .truth = (Truth) { .frequency = 1.0, .confidence = 0.9 }, .occurrenceTime = currentTime }; bool success; Event seqop = Inference_BeliefIntersection(&updated_precondition, &op, &success); //(&/,a,op). :|: if(success) { Event result = Inference_BeliefDeduction(&seqop, &imp); //b. :/: if(Truth_Expectation(result.truth) > ANTICIPATION_THRESHOLD) { Implication negative_confirmation = imp; Truth TNew = { .frequency = 0.0, .confidence = ANTICIPATION_CONFIDENCE }; Truth TPast = Truth_Projection(precondition->truth, 0, imp.occurrenceTimeOffset); negative_confirmation.truth = Truth_Eternalize(Truth_Induction(TNew, TPast)); negative_confirmation.stamp = (Stamp) { .evidentalBase = { -stampID } }; assert(negative_confirmation.truth.confidence >= 0.0 && negative_confirmation.truth.confidence <= 1.0, "(666) confidence out of bounds"); Implication *added = Table_AddAndRevise(&postc->precondition_beliefs[operationID], &negative_confirmation); if(added != NULL) { added->sourceConcept = negative_confirmation.sourceConcept; added->sourceConceptId = negative_confirmation.sourceConceptId; } stampID--; } } } } } } Decision Decision_Suggest(Concept *postc, Event *goal, long currentTime) { Decision babble_decision = {0}; //try motor babbling with a certain chance if(myrand() < (int)(MOTOR_BABBLING_CHANCE * MY_RAND_MAX)) { babble_decision = Decision_MotorBabbling(); } //try matching op if didn't motor babble Decision decision_suggested = Decision_BestCandidate(postc, goal, currentTime); if(!babble_decision.execute || decision_suggested.desire > MOTOR_BABBLING_SUPPRESSION_THRESHOLD) { return decision_suggested; } return babble_decision; }